Android異步、延遲和定時任務的簡易用法

程序猿的要求不高

異步多線程java

延遲觸發react

循環定時觸發android

1、前言

項目開發中必定會用到網絡請求,文件讀寫,開啓子線程進行耗時操做,延遲返回或關閉提示框,輪詢接口獲取處理結果,子線程發送內容到主線程更新界面等等。碰到這些問題或需求的時候,每位程序猿都會使用本身喜歡或習慣的用法來實現或界面問題。固然結果是功能完成了或問題修復了。可是代碼風格的差別,使用時考慮不全,使用方法不是最優,等等多多少少存在一些瑕疵。例如:git

  • 阿里的java編程規範不推薦顯式使用Thread
  • 直接使用AsyncTask存在內存泄露或者weak用法致使空指針的問題
  • 使用Handler進行更新界面的複雜操做
  • 使用postDelayed()進行延遲操做不能在子線程中使用
  • 使用TimerTask進行輪詢時複雜的更新頁面 綜上,特別想在代碼上統一用法,而且是簡單又安全,並且性能最優。因此想到RxJava是否是能夠來實現這個願望。嘗試和研究中記錄的Demo,有使用錯誤或更好的方案,請多指教。

2、 鋪墊

  1. RxJava集成 項目中先集成RxJava庫
api 'io.reactivex.rxjava2:rxandroid:2.1.0'
api 'io.reactivex.rxjava2:rxjava:2.2.5'
api 'com.squareup.retrofit2:retrofit:2.5.0'
api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
api 'com.squareup.retrofit2:converter-gson:2.5.0'
複製代碼

關於RxJava的方法介紹或基礎使用,請參考RxJava2在Android中的使用github

  1. RxJava多線程 使用RxJava進行多線程操做的原理、用法及其餘,請參考在 Andoid 中如何使用 RxJava 2 進行多線程編程?.編程

  2. Scheduler 針對多線程的操做,Schedulers的參數瞭解,請參考我所理解的RxJava——上手其實很簡單(三);api

3、異步

最直接的用法就是new Thread()建立一個子線程,而後用EventMessage或Handler發送Message來更新頁面。更好一點的方法是ExecutorService建立線程池,統一管理線程而且複用線程以及控制線程的總數,可是須要再花點時間維護和優化。可是不建議爲每個Activity或fragment建立一個線程池,從性能和線程複用率上沒有必要性。安全

private void testCreate() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                Log.e("wyn", "ObservableEmitter");

                Log.e("wyn", "ObservableEmitter thread is " + Thread.currentThread().getName());


                long a = 1;
                for (int i = 0; i < 1000000000; i++) {
                    a = a + (a + 1);
                }

                Log.e("wyn", "a is " + a);

                emitter.onNext("wang" + a);
                emitter.onNext("yinan");

                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this.observerString);
    }
複製代碼

打印的結果bash

2019-01-18 11:03:08.326 27120-27120/? E/wyn: onSubscribe
2019-01-18 11:03:08.326 27120-27120/? E/wyn: onSubscribe thread is main
2019-01-18 11:03:08.328 27120-27138/? E/wyn: ObservableEmitter
2019-01-18 11:03:08.328 27120-27138/? E/wyn: ObservableEmitter thread is RxCachedThreadScheduler-1
2019-01-18 11:03:13.226 27120-27138/com.example.RxThread E/wyn: a is -1
2019-01-18 11:03:13.227 27120-27120/com.example.RxThread D/wyn: onNext is wang-1
2019-01-18 11:03:13.227 27120-27120/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:03:13.229 27120-27120/com.example.RxThread D/wyn: onNext is yinan
2019-01-18 11:03:13.229 27120-27120/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:03:13.229 27120-27120/com.example.RxThread E/wyn: onComplete
2019-01-18 11:03:13.229 27120-27120/com.example.RxThread E/wyn: onComplete thread is main
複製代碼

重點

使用create方法簡易的實現子線程操做(subscribeOn設置子線程類型),發送內容(onNext發送內容)到主線程(observeOn設置在主線程操做)更新界面。網絡

4、延遲

最直接的方案就是postDelayed()觸發一個延遲的操做。若是是在子線程進行postDelayed()操做,那麼不能直接使用,會崩潰。

private void testTimer() {
        Observable.timer(3, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this.observer);
    }
複製代碼

輸出:

2019-01-18 11:31:11.820 28231-28231/com.example.RxThread E/wyn: onSubscribe
2019-01-18 11:31:11.820 28231-28231/com.example.RxThread E/wyn: onSubscribe thread is main
2019-01-18 11:31:14.832 28231-28231/com.example.RxThread D/wyn: onNext is 0
2019-01-18 11:31:14.833 28231-28231/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:31:14.835 28231-28231/com.example.RxThread E/wyn: onComplete
2019-01-18 11:31:14.835 28231-28231/com.example.RxThread E/wyn: onComplete thread is main
複製代碼

Timer能夠在子線程進行延遲操做,那麼輸出結果爲:

2019-01-18 11:33:14.018 28398-28419/? E/wyn: onSubscribe
2019-01-18 11:33:14.019 28398-28419/? E/wyn: onSubscribe thread is Thread-2
2019-01-18 11:33:17.026 28398-28398/com.example.RxThread D/wyn: onNext is 0
2019-01-18 11:33:17.027 28398-28398/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:33:17.029 28398-28398/com.example.RxThread E/wyn: onComplete
2019-01-18 11:33:17.030 28398-28398/com.example.RxThread E/wyn: onComplete thread is main
複製代碼

重點

timer設置延遲的時間,而後在主線程更新界面。

5、定時、輪詢、循環

通常輪詢接口獲取數據或倒計時顯示內容,使用TimerTask來實現,而後採用Handler發送Message更新界面。

private void testInterval() {
        Observable.interval(3, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this.observer);
    }
複製代碼

輸出結果:

2019-01-18 11:41:42.108 29064-29064/? E/wyn: onSubscribe
2019-01-18 11:41:42.108 29064-29064/? E/wyn: onSubscribe thread is main
2019-01-18 11:41:45.115 29064-29064/com.example.RxThread D/wyn: onNext is 0
2019-01-18 11:41:45.115 29064-29064/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:41:48.112 29064-29064/com.example.RxThread D/wyn: onNext is 1
2019-01-18 11:41:48.112 29064-29064/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:41:51.113 29064-29064/com.example.RxThread D/wyn: onNext is 2
2019-01-18 11:41:51.114 29064-29064/com.example.RxThread E/wyn: onNext thread is main
2019-01-18 11:41:54.113 29064-29064/com.example.RxThread D/wyn: onNext is 3
2019-01-18 11:41:54.114 29064-29064/com.example.RxThread E/wyn: onNext thread is main
.......
複製代碼

重點

interval間隔指定的時間,在主線程執行操做。

6、補充

測試代碼中使用到的observer和observerString,

private Observer<Long> observer = new Observer<Long>() {
        Disposable disposable;

        @Override
        public void onSubscribe(Disposable d) {
            Log.e("wyn", "onSubscribe");

            Log.e("wyn", "onSubscribe thread is " + Thread.currentThread().getName());

            disposable = d;
        }

        @Override
        public void onNext(Long s) {
            Log.d("wyn", "onNext is " + s);

            Log.e("wyn", "onNext thread is " + Thread.currentThread().getName());

            tvContent.setText(s + "");

            if (s == 10) {
                disposable.dispose();
            }
        }

        @Override
        public void onError(Throwable e) {
            Log.e("wyn", "onError");

            Log.e("wyn", "onError thread is " + Thread.currentThread().getName());
        }

        @Override
        public void onComplete() {
            Log.e("wyn", "onComplete");

            Log.e("wyn", "onComplete thread is " + Thread.currentThread().getName());
        }
    };

    private Observer<String> observerString = new Observer<String>() {
        Disposable disposable;

        @Override
        public void onSubscribe(Disposable d) {
            Log.e("wyn", "onSubscribe");

            Log.e("wyn", "onSubscribe thread is " + Thread.currentThread().getName());

            disposable = d;
        }

        @Override
        public void onNext(String s) {
            Log.d("wyn", "onNext is " + s);

            Log.e("wyn", "onNext thread is " + Thread.currentThread().getName());

            tvContent.setText(s);
        }

        @Override
        public void onError(Throwable e) {
            Log.e("wyn", "onError");

            Log.e("wyn", "onError thread is " + Thread.currentThread().getName());
        }

        @Override
        public void onComplete() {
            Log.e("wyn", "onComplete");

            Log.e("wyn", "onComplete thread is " + Thread.currentThread().getName());
        }
    };
複製代碼

7、注意

選擇子線程操做的時候,若是有文件操做那麼必定要用Schedulers.io()。否則建議使用Schedulers.computation().

8、其餘

從友盟統計iOS和Android的崩潰來看,Android的空指針崩潰真的是多如牛毛啊。爲啥不整一個nil類型,從系統級別上,進行全局的空指針的保護呢!少一點崩潰,多一點快樂!!!

// END

相關文章
相關標籤/搜索