andriod搭建自己的轮询框架
前言
很多时候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搭建自己的轮询框架相关推荐
- android 轮询框架,Android 轮询总结
轮询应用场景 Android中涉及到将服务器中数据变化信息通知用户一般有两种办法,推送和轮询. 譬如有这样一个app,实时性要求不高,每天只要能获取10次最新数据就能满足 要求了,这种情况显然轮询更适 ...
- Flask框架:如何运用Ajax轮询动态绘图
摘要:Ajax是异步JavaScript和XML可用于前后端交互. 本文分享自华为云社区<Flask框架:运用Ajax轮询动态绘图>,作者:LyShark. Ajax是异步JavaScri ...
- Netty框架之Selector轮询器
Selector的原理详解 1.传统多线程网络通信的服务器设计 2.线程池版网络通信的服务器设计 Selector版网络通信的服务器设计 说到Selector的作用,我们不得不引入 多线程网络通信的设 ...
- STM32基于时间片轮询机制
1. 基于时间片的轮询调度算法(仅局限单核CPU芯片): 利用定时器为每个任务函数设定执行时间间隔,保证使用频率高的函数多次被调用,提高单核芯片的资源利用率.如果只是简单地将A.B两个函数放在whil ...
- 用了这么久配置中心,还不知道长轮询是什么?
前言 传统的静态配置方式想要修改某个配置时,必须重新启动一次应用,如果是数据库连接串的变更,那可能还容易接受一些,但如果变更的是一些运行时实时感知的配置,如某个功能项的开关,重启应用就显得有点大动干戈 ...
- NAPI模式--中断和轮询的折中以及一个负载均衡的问题
处理外部事件是cpu必须要做的事,因为cpu和外设的不平等性导致外设的事件被cpu 当作是外部事件,其实它们是平等的,只不过冯氏机器不这么认为罢了,既然要处理外部事件,那么就需要一定的方法,方法不止一 ...
- 负载策略_面试官:讲一下什么是负载均衡,什么是轮询策略随机策略哈希策略
什么是负载均衡? 先举个例子吧.以超市收银为例,假设现在只有一个窗口.一个收银员: 一般情况下,收银员平均 2 分钟服务一位顾客,10 分钟可以服务 5 位顾客:到周末高峰期时,收银员加快收银,平均 ...
- WINDOWS与LINUX下的DNS轮询配置
11月12日我参加了51CTO的技术沙龙"Exchange Server系统升级与迁移",两位老师的分享都比较精彩,也让我学到了一些EXCHANGE在升级和迁移方面的知识和经验.记 ...
- 客户端与服务器持续同步解析(轮询,comet,WebSocket)
作者:盼逆邵年来源:博客园发布时间:2012-02-10 20:42 阅读:1943 次原文链接[收藏] 在B/S模型的Web应用中,客户端常常需要保持和服务器的持续更新.这种对及时性要求比较高的应 ...
- Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
摘要 Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯 方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Server- ...
最新文章
- 群同态基本定理证明_近世代数(3)——群的基本性质
- 计算机专业会比投档线高多少,比投档线高多少安全 投档线和录取线差多少
- SharePoint 2013 Word 转换PDF服务介绍及示例
- 【Vue学习第三天】组件的使用
- 如何巧妙拒绝老同学借钱?哈哈哈哈哈......
- stream去重_重复数据如何处理?List集合去重的四种方式
- python 判断当前时间是否为零点
- JDK的bin目录下所有程序的使用介绍
- 数据库存储时间的时区问题
- IT 趣味故事:TCP 出“大事”了!
- centos7.5部署mysql cluster NDB总结
- Java三种连接池(druid、c3p0、dbcp)
- heidisql导入sql文件
- 分治法--线性时间选择(求第k小数)
- matlab 图像范围,Matlab对数范围colorbar图像c
- windows 7 开机启动提示bcd错误修复方法
- 第十一周助教工作总结——NWNU李泓毅
- 就绪函数的定义_准备就绪的定义被认为是有害的
- Android Notification不显示浮动通知,不显示锁屏通知
- 数学建模学习笔记(第三章:量纲分析法,量纲齐次原则)
热门文章
- ASP.net MVC redis完整示例(含集合,哈希,sortedset)
- 【Github使用感触之一】使多文件多版本变得简单
- 【 理想的机器学习书】
- 【版本控制】如何从github 上获取源码
- 智能优化算法应用:基于GWO优化的最小交叉熵图像多阈值分割 - 附代码
- 排序算法专题-归并排序
- 【Tensorflow2.0】关于制作标签遇到的问题小结
- 从零基础入门Tensorflow2.0 ----四、12. tf_data基础api使用
- 文献笔记4 water volume variations
- 利用composer搭建PHP框架(一.路由解析)