不少時候Android應用須要每間隔一段時間向服務器請求數據,若是服務器數據有更新則通知界面變化。Android中最經常使用的紅點通常採用的就是輪詢,紅點是爲了在數據有更新時及時的提醒用戶,好比朋友圈更新,當用戶的朋友圈更新時就會顯示紅點,就是經過移動端不斷的向服務器查詢朋友圈的更新狀態。java
在實現輪詢框架時會主要會要到下面兩個類,會結合輪詢框架對這三個類進行講解,在應用中分析會理解更加深入。git
一、IntentService IntentService是一種特殊的Service,繼承了Service而且是一個抽象類,必須建立它的子類才能用。IntentService能夠用於執行後臺耗時的任務,當任務執行後會自動中止,IntentService的優先級比通常的線程高,比較適合執行一些優先級高的後臺任務。github
二、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 用於每次輪詢時向請求服務器接口數據。ide
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");
}
@Override
protected 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是一個線程池控制類。oop
public class PollingUtil {
/** * 開始輪詢服務 */
public static void startPollingService(final Context context, String action) {
//包裝須要執行Service的Intent
Intent 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()來開啓一個服務。post
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() {
@Override
public 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中的方法,代碼以下:
@Override
public 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的生成位置:
@Override
public 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);
}
@Override
public 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);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent) msg.obj);
}
}
public PollingIntentService(String name) {
super();
mName = name;
}
@Override
public 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);
}
@Override
public 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();
}
@Override
public void onDestroy() {
mServiceLooper.quit();
Log.d(TAG, "onDestroy");
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
複製代碼
PollingIntentService 繼承Service,在ServiceHandler 的handleMessage方法執行後並不執行stopSelf方法,而是專門提供了onStopService方法來中止整個Service。另外暴露出enterPollingQueue方法,能夠直接經過這個方法往輪詢隊列中加入輪詢消息。 使用: 只需將
PollingService extends IntentService{
}
複製代碼
改成
PollingService extends PollingIntentService{
}
複製代碼
根據評論區的反饋,我將會使用WorkManager對輪詢框架進行重構,重構文章連接:juejin.im/post/5c4472…