android輪詢最佳實踐service+AlarmManager+Thread

轉(zhuǎn)自:泡在網(wǎng)上的日子 輪詢,android,service,alarmmanager

android中涉及到將服務器中數(shù)據(jù)變化信息通知用戶一般有兩種辦法蝙昙,推送輪詢誉己。

消息推送是服務端主動發(fā)消息給客戶端囤锉,因為第一時間知道數(shù)據(jù)發(fā)生變化的是服務器自己芝囤,所以推送的優(yōu)勢是實時性高秕脓。但服務器主動推送需要單獨開發(fā)一套能讓客戶端持久連接的服務端程序灌灾,不過現(xiàn)在已經(jīng)有很多開源的代碼實現(xiàn)了基于xmmp協(xié)議的推送方案雕旨,而且還可以使用谷歌的推送方案扮匠。但有些情況下并不需要服務端主動推送捧请,而是在一定的時間間隔內(nèi)客戶端主動發(fā)起查詢。

譬如有這樣一個app棒搜,實時性要求不高疹蛉,每天只要能獲取10次最新數(shù)據(jù)就能滿足要求了,這種情況顯然輪詢更適合一些力麸,推送顯得太浪費可款,而且更耗電。
但是不管是輪詢還是推送都需要無論應用程序是否正在運行或者關閉的情況下能給用戶發(fā)送通知克蚂,因此都需要用到service筑舅。
我們有兩種方案來使用service達到此目的:

方案一:service +****Thread

在service中開啟一個帶有while循環(huán)的線程,使其不斷的從服務器查詢數(shù)據(jù)(一定時間間隔內(nèi))陨舱,當發(fā)現(xiàn)有需要通知用戶的情況下發(fā)送notification翠拣。這種方案的代碼大致是:

import org.apache.http.Header;
import org.json.JSONObject;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
/**
 *
 * 短信推送服務類,在后臺長期運行游盲,每個一段時間就向服務器發(fā)送一次請求
 *
 * @author jerry
 *
 */
public class PushSmsService extends Service {
   private MyThread myThread;
   private NotificationManager manager;
   private Notification notification;
   private PendingIntent pi;
   private AsyncHttpClient client;
   private boolean flag = true;
   @Override
   public IBinder onBind(Intent intent) {
     // TODO Auto-generated method stub
     return null;
   }
   @Override
   public void onCreate() {
     System.out.println("oncreate()");
     this.client = new AsyncHttpClient();
    this.myThread = new MyThread();
     this.myThread.start();
     super.onCreate();
   }
   @Override
   public void onDestroy() {
     this.flag = false;
     super.onDestroy();
   }
   private void notification(String content, String number, String date) {
     // 獲取系統(tǒng)的通知管理器
     manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
     notification = new Notification(R.drawable.ic_menu_compose, content,
     System.currentTimeMillis());
     notification.defaults = Notification.DEFAULT_ALL; // 使用默認設置误墓,比如鈴聲、震動益缎、閃燈
     notification.flags = Notification.FLAG_AUTO_CANCEL; // 但用戶點擊消息后谜慌,消息自動在通知欄自動消失
     notification.flags |= Notification.FLAG_NO_CLEAR;// 點擊通知欄的刪除,消息不會依然不會被刪除
     Intent intent = new Intent(getApplicationContext(),
     ContentActivity.class);
     intent.putExtra("content", content);
     intent.putExtra("number", number);
     intent.putExtra("date", date);
     pi = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
 
     notification.setLatestEventInfo(getApplicationContext(), number
     + "發(fā)來短信", content, pi);
     // 將消息推送到狀態(tài)欄
     manager.notify(0, notification);
   }
 private class MyThread extends Thread {
   @Override
   public void run() {
     String url = "你請求的網(wǎng)絡地址";
     while (flag) {
       System.out.println("發(fā)送請求");
       try {
         // 每個10秒向服務器發(fā)送一次請求
         Thread.sleep(10000);
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
       // 采用get方式向服務器發(fā)送請求
       client.get(url, new AsyncHttpResponseHandler() {
         @Override
         public void onSuccess(int statusCode, Header[] headers,
           byte[] responseBody) {
           try {
             JSONObject result = new JSONObject(new String(
             responseBody, "utf-8"));
             int state = result.getInt("state");
             // 假設偶數(shù)為未讀消息
             if (state % 2 == 0) {
               String content = result.getString("content");
               String date = result.getString("date");
               String number = result.getString("number");
               notification(content, number, date);
             }
           } catch (Exception e) {
             e.printStackTrace();
           }
         }
       @Override
       public void onFailure(int statusCode, Header[] headers,
             byte[] responseBody, Throwable error) {
         Toast.makeText(getApplicationContext(), "數(shù)據(jù)請求失敗", 0)
               .show();
       }
      });
   }
   }
 }
}

其中AsyncHttpClient為網(wǎng)絡異步請求的開源庫莺奔,可以很方便的實現(xiàn)異步網(wǎng)絡請求欣范。

這種方案存在的不足有很多,
是應用長期有一個后臺程序運行令哟,如果是一個喜歡用手機安全的用戶恼琼,這個service很可能被他殺死;
是雖然service可以運行在后臺屏富,但在手機休眠的情況下線程好像是被掛起的晴竞,這里涉及一個Android系統(tǒng)鎖的機制,即系統(tǒng)在檢測到一段時間沒有活躍以后狠半,會關閉一些不必要的服務來減少資源和電量消耗噩死,這跟很多應用表現(xiàn)出來的都不一樣,不符合用戶習慣神年。
因此我們還是選擇第二種方案已维。

方案二:service+AlarmManager+Thread

雖然alarm的意思是鬧鐘,而且在原生android自帶的鬧鐘應用中AlarmManager也確實非常重要已日,但并不代表AlarmManager只是用來做鬧鐘應用的垛耳,作為一個一種系統(tǒng)級別的提示服務,肯定應該有著非常重要的地位,實際上android中很多東西都可以利用AlarmManager來實現(xiàn)艾扮。
AlarmManager在特定的時刻為我們廣播一個指定的Intent既琴。簡單的說就是我們設定一個時間,然后在該時間到來時泡嘴,AlarmManager為我們廣播一個我們設定的Intent甫恩。這個intent可以指向一個activity,也可以指向一個service酌予。

下面就是使用alarm定時調(diào)用service實現(xiàn)輪詢的實現(xiàn)方法:
一磺箕、新建輪詢工具類PollingUtils.java

public class PollingUtils {
   //開啟輪詢服務
   public static void startPollingService(Context context, int seconds, Class<?> cls,String action) {
     //獲取AlarmManager系統(tǒng)服務
     AlarmManager manager = (AlarmManager) context
           .getSystemService(Context.ALARM_SERVICE);
 
     //包裝需要執(zhí)行Service的Intent
     Intent intent = new Intent(context, cls);
     intent.setAction(action);
     PendingIntent pendingIntent = PendingIntent.getService(context, 0,
     intent, PendingIntent.FLAG_UPDATE_CURRENT);
 
     //觸發(fā)服務的起始時間
     long triggerAtTime = SystemClock.elapsedRealtime();
 
     //使用AlarmManger的setRepeating方法設置定期執(zhí)行的時間間隔(seconds秒)和需要執(zhí)行的Service
     manager.setRepeating(AlarmManager.ELAPSED_REALTIME, triggerAtTime,
     seconds * 1000, pendingIntent);
   }
   //停止輪詢服務
   public static void stopPollingService(Context context, Class<?> cls,String action) {
     AlarmManager manager = (AlarmManager) context
           .getSystemService(Context.ALARM_SERVICE);
     Intent intent = new Intent(context, cls);
     intent.setAction(action);
     PendingIntent pendingIntent = PendingIntent.getService(context, 0,
           intent, PendingIntent.FLAG_UPDATE_CURRENT);
     //取消正在執(zhí)行的服務
     manager.cancel(pendingIntent);
   }
}

二、構建輪詢?nèi)蝿請?zhí)行PollingService.java

public class PollingService extends Service {
   public static final String ACTION = "com.ryantang.service.PollingService";
 
   private Notification mNotification;
   private NotificationManager mManager;
   @Override
   public IBinder onBind(Intent intent) {
     return null;
   }
   @Override
   public void onCreate() {
     initNotifiManager();
   }
 
   @Override
   public void onStart(Intent intent, int startId) {
     new PollingThread().start();
   }
   //初始化通知欄配置
   private void initNotifiManager() {
     mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
     int icon = R.drawable.ic_launcher;
     mNotification = new Notification();
     mNotification.icon = icon;
     mNotification.tickerText = "New Message";
     mNotification.defaults |= Notification.DEFAULT_SOUND;
     mNotification.flags = Notification.FLAG_AUTO_CANCEL;
   }
   //彈出Notification
   private void showNotification() {
     mNotification.when = System.currentTimeMillis();
     //Navigator to the new activity when click the notification title
     Intent i = new Intent(this, MessageActivity.class);
     PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i,
         Intent.FLAG_ACTIVITY_NEW_TASK);
     mNotification.setLatestEventInfo(this,
         getResources().getString(R.string.app_name), "You have new message!", pendingIntent);
     mManager.notify(0, mNotification);
   }
   /**
   * Polling thread
   * 模擬向Server輪詢的異步線程
   * @Author Ryan
   * @Create 2013-7-13 上午10:18:34
   */
   int count = 0;
   class PollingThread extends Thread {
     @Override
     public void run() {
       System.out.println("Polling...");
       count ++;
       //當計數(shù)能被5整除時彈出通知
       if (count % 5 == 0) {
       showNotification();
       System.out.println("New message!");
       }
     }
   }
 
   @Override
   public void onDestroy() {
     super.onDestroy();
     System.out.println("Service:onDestroy");
   }
}

三抛虫、在MainActivity.java中開啟和停止PollingService

public class MainActivity extends Activity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     //Start polling service
     System.out.println("Start polling service...");
     PollingUtils.startPollingService(this, 5, PollingService.class, PollingService.ACTION);
   }
 
   @Override
   protected void onDestroy() {
     super.onDestroy();
     //Stop polling service
     System.out.println("Stop polling service...");
     PollingUtils.stopPollingService(this, PollingService.class, PollingService.ACTION);
   }
}

可以看出第二種方案和第一種方案的本質(zhì)區(qū)別是實現(xiàn)定時查詢的方式不同松靡,一種是利用系統(tǒng)服務,一種是自己通過while循環(huán)建椰。顯然使用系統(tǒng)服務具有更高的穩(wěn)定性雕欺,而且恰好解決了休眠狀態(tài)下輪詢中斷的問題,因為AlarmManager是始終運行者的棉姐。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屠列,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子伞矩,更是在濱河造成了極大的恐慌笛洛,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乃坤,死亡現(xiàn)場離奇詭異苛让,居然都是意外死亡,警方通過查閱死者的電腦和手機湿诊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門狱杰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枫吧,你說我怎么就攤上這事浦旱∮钌” “怎么了九杂?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宣蠕。 經(jīng)常有香客問我例隆,道長,這世上最難降的妖魔是什么抢蚀? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任镀层,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唱逢。我一直安慰自己吴侦,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布坞古。 她就那樣靜靜地躺著备韧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痪枫。 梳的紋絲不亂的頭發(fā)上织堂,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音奶陈,去河邊找鬼易阳。 笑死,一個胖子當著我的面吹牛吃粒,可吹牛的內(nèi)容都是我干的潦俺。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼徐勃,長吁一口氣:“原來是場噩夢啊……” “哼黑竞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疏旨,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤很魂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后檐涝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遏匆,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年谁榜,在試婚紗的時候發(fā)現(xiàn)自己被綠了幅聘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡窃植,死狀恐怖帝蒿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巷怜,我是刑警寧澤葛超,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站延塑,受9級特大地震影響绣张,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜关带,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一侥涵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦芜飘、人聲如沸务豺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冲呢。三九已至,卻和暖如春招狸,著一層夾襖步出監(jiān)牢的瞬間敬拓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工裙戏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乘凸,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓累榜,卻偏偏與公主長得像营勤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子壹罚,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容