前言

很多时候Android应用需要每间隔一段时间向服务器请求数据,如果服务器数据有更新则通知界面变化。Android中最常用的红点一般采用的就是轮询,红点是为了在数据有更新时及时的提醒用户,比如朋友圈更新,当用户的朋友圈更新时就会显示红点,就是通过移动端不断的向服务器查询朋友圈的更新状态。

相关知识点

在实现轮询框架时会主要会要到下面两个类,会结合轮询框架对这三个类进行讲解,在应用中分析会理解更加深刻。

1、IntentService IntentService是一种特殊的Service,继承了Service并且是一个抽象类,必须创建它的子类才能用。IntentService可以用于执行后台耗时的任务,当任务执行后会自动停止,IntentService的优先级比一般的线程高,比较适合执行一些优先级高的后台任务。

2、PendingIntent PendingIntent是延迟的intent,主要用来在某个事件完成后执行特定的Action。PendingIntent包含了Intent及Context,所以就算Intent所属程序结束,PendingIntent依然有效,可以在其他程序中使用。PendingIntent一般作为参数传给某个实例,在该实例完成某个操作后自动执行PendingIntent上的Action,也可以通过PendingIntent的send函数手动执行,并可以在send函数中设置OnFinished表示send成功后执行的动作。

轮询框架实现

要实现轮询,可以借鉴Handler中的looper机制,如下图,维护一个消息队列,循环的从消息队列中取出消息来执行,轮询框架可以定时的向消息队列中加入消息,然后循环中消息队列中取出消息执行。

可以自己实现一个Looper,但是IntentService中已经包含了一个Looper和一个HandlerThread。因此轮询框架中使用IntentService作为循环框架。继承IntentService接口来实现处理消息访问服务器。

PollingService 用于每次轮询时向请求服务器接口数据

public class PollingService extends IntentService {public static final String ACTION_CHECK_CIRCLE_UPDATE="ACTION_CHECK_CIRCLE_UPDATE";  public static final long DEFAULT_MIN_POLLING_INTERVAL = 60000;//最短轮询间隔1分钟public PollingService() {super("PollingService");}@Overrideprotected void onHandleIntent(Intent intent) {if (intent == null)return;final String action = intent.getAction();if (ACTION_CHECK_Circle_UPDATE.equals(action)) {CheckCircleOfFriendsUpdate();//这个是访问服务器获取朋友圈是否更新}}
}
复制代码

PollingService 用来处理接到轮询的消息之后在onHandleIntent(Intent intent)中根据Intent所带有的action不同来进行访问服务器不同的接口获取数据。

PollingUtil 用于控制轮询服务的开始和结束 使用PollingUtil中的startPollingService来根据action和context生成一个PendingIntent,并将PendingIntent交给PollingScheduler来处理。PollingScheduler是一个线程池控制类。

public class PollingUtil {/*** 开始轮询服务*/public static void startPollingService(final Context context, String action) {//包装需要执行Service的IntentIntent intent = new Intent(context, PollingService.class);intent.setAction(action);PendingIntent pendingIntent = PendingIntent.getService(context, 0,intent, PendingIntent.FLAG_UPDATE_CURRENT);PollingScheduler.getInstance().addScheduleTask(pendingIntent, 0, PollingService.DEFAULT_MIN_POLLING_INTERVAL);}}/*** 停止轮询服务** @param context*/public static void stopPollingServices(Context context, String action) {PollingScheduler.getInstance().clearScheduleTasks();}}
复制代码

PollingScheduler实现定时向IntentService的Looper中加入消息 PollingScheduler中生成一个单线程池,addScheduleTask中定时的执行pendingIntent.send(),其中PendingIntent是由PendingIntent pendingIntent = PendingIntent.getService(context, 0,intent, PendingIntent.FLAG_UPDATE_CURRENT);生成的,pendingIntent.send()函数会调用Service.startService()来开启一个服务。

public class PollingScheduler {private static PollingScheduler sInstance;private ScheduledExecutorService mScheduler;private PollingScheduler() {mScheduler = Executors.newSingleThreadScheduledExecutor();}public static synchronized PollingScheduler getInstance() {if (sInstance == null) {sInstance = new PollingScheduler();}if (sInstance.mScheduler.isShutdown()) {sInstance.mScheduler = Executors.newSingleThreadScheduledExecutor();}return sInstance;}public void addScheduleTask(final PendingIntent pendingIntent, long initialDelay, long period) {Runnable command = new Runnable() {@Overridepublic void run() {try {pendingIntent.send();} catch (PendingIntent.CanceledException e) {e.printStackTrace();}}};mScheduler.scheduleAtFixedRate(command, initialDelay, period, TimeUnit.MILLISECONDS);}public void clearScheduleTasks() {mScheduler.shutdownNow();}
}
复制代码

代码分析

先给出类图之间的关系如下:

PollingService继承了IntentService,并且在PollingUtil的startPollingService方法中通过Intent intent = new Intent(context, PollingService.class);和将PendingIntent 与PollingService关联起来,并将PendingIntent加入到定时执行的线程池中,在PollingScheduler 中使用pendingIntent.send();由于PendingIntent与PollingService关联,所以执行pendingIntent.send()的时候会调用PollingIntentServide中的onStart()方法。onStart()方法是IntentService中的方法,代码如下:

    @Overridepublic void onStart(@Nullable Intent intent, int startId) {Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}
复制代码

在onstart()中有一个mServiceHandler.sendMessage(msg);,找到mServiceHandler的生成位置:

    @Overridepublic void onCreate() {super.onCreate();HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");thread.start();mServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}
复制代码

在IntentService的onCreate方法中生成了一个HandlerThread,一个mServiceLooper,一个mServiceHandler,其中mServiceHandler.sendMessage(msg)中的msg都会放到mServiceLooper,执行时从mServiceLooper中取出执行,其中ServiceHandler 的代码如下

    private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {onHandleIntent((Intent)msg.obj);stopSelf(msg.arg1);}}
复制代码

handleMessage(Message msg)中会调用onHandleIntent((Intent)msg.obj);方法,也就是在PollingService中重写的onHandleIntent方法。因此我们在addScheduleTask中不断的执行pending.send()方法,会不断的调用IntentService中的onStart方法中的mServiceHandler.sendMessage(msg);不断的向消息队列中发消息,然后在onHandleIntent处理消息。 这样一个轮询框架就完成了。

总结

本文的轮询框架利用了IntentService中的handler和Looper机制来实现循环的处理消息,由于IntentService具有服务的特性因此特别适合后台轮询访问服务器数据。

更改

经过评论区的提醒,又测试了几遍发现每次轮询确实都会新建和销毁IntentService,这样就没有利用到消息队列,所以重写了一个PollingIntentService类继承Service,使得每次使用时不会重写创建Service,达到复用的效果。同时增加了enterPollingQueue()方法,可以直接往PollingIntentService的队列中增加轮询的Intent消息。 PollingIntentService代码

 * Created time 11:40.** @author huhanjun* @since 2019/1/7*/
public abstract class PollingIntentService extends Service {private volatile Looper mServiceLooper;private volatile ServiceHandler mServiceHandler;private String mName;private boolean mRedelivery;private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {onHandleIntent((Intent) msg.obj);}}public PollingIntentService(String name) {super();mName = name;}@Overridepublic void onCreate() {super.onCreate();Log.d(TAG, "onCreate");HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");thread.start();mServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}//进入轮询队列public void enterPollingQueue(@Nullable Intent intent, int startId) {Log.d(TAG, "enterPollingQueue");Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}@Overridepublic int onStartCommand(@Nullable Intent intent, int flags, int startId) {Log.d(TAG, "onStartCommand");enterPollingQueue(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}//停止服务public void onStopService() {stopSelf();}@Overridepublic void onDestroy() {mServiceLooper.quit();Log.d(TAG, "onDestroy");}@Override@Nullablepublic IBinder onBind(Intent intent) {return null;}@WorkerThreadprotected abstract void onHandleIntent(@Nullable Intent intent);}
复制代码

PollingIntentService 继承Service,在ServiceHandler 的handleMessage方法执行后并不执行stopSelf方法,而是专门提供了onStopService方法来停止整个Service。另外暴露出enterPollingQueue方法,可以直接通过这个方法往轮询队列中加入轮询消息。 使用: 只需将

PollingService extends IntentService{
}
复制代码

改为

PollingService extends PollingIntentService{
}
复制代码

源码地址:github.com/hankinghu/P…

轮询框架重构

根据评论区的反馈,我将会使用WorkManager对轮询框架进行重构,重构文章链接:juejin.im/post/5c4472…

我的scdn

blog.csdn.net/u013309870

andriod搭建自己的轮询框架相关推荐

  1. android 轮询框架,Android 轮询总结

    轮询应用场景 Android中涉及到将服务器中数据变化信息通知用户一般有两种办法,推送和轮询. 譬如有这样一个app,实时性要求不高,每天只要能获取10次最新数据就能满足 要求了,这种情况显然轮询更适 ...

  2. Flask框架:如何运用Ajax轮询动态绘图

    摘要:Ajax是异步JavaScript和XML可用于前后端交互. 本文分享自华为云社区<Flask框架:运用Ajax轮询动态绘图>,作者:LyShark. Ajax是异步JavaScri ...

  3. Netty框架之Selector轮询器

    Selector的原理详解 1.传统多线程网络通信的服务器设计 2.线程池版网络通信的服务器设计 Selector版网络通信的服务器设计 说到Selector的作用,我们不得不引入 多线程网络通信的设 ...

  4. STM32基于时间片轮询机制

    1. 基于时间片的轮询调度算法(仅局限单核CPU芯片): 利用定时器为每个任务函数设定执行时间间隔,保证使用频率高的函数多次被调用,提高单核芯片的资源利用率.如果只是简单地将A.B两个函数放在whil ...

  5. 用了这么久配置中心,还不知道长轮询是什么?

    前言 传统的静态配置方式想要修改某个配置时,必须重新启动一次应用,如果是数据库连接串的变更,那可能还容易接受一些,但如果变更的是一些运行时实时感知的配置,如某个功能项的开关,重启应用就显得有点大动干戈 ...

  6. NAPI模式--中断和轮询的折中以及一个负载均衡的问题

    处理外部事件是cpu必须要做的事,因为cpu和外设的不平等性导致外设的事件被cpu 当作是外部事件,其实它们是平等的,只不过冯氏机器不这么认为罢了,既然要处理外部事件,那么就需要一定的方法,方法不止一 ...

  7. 负载策略_面试官:讲一下什么是负载均衡,什么是轮询策略随机策略哈希策略

    什么是负载均衡? 先举个例子吧.以超市收银为例,假设现在只有一个窗口.一个收银员: 一般情况下,收银员平均 2 分钟服务一位顾客,10 分钟可以服务 5 位顾客:到周末高峰期时,收银员加快收银,平均 ...

  8. WINDOWS与LINUX下的DNS轮询配置

    11月12日我参加了51CTO的技术沙龙"Exchange Server系统升级与迁移",两位老师的分享都比较精彩,也让我学到了一些EXCHANGE在升级和迁移方面的知识和经验.记 ...

  9. 客户端与服务器持续同步解析(轮询,comet,WebSocket)

    作者:盼逆邵年来源:博客园发布时间:2012-02-10 20:42  阅读:1943 次原文链接[收藏] 在B/S模型的Web应用中,客户端常常需要保持和服务器的持续更新.这种对及时性要求比较高的应 ...

  10. Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

    摘要 Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯 方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Server- ...

最新文章

  1. 群同态基本定理证明_近世代数(3)——群的基本性质
  2. 计算机专业会比投档线高多少,比投档线高多少安全 投档线和录取线差多少
  3. SharePoint 2013 Word 转换PDF服务介绍及示例
  4. 【Vue学习第三天】组件的使用
  5. 如何巧妙拒绝老同学借钱?哈哈哈哈哈......
  6. stream去重_重复数据如何处理?List集合去重的四种方式
  7. python 判断当前时间是否为零点
  8. JDK的bin目录下所有程序的使用介绍
  9. 数据库存储时间的时区问题
  10. IT 趣味故事:TCP 出“大事”了!
  11. centos7.5部署mysql cluster NDB总结
  12. Java三种连接池(druid、c3p0、dbcp)
  13. heidisql导入sql文件
  14. 分治法--线性时间选择(求第k小数)
  15. matlab 图像范围,Matlab对数范围colorbar图像c
  16. windows 7 开机启动提示bcd错误修复方法
  17. 第十一周助教工作总结——NWNU李泓毅
  18. 就绪函数的定义_准备就绪的定义被认为是有害的
  19. Android Notification不显示浮动通知,不显示锁屏通知
  20. 数学建模学习笔记(第三章:量纲分析法,量纲齐次原则)

热门文章

  1. ASP.net MVC redis完整示例(含集合,哈希,sortedset)
  2. 【Github使用感触之一】使多文件多版本变得简单
  3. 【 理想的机器学习书】
  4. 【版本控制】如何从github 上获取源码
  5. 智能优化算法应用:基于GWO优化的最小交叉熵图像多阈值分割 - 附代码
  6. 排序算法专题-归并排序
  7. 【Tensorflow2.0】关于制作标签遇到的问题小结
  8. 从零基础入门Tensorflow2.0 ----四、12. tf_data基础api使用
  9. 文献笔记4 water volume variations
  10. 利用composer搭建PHP框架(一.路由解析)