android產品研發(十三)-->App輪訓操作

來源:互聯網
上載者:User

標籤:

轉載請標明出處:一片楓葉的專欄

上一篇文章中我們講解了android app實現長串連的幾種方式,各自的優缺點以及具體的實現,一般而言使用第三方的推送服務已經可以滿足了基本的業務需求,當然了若是對技術有追求的可以通過NIO或者是MINA實現自身的長串連服務,但是自己實現的長串連服務一來比較複雜耗時比較多,而且可能過程中有許多坑要填,一般而言推薦使用第三方的推送服務,穩定簡單,具體管理長串連部分的模組可參考:android產品研發(十二)–>App長串連實現。

而本文將講解app端的輪訓請求服務,一般而言我們經常將輪訓操作用於請求伺服器。比如某一個頁面我們有定時任務需要時時的從伺服器擷取更新資訊並顯示,比如當長串連斷掉之後我們可能需要啟動輪訓請求作為長串連的補充等,所以這時候就用到了輪訓服務。

什麼是輪訓請求

在說明我們輪訓請求之前,這裡先說明一下什麼叫輪訓請求,我的理解就是App端每隔一定的時間重複請求的操作就叫做輪訓請求,比如:App端每隔一段時間上報一次定位資訊,App端每隔一段時間拉去一次使用者狀態等,這些應該都是輪訓請求,那麼前一篇我們講了App端的長串連,為什麼我們有了長串連之後還需要輪訓操作呢?

這是因為我們的長串連並不是穩定的可靠的,而我們執行輪訓操作的時候一般都是要穩定的網路請求,而且輪訓操作一般都是有生命週期的,即在一定的生命週期內執行輪訓操作,而我們的長串連一般都是整個進程生命週期的,所以從這方面講也不太適合。

輪訓請求實踐

與長串連相關的輪訓請求

  1. 上一篇我們在講解長串連的時候說過長串連有可能會斷,而這時候在長串連斷的時候我們就需要啟動一個輪訓服務,它作為長串連的補充。
/**     * 啟動輪詢服務     */    public void startLoopService() {        // 啟動輪詢服務        // 暫時不考慮加入網路情況的判斷...        if (!LoopService.isServiceRuning) {            // 使用者是登入態,啟動輪詢服務            if (UserConfig.isPassLogined()) {                // 判斷當前長串連的狀態,若長串連已串連,則不再開啟輪詢服務                if (MinaLongConnectManager.session != null && MinaLongConnectManager.session.isConnected()) {                    LoopService.quitLoopService(context);                    return;                }                LoopService.startLoopService(context);            } else {                LoopService.quitLoopService(context);            }        }    }

這裡就是我們執行輪訓服務的作業碼,其作用就是啟動了一個輪訓service(即輪訓服務),然後在輪訓服務中執行具體的輪訓請求,既然這樣我們就具體看一下這個service的代碼邏輯。

  • 與長串連相關的輪訓服務要求
/** * 長串連異常時啟動服務,長串連恢複時關閉服務 */public class LoopService extends Service {    public static final String ACTION = "com.youyou.uuelectric.renter.Service.LoopService";    /**     * 用戶端執行輪詢的時間間隔,該值由StartQueryInterface介面返回,預設設定為30s     */    public static int LOOP_INTERVAL_SECS = 30;    /**     * 輪詢時間間隔(MLOOP_INTERVAL_SECS 這個時間間隔變數有伺服器下發,此時輪詢服務的情境與邏輯與定義時發生變化,涉及到IOS端,因此採用自己定義的常量在用戶端寫死時間間隔)     */    public static int MLOOP_INTERVAL_SECS = 30;    /**     * 當前服務是否正在執行     */    public static boolean isServiceRuning = false;    /**     * 定時任務工具類     */    public static Timer timer = new Timer();    private static Context context;    public LoopService() {        isServiceRuning = false;    }    //-------------------------------使用鬧鐘執行輪詢服務------------------------------------    /**     * 啟動輪詢服務     */    public static void startLoopService(Context context) {        if (context == null)            return;        quitLoopService(context);        L.i("開啟輪詢服務,輪詢間隔:" + MLOOP_INTERVAL_SECS + "s");        AlarmManager manager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);        Intent intent = new Intent(context.getApplicationContext(), LoopService.class);        intent.setAction(LoopService.ACTION);        PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);        // long triggerAtTime = SystemClock.elapsedRealtime() + 1000;        /**         * 鬧鐘的第一次執行時間,以毫秒為單位,可以自訂時間,不過一般使用目前時間。需要注意的是,本屬性與第一個屬性(type)密切相關,         * 如果第一個參數對應的鬧鐘使用的是相對時間(ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那麼本屬性就得使用相對時間(相對於系統啟動時間來說),         *      比如目前時間就表示為:SystemClock.elapsedRealtime();         * 如果第一個參數對應的鬧鐘使用的是絕對時間(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那麼本屬性就得使用絕對時間,         *      比如目前時間就表示為:System.currentTimeMillis()。         */        manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), MLOOP_INTERVAL_SECS * 1000, pendingIntent);    }    /**     * 停止輪詢服務     */    public static void quitLoopService(Context context) {        if (context == null)            return;        L.i("關閉輪詢鬧鐘服務...");        AlarmManager manager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);        Intent intent = new Intent(context.getApplicationContext(), LoopService.class);        intent.setAction(LoopService.ACTION);        PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);        manager.cancel(pendingIntent);        // 關閉輪詢服務        L.i("關閉輪詢服務...");        context.stopService(intent);    }    @Override    public void onCreate() {        super.onCreate();        context = getApplicationContext();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        L.i("開始執行輪詢服務... \n 判斷目前使用者是否已登入...");        // 若當前網路不正常或者是使用者未登陸,則不再跳轉        if (UserConfig.isPassLogined()) {            // 判斷當前長串連狀態,若長串連正常,則關閉輪詢服務            L.i("目前使用者已登入... \n 判斷長串連是否已經串連...");            if (MinaLongConnectManager.session != null && MinaLongConnectManager.session.isConnected()) {                L.i("長串連已恢複串連,退出輪詢服務...");                quitLoopService(context);            } else {                if (isServiceRuning) {                    return START_STICKY;                }                // 啟動輪詢拉取訊息                startLoop();            }        } else {            L.i("使用者已退出登入,關閉輪詢服務...");            quitLoopService(context);        }        return START_STICKY;    }    @Override    public void onDestroy() {        super.onDestroy();        L.i("輪詢服務退出,執行onDestory()方法,inServiceRuning賦值false");        isServiceRuning = false;        timer.cancel();        timer = new Timer();    }    @Override    public IBinder onBind(Intent intent) {        throw new UnsupportedOperationException("Not yet implemented");    }    /**     * 啟動輪詢拉去訊息     */    private void startLoop() {        if (timer == null) {            timer = new Timer();        }        timer.schedule(new TimerTask() {            @Override            public void run() {                isServiceRuning = true;                L.i("長串連未恢複串連,執行輪詢操作... \n 輪詢服務中請求getInstance介面...");                LoopRequest.getInstance(context).sendLoopRequest();            }        }, 0, MLOOP_INTERVAL_SECS * 1000);    }}

可以發現這裡的service輪訓服務的代碼量還是比較多的,但是輪訓服務要求代碼注釋已經很詳細了,所以就不做過多的說明,需要說明的是其核心就是通過Timer對象每個一段時間執行一次網路請求。具體的網路請求代碼:

L.i("長串連未恢複串連,執行輪詢操作... \n 輪詢服務中請求getInstance介面...");           LoopRequest.getInstance(context).sendLoopRequest();

這裡的輪訓服務要求核心邏輯:當長串連出現異常時,啟動輪訓服務,並通過Timer對象每隔一定時間拉取伺服器狀態,當長串連恢複時,關閉輪訓服務。這就是我們與長串連有關的輪訓服務的代碼執行邏輯,看完這部分之後我們再看一下與頁面相關的輪訓請求的執行邏輯。

與頁面相關的輪訓請求

  • 與頁面相關的輪訓請求
    我們的App中當使用者停留在某一個頁面的時候我們可能需要定時的拉取使用者狀態,這時候也需要使用輪訓請求拉取伺服器狀態,當使用者離開該頁面的時候關閉輪訓服務要求。

這裡我們看一下我們產品當前行程頁面的輪訓操作,用於輪訓請求目前使用者的車輛裡程,費用,用時等資訊,具體可參考:

其實在當前Fragment頁面有一個定時的拉去訂單資訊的輪訓請求,下面我們具體看一下這個定時請求的執行邏輯:

/** * TimerTask對象,主要用於定時拉去伺服器資訊 */public class Task extends TimerTask {        @Override        public void run() {            L.i("開始執行執行timer定時任務...");            handler.post(new Runnable() {                @Override                public void run() {                    isFirstGetData = false;                    getData(true);                }            });        }    }

而這裡的getData方法就是拉去伺服器狀態的方法,這裡不做過多的解釋,當使用者退出這個頁面的時候需要清除這裡的輪訓操作。所以在Fragment的onDesctoryView方法中執行了清除timerTask的操作。

@Override    public void onDestroyView() {        super.onDestroyView();        ...        if (timer != null) {            timer.cancel();            timer = null;        }        if (timerTask != null) {            timerTask.cancel();            timerTask = null;        }        ...    }

這樣當使用者開啟這個頁面的時候初始化TimerTask對象,每個一分鐘請求一次伺服器拉取訂單資訊並更新UI,當使用者離開頁面的時候清除TimerTask對象,即取消輪訓請求操作。可以發現上面我們看到的與長串連和頁面相關的輪訓請求服務都是通過timer對象的定時任務實現的輪訓請求服務,下面我們看一下如何通過Handler對象實現輪訓請求服務。

通過Handler對象實現輪訓請求

  • 下面我們來看一個通過Handler非同步訊息實現的輪訓請求服務。
/**     * 預設的時間間隔:1分鐘     */    private static int DEFAULT_INTERVAL = 60 * 1000;    /**     * 異常情況下的輪詢時間間隔:5秒     */    private static int ERROR_INTERVAL = 5 * 1000;    /**     * 當前輪詢執行的時間間隔     */    private static int interval = DEFAULT_INTERVAL;    /**     * 輪詢Handler的訊息類型     */    private static int LOOP_WHAT = 10;    /**     * 是否是第一次拉取資料     */    private boolean isFirstRequest = false;    /**     * 第一次請求資料是否成功     */    private boolean isFirstRequestSuccess = false;    /**     * 開始執行輪詢,正常情況下,每隔1分鐘輪詢拉取一次最新資料     * 在onStart時開啟輪詢     */    private void startLoop() {        L.i("頁面onStart,需要開啟輪詢");        loopRequestHandler.sendEmptyMessageDelayed(LOOP_WHAT, interval);    }    /**     * 關閉輪詢,在介面onStop時,停止輪詢操作     */    private void stopLoop() {        L.i("頁面已onStop,需要停止輪詢");        loopRequestHandler.removeMessages(LOOP_WHAT);    }    /**     * 處理輪詢的Handler     */    private Handler loopRequestHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            // 如果首次請求失敗,            if (!isFirstRequestSuccess) {                L.i("首次請求失敗,需要將輪詢時間設定為:" + ERROR_INTERVAL);                interval = ERROR_INTERVAL;            } else {                interval = DEFAULT_INTERVAL;            }            L.i("輪詢中-----當前輪詢間隔:" + interval);            loopRequestHandler.removeMessages(LOOP_WHAT);            // 首次請求為成功、或者定位未成功時執行重定位,並載入網點資料            if (!isFirstRequestSuccess || !Config.locationIsSuccess()) {                isClickLocationButton = false;                doLocationOption();            } else {                loadData();            }            System.gc();            loopRequestHandler.sendEmptyMessageDelayed(LOOP_WHAT, interval);        }    };

這裡是通過Handler實現的輪訓操作,其核心原理就是在handler的handlerMessage方法中,接收到訊息之後再次發送延遲訊息,這裡的延遲時間就是我們定義的輪訓間隔時間,這樣當我們下次接收到訊息的時候又一次發送延遲訊息,從而造成我們時時發送輪訓訊息的情景。

以上就是我們實現輪訓操作的兩種方式:

  • Timer對象實現輪訓操作

  • Handler對象實現輪訓操作

上面我們分析了輪訓請求的不同使用情境,作用以及實現方式,當我們在具體的開發過程中需要定時的向伺服器拉取訊息的時候就可以考慮使用輪訓請求了。

總結:

  • 輪訓操作一般都是通過定時請求伺服器拉取資訊並更新UI;

  • 輪訓操作一般都有一定的生命週期,比如在某個頁面開啟時啟動輪訓操作,在某個頁面關閉時取消輪訓操作;

  • 輪訓操作的請求間隔需要根據具體的需求確定,間隔時間不宜果斷,否則可能造成並發性問題;

  • 產品開發過程中,某些需要試試補救伺服器拉取資訊並更新UI時,可以考慮使用輪訓操作實現;

  • 可以通過Timer對象和Handler對象兩種方式實現輪訓請求操作;


另外對產品研發技術,技巧,實踐方面感興趣的同學可以參考我的:
android產品研發(一)–>實用開發規範
android產品研發(二)–>啟動頁最佳化
android產品研發(三)–>基類Activity
android產品研發(四)–>減小Apk大小
android產品研發(五)–>多渠道打包
android產品研發(六)–>Apk混淆
android產品研發(七)–>Apk熱修複
android產品研發(八)–>App資料統計
android產品研發(九)–>App網路傳輸協議
android產品研發(十)–>不使用靜態變數儲存資料

本文以同步至github中:https://github.com/yipianfengye/androidProject,歡迎star和follow

android產品研發(十三)-->App輪訓操作

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.