Android 輪詢最佳實踐 Service + AlarmManager+Thread

android中涉及到將服務器中數據變化信息通知用戶通常有兩種辦法,推送輪詢java

消息推送是服務端主動發消息給客戶端,由於第一時間知道數據發生變化的是服務器本身,因此推送的優點是實時性高。但服務器主動推送須要單獨開發一套能讓客戶端持久鏈接的服務端程序,不過如今已經有不少開源的代碼實現了基於xmmp協議的推送方案,並且還可使用谷歌的推送方案。但有些狀況下並不須要服務端主動推送,而是在必定的時間間隔內客戶端主動發起查詢。 android

譬若有這樣一個app,實時性要求不高,天天只要能獲取10次最新數據就能知足要求了,這種狀況顯然輪詢更適合一些,推送顯得太浪費,並且更耗電。 apache

可是不論是輪詢仍是推送都須要不管應用程序是否正在運行或者關閉的狀況下能給用戶發送通知,所以都須要用到service。咱們有兩種方案來使用service達到此目的: json

方案一:service +Thread 安全

在service中開啓一個帶有while循環的線程,使其不斷的從服務器查詢數據(必定時間間隔內),當發現有須要通知用戶的狀況下發送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;
/**
 *
 * 短信推送服務類,在後臺長期運行,每一個一段時間就向服務器發送一次請求
 *
 * @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) {
        // 獲取系統的通知管理器
        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
                + "發來短信", content, pi);
        // 將消息推送到狀態欄
        manager.notify(0, notification);
    }
    private class MyThread extends Thread {
        @Override
        public void run() {
            String url = "你請求的網絡地址";
            while (flag) {
                System.out.println("發送請求");
                try {
                    // 每一個10秒向服務器發送一次請求
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 採用get方式向服務器發送請求
                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");
                            // 假設偶數爲未讀消息
                            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(), "數據請求失敗", 0)
                                .show();
                    }
                });
            }
        }
    }
}



其中AsyncHttpClient爲網絡異步請求的開源庫,能夠很方便的實現異步網絡請求。
這種方案存在的不足有不少,一是應用長期有一個後臺程序運行,若是是一個喜歡用手機安全的用戶,這個service極可能被他殺死;二是雖然service能夠運行在後臺,但在手機休眠的狀況下線程好像是被掛起的,這裏涉及一個Android系統鎖的機制,即系統在檢測到一段時間沒有活躍之後,會關閉一些沒必要要的服務來減小資源和電量消耗,這跟不少應用表現出來的都不同,不符合用戶習慣。所以咱們仍是選擇第二種方案。


方案二:service+AlarmManager+Thread 網絡

雖然alarm的意思是鬧鐘,並且在原生android自帶的鬧鐘應用中AlarmManager也確實很是重要,但並不表明AlarmManager只是用來作鬧鐘應用的,做爲一個一種系統級別的提示服務,確定應該有着很是重要的地位,實際上android中不少東西均可以利用AlarmManager來實現。 app

AlarmManager在特定的時刻爲咱們廣播一個指定的Intent。簡單的說就是咱們設定一個時間,而後在該時間到來時,AlarmManager爲咱們廣播一個咱們設定的Intent。這個intent能夠指向一個activity,也能夠指向一個service。 異步

下面就是使用alarm定時調用service實現輪詢的實現方法: ide

1、新建輪詢工具類PollingUtils.java

public class PollingUtils {
    //開啓輪詢服務
    public static void startPollingService(Context context, int seconds, Class<?> cls,String action) {
        //獲取AlarmManager系統服務
        AlarmManager manager = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
                                                                                                                                                                                                                                       
        //包裝須要執行Service的Intent
        Intent intent = new Intent(context, cls);
        intent.setAction(action);
        PendingIntent pendingIntent = PendingIntent.getService(context, 0,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
                                                                                                                                                                                                                                       
        //觸發服務的起始時間
        long triggerAtTime = SystemClock.elapsedRealtime();
                                                                                                                                                                                                                                       
        //使用AlarmManger的setRepeating方法設置按期執行的時間間隔(seconds秒)和須要執行的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);
        //取消正在執行的服務
        manager.cancel(pendingIntent);
    }
}



2、構建輪詢任務執行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 ++;
            //當計數能被5整除時彈出通知
            if (count % 5 == 0) {
                showNotification();
                System.out.println("New message!");
            }
        }
    }
                                                                                                                                                                                                                           
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("Service:onDestroy");
    }
}



3、在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);
    }
}



能夠看出第二種方案和第一種方案的本質區別是實現定時查詢的方式不一樣,一種是利用系統服務,一種是本身經過while循環。顯然使用系統服務具備更高的穩定性,並且剛好解決了休眠狀態下輪詢中斷的問題,由於 AlarmManager 是始終運行者的。
相關文章
相關標籤/搜索