Android官方響應式框架Agera詳解:一、相關(guān)概念和基本使用

Android

前言

在學習 Agera 之前沒有接觸過響應式編程和 RxJava ,所以當時學起來非常的費勁,也踩了很多坑。寫這篇博客的目的就是把自己學習到的成果分享出來,希望能夠幫助那些需要的人

后面的文章已經(jīng)更新啦~
Android官方響應式框架Agera詳解:二、Repository的創(chuàng)建和操作符
Android官方響應式框架Agera詳解:三、Repository的更新規(guī)則及Agera+Retrofit+Okhttp實戰(zhàn)

目錄

一、Agera 及相關(guān)概念簡介
二、Agera 使用示例
三、Agera 相關(guān)類和基本使用
四、Repository 的分類和創(chuàng)建使用
五、總結(jié)
六、相關(guān)代碼
七、預告

一、Agera 及相關(guān)概念簡介

Agera

Agera的github地址

Agera is a set of classes and interfaces to help write functional, asynchronous, and reactive applications for Android.Requires Android SDK version 9 or higher

簡單翻譯一下,Agera 是一組類和接口,幫助安卓開發(fā)者實現(xiàn)功能性的、異步的和響應式的應用。要求Android 的 SDK 版本在9以上

Agera (瑞典文的意思是"采取行動")是一個超輕量級的 Android 庫,幫助 Android 應用中有生命周期的組件(比如:Activities)或者組件中的對象(比如:Views)預準備數(shù)據(jù)。 通過加入函數(shù)式響應式編程,Agera可以在 什么時機, 什么線程 和 什么數(shù)據(jù) 層面上更清晰的分離數(shù)據(jù)處理流程,并且使用一個接近自然語言的單個表達式就能編寫一個復雜的異步流

觀察者模式

Agera 使用著名的 觀察者模式 作為響應式編程的驅(qū)動機制。 被觀察者(observable)實現(xiàn) Observable 接口, 并向所有注冊的觀察者們(observers)廣播事件。 觀察者(observer)實現(xiàn) Updatable 接口, 可以注冊和注銷到 Observable 中,接受通知事件觸發(fā)更新操作,故此命名為 Updatable

Push event, pull data

Agera 使用 push event, pull data 模型(推送事件,拉取數(shù)據(jù))。
push event:被觀察者只做事件通知,不攜帶任何數(shù)據(jù);
pull data:觀察者根據(jù)自己需要從數(shù)據(jù)倉庫(Repository.get())拉取數(shù)據(jù)

注冊/注銷 觀察者

Updatable 的注冊和注銷必須配對使用。不能重復注冊 Updatable,不能注銷沒有注冊過的 Updatable,也不能重復注銷 Updatable,等其他非法操作

public class Test extends Activity {
    //被觀察者
    Observable observable = new Observable() {
        @Override
        public void addUpdatable(@NonNull Updatable updatable) {
        }
        @Override
        public void removeUpdatable(@NonNull Updatable updatable) {
        }
    };
    //觀察者
    Updatable updatable = new Updatable() {
        @Override
        public void update() {
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //注冊觀察者
        observable.addUpdatable(updatable);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //注銷觀察者
        observable.removeUpdatable(updatable);
    }
}

Activation lifecycle

被觀察者(Observable)的有兩種狀態(tài):

  1. active狀態(tài)(活動狀態(tài)):有觀察者 (至少注冊一個了Updatable)
  2. inactive狀態(tài)(非活動狀態(tài)):沒有觀察者 (沒有注冊的Updatable)

也就是, 注冊 Updatable 讓 Observable 從非活動狀態(tài)激活為活動狀態(tài),當 Updatable 全部注銷了,Observable 從活動狀態(tài)變?yōu)榉腔顒訝顟B(tài)

二、 Agera 使用示例

為了讓小伙伴們對 Agera 有一個最初的印象,這里先給出一個 Agera 的使用示例

假設一下,有一個業(yè)務場景:

  1. 有一個按鈕,當我們點擊的時候在子線程進行網(wǎng)絡請求,下載一張圖片
  2. 切換到另一個子線程去壓縮圖片
  3. 回到主線程顯示出圖片
    //網(wǎng)絡請求框架
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            .build();

    Repository<Result<Bitmap>> repository = Repositories.repositoryWithInitialValue(Result.<Bitmap>absent())
            .observe()
            .onUpdatesPerLoop()
            .goTo(NETWORK_EXECUTOR)  //切換到子線程進行網(wǎng)絡請求
            .attemptGetFrom(() -> {
                runOnUiThread(() -> tvState.setText("正在子線程進行圖片下載..."));
                //網(wǎng)絡請求部分
                Request request = new Request.Builder()
                        .url(IMAGE_URL)
                        .get()
                        .build();
                try {
                    Response response = client.newCall(request).execute();
                    if (response.isSuccessful()) {
                        //為了更清楚的觀察狀態(tài),此處休眠一秒
                        Thread.sleep(1000);
                        return Result.present(response.body().byteStream());
                    } else {
                        //網(wǎng)絡請求失敗時的處理
                        return Result.failure(new Throwable("下載圖片失??!" + response.code() + response.message()));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    //異常時的處理
                    return Result.failure(new Throwable("下載圖片異常!" + e.getMessage()));
                }
            })
            .orEnd(Result::failure)
            .goTo(COMPRESS_EXECUTOR) //切換到壓縮圖片的線程
            .thenTransform(input -> {
                runOnUiThread(() -> tvState.setText("正在子線程進行圖片壓縮..."));
                //壓縮圖片的部分
                try {
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inSampleSize = 2;
                    Bitmap bitmap = BitmapFactory.decodeStream(input, new Rect(0, 0, 0, 0), options);
                    Thread.sleep(1500);
                    return Result.present(bitmap);
                } catch (Exception e) {
                    e.printStackTrace();
                    return Result.failure(new Throwable("壓縮圖片異常!" + e.getMessage()));
                }
            })
            .compile();

    Updatable updatable = new Updatable() {
        @Override
        public void update() {
            repository.get()
                    //上面所有的流程都正確執(zhí)行
                    .ifSucceededSendTo(value -> {  
                        tvState.setText("加載圖片完成");
                        ivImage.setImageBitmap(value);
                    })
                    //上面的流程發(fā)生錯誤
                    .ifFailedSendTo(value -> tvState.setText(value.getMessage()));
        }
    };

效果圖如下:


下載圖片.gif

如果看不太懂沒關(guān)系,后面我們會慢慢學到。

雖然你可能沒有看太懂,但是通過代碼你也可以感受到 Agera 的強大,整體非常簡潔,切換線程非常方便,只需一個 goto() 方法。

其實,在網(wǎng)絡請求那一部分我們還可以用更簡潔的代碼去完成,需要使用 Agera + Retrofit + Okhttp 三個框架來共同完成,這個我們后面也會講到

好了,對 Agera 有了初步的印象后,讓我們一起開始學習吧

三、 Agera相關(guān)類和基本使用

在 Agera 中,有幾個比較重要的類和接口,我們以它們?yōu)榍腥朦c,一步一步地學習如果使用 Agera

最開始的時候介紹了,Agera 是基于觀察者模式的,首先我們看一下觀察者、被觀察者在 Agera 中是如何表現(xiàn)的

1、 Updatable

翻譯為 :觀察者
用途:接收事件通知,更新數(shù)據(jù)
說明:觀察者模式中的 Observer,在 Agera 中使用 Updatable

我們看一下它的代碼,非常簡單,它是一個接口,包含一個 update() 方法。

public interface Updatable {
  void update();
}

2、 Observable

翻譯為:被觀察者、事件源
用途:作為事件源,通知觀察者更新數(shù)據(jù),可以注冊、注銷觀察者。
說明:當事件發(fā)生的時候,調(diào)用 dispatchUpdate() 通知觀察者。

Observable 也是一個接口,里面包含兩個方法,一個用于添加觀察者(也就是 Updatable ),一個用于移除觀察者( Updatable )

當有事件發(fā)生的時候,被觀察者(也就是 Observable ) 會通知所有添加過的觀察者( Updatable )

public interface Observable {
  void addUpdatable(@NonNull Updatable updatable);
  void removeUpdatable(@NonNull Updatable updatable);
}

注意,Updatable 的注冊和注銷必須配對使用

小結(jié)

我們先做一個小結(jié)

  1. Agera 是基于觀察模式的,Updatable 指代的是觀察者模式中的觀察者,而 Observable 所指代的就是觀察者模式中的被觀察者

  2. Agera 的更新機制是:使用 Updatable 去觀察Observable,Observable 去通知 Updatable 更新

現(xiàn)在我們了解了 Agera 的更新機制,那具體到代碼中是如何使用的呢?

  1. 定義一個 Updateable 和一個 Observable
  2. 使用 Observable 的 addUpdatable() 方法去將Updatable 注冊到 Observable 中
  3. 當某個事件發(fā)生時調(diào)用 Updatable 的 update() 方法進行更新操作
  4. 當我們不使用的時候,注銷 Updatable

下面我們來看一個具體的例子,按照上面的三個步驟實現(xiàn)。在布局中定義一個 Button 和 TextView,當點擊這個 Button 的時候,更新 TextView 中的文字

public class SimpleActivity extends Activity {

    Button btnTest;
    TextView tvHello;

      // 步驟1 定義一個 Updatable 
    Updatable updatable = new Updatable() {
        @Override
        public void update() {
           //當收到更新事件時,更新TextView中的文字
            tvHello.setText("Hello Agera");

            //步驟4 注銷 Updatable 
             observable.removeUpdatable(updatable);
        }
    };

    //步驟1 定義一個Observable 
    Observable observable = new Observable() {
        @Override
        public void addUpdatable(@NonNull Updatable updatable) {
            //步驟3 當某個 Updatable 被注冊時,通知該 Updatable 更新
            updatable.update();
        }

        @Override
        public void removeUpdatable(@NonNull Updatable updatable) {
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);

        btnTest = findViewById(R.id.btnTest);
        tvHello = findViewById(R.id.tvHello);

        btnTest.setOnClickListener(v -> 
        //步驟2 使用Observable的addUpdatable()方法去將Updatable注冊到Observable中
        observable.addUpdatable(updatable));
    }
}

由于代碼比較簡單,此處就不在給出實現(xiàn)的效果圖了

看到這里,有的小伙伴可能會有疑問了,這么簡單一個功能,我直接在點擊事件那里去更新TextView的提示就好了,何必要費那么大功夫,寫一個什么觀察者、被觀察者去實現(xiàn)?(這不就是 一頓操作猛如虎,一看戰(zhàn)績0-5么。。。orz)

剛開始接觸的時候可能會不太理解 Agera (觀察者模式)的工作機制,這個例子的目的是讓大家更好地理解 Agera 的工作模式

理解了上面的內(nèi)容,我們繼續(xù)往下看:

3、 Supplier

翻譯為:數(shù)據(jù)提供者
用途:get()新數(shù)據(jù)

Supplier 是一個帶泛型參數(shù)的接口,它有一個 get() 方法,返回一個T類型的值

public interface Supplier<T> {
  @NonNull
  T get();
}

Supplier 該怎么理解呢?簡單來說,它只有一個功能,就是提供一個數(shù)據(jù)。甚至就可以把它理解為一個變量

它的使用是這樣的:

  //定義一個數(shù)據(jù)為String類型的Supplier
  Supplier<String> stringSupplier = new   Supplier<String>() {
        @Override
        public String get() {
            //這里提供具體的值
            return "hello";
        }
    };
 
  //get()方法拿到上面提供的具體值
  //輸出 hello
   Log.d("tag", "stringSupplier " + stringSupplier.get()); 


  Supplier<Integer> integerSupplier = new Supplier<Integer>() {
        @Override
        public Integer get() {
            return 666;
        }
    };
    //輸出 666
    Log.d("tag", "integerSupplier " + integerSupplier.get());


  Supplier<JSONObject> jsonObjectSupplier = new Supplier<JSONObject>() {
        @Override
        public JSONObject get() {

            try {
                JSONObject jsonObject = new JSONObject("{\n" +
                        "    \"returnCode\": \"0000\",\n" +
                        "    \"returnMessage\": \"success\"\n" +
                        "}");
                return jsonObject;

            } catch (JSONException e) {
                e.printStackTrace();
            }

            return null;
        }
    };
   //輸出 {"returnCode":"0000","returnMessage":"success"}
    Log.d("tag", "jsonObjectSupplier " + jsonObjectSupplier.get().toString());

看到這,你應該已經(jīng)明白了它的作用了,非常簡單對吧。你可能還會有疑問,只是用于提供一個值,那我直接提供一個變量值就行了,為啥要巴拉巴拉寫那么大一堆?(難道又是傳說中的一頓操作猛如虎。。。)

我是這么理解的,Supplier 是一個提供數(shù)據(jù)的接口,它不僅僅是提供了一個數(shù)據(jù),關(guān)鍵是它代表了一種能力和一種規(guī)范,什么能力?能夠提供一個數(shù)據(jù)的能力!什么規(guī)范?當需要獲取數(shù)據(jù)時數(shù)據(jù)源必須是 Supplier 的規(guī)范!這種能力和規(guī)范接下來我們會接觸到

在實際的代碼中,它也沒有像上面那樣復雜,我們會使用lambda表達式還有匿名類將其簡化,比如

//使用lambda表達式簡化
 Supplier<String> stringSupplier = () -> "hello";


//使用lambda表達式和匿名類簡化
Repositories.repositoryWithInitialValue(0)
             .observe()
             .onUpdatesPerLoop()
            //注意,getFrom方法的參數(shù)必須是一個Supplier 。這就是上面說到的規(guī)范
             .getFrom(() -> "hello agera")
             ...
             ...//后面省略了一頓操作


//如果你是用Kotlin語言的話,還可以簡化成下面這樣
Repositories.repositoryWithInitialValue(0)
            .observe()
            .onUpdatesPerLoop()
            //同樣的參數(shù)必須是一個Supplier,此處是用lambda表達式簡化
            .getFrom { "hello Kotlin" }
            ...
            ...//后面省略了一頓操作
    ...

4、 Receiver

翻譯為:數(shù)據(jù)接收者
用途:accept(T vaule)接收新數(shù)據(jù)。

public interface Receiver<T> {
  void accept(@NonNull T value);
}

Receiver 是一個數(shù)據(jù)接收者,它與上面的講的 Supplier 的作用剛好相反。Receiver 也定義了一種能力和規(guī)范。能力是接收一個數(shù)據(jù)。規(guī)范是在 Agera中接收數(shù)據(jù)時,必須使用 Receiver 接口

在實際代碼中是這樣的:

   repository.get()
             .ifSucceededSendTo(new Receiver<Bitmap>() {
                        @Override
                        public void accept(@NonNull Bitmap value) {
                          //value值就是我們接收到的數(shù)據(jù)
                        }
                    })
             .ifFailedSendTo(new Receiver<Throwable>() {
                        @Override
                        public void accept(@NonNull Throwable value) {

                        }
                    });

上面代碼中的 ifSucceededSendTo 和 ifFailedSendTo 這兩個方法的參數(shù)都是一個 Receiver 對象

Supplier 定義了一種能力,提供 一個數(shù)據(jù)的能力
Receiver 定義了一種能力,接收 一個數(shù)據(jù)的能力

5、 Repository

翻譯為:Repository、數(shù)據(jù)倉庫
用途:接收事件源、提供數(shù)據(jù)源的結(jié)合體

上面我們已經(jīng)知道了 Updatable (觀察者)和 Observable(被觀察者),兩個主角都已經(jīng)就位了。那這個 Repository 又是何方神圣?它難道要來搶女一號?

是的,它才是真真正正主角。我們來看一下它的實現(xiàn):

public interface Repository<T> extends Observable, Supplier<T> {
}

它是一個接口,繼承了 Observable 接口和 Supplier 接口。

Observable 接口代表了是一個被觀察者,Supplier 代表了有提供數(shù)據(jù)的能力,Repository 同時繼承了這兩個接口說明什么?說明了它是一個 擁有提供數(shù)據(jù)能力的被觀察者!

Repository 翻譯為 倉庫 (或者數(shù)據(jù)倉庫)。名字非常形象,既然是倉庫,肯定能從它那里拿到貨物(也就是我們需要的數(shù)據(jù))

我們回想一下上面的那個例子,Observable 作為一個被觀察者,它能夠在某個事件發(fā)生時通知 Updatable。注意,這里僅僅是通知,并沒有提供任何數(shù)據(jù)。這也就是為什么會產(chǎn)生 Repository 的原因

Repository 彌補了不能提供數(shù)據(jù)的缺陷,它是擁有提供數(shù)據(jù)能力的被觀察者。與 Observable 相比它不僅能在某個事件發(fā)生時通知 Updatable,它還能提供一個數(shù)據(jù)。當 Updatable 收到更新事件的時候,我們就可以從 Repository 中獲取數(shù)據(jù),用于后續(xù)操作

注:在實際的應用中,我們接觸最多的就是Repository ,它才是真正的主角

6、 MutableRepository

翻譯為:MutableRepository、可改變數(shù)據(jù)倉庫
用途: 接收事件源、提供數(shù)據(jù)源的結(jié)合體、改變倉庫中的數(shù)據(jù)

這里怎么突然冒出來個 MutableRepository? 什么是 MutableRepository ? 它和 Repository 有什么區(qū)別?

通過上面的介紹,我們知道,Repository 通過繼承 Supplier 接口,實現(xiàn)了提供數(shù)據(jù)的能力。

注意哈,這里只是實現(xiàn)了 提供數(shù)據(jù)的能力。是不是感覺缺少點什么能力?是的,它缺少的就是 改變數(shù)據(jù)的能力!

在實際的開發(fā)中,我們僅僅能從 Repository 得到數(shù)據(jù)是不夠的,我們還需要的就是能夠需要改變 Repository 中的數(shù)據(jù)。這也就是 MutableRepository 存在的意義

public interface MutableRepository<T> extends Repository<T>, Receiver<T> {}

MutableRepository 也是一個接口,它繼承了 Repository 接口和 Receiver 接口

繼承 Receiver 接口給 MutableRepository 提供了一項新的能力,接收一個數(shù)據(jù)。通過 accept(@NonNull T value) 方法接收數(shù)據(jù),這樣就是實現(xiàn)了數(shù)據(jù)倉庫中的數(shù)據(jù)是可改變的

四、 Repository 的分類和創(chuàng)建使用

Repository 的分類

通過上面的介紹我們了解到,Repository 是擁有提供數(shù)據(jù)能力的被觀察者。它是一個接口,根據(jù)具體能力的不同,它的實現(xiàn)類被分為兩種:

1、簡單的 Repository

簡單的 Repository 又被分為兩種

  1. static repositories :提供相同的數(shù)據(jù)源而且不生成通知事件,只有g(shù)et()方法
  1. mutable repositories : 可提供變化的數(shù)據(jù)源(accept輸入->get輸出),數(shù)據(jù)變化時生成通知事件(依賴方法Object.equals(Object))

2、復雜的repositories

復雜的數(shù)據(jù)倉庫(Repository)可以響應其他數(shù)據(jù)倉庫(Repositories)、任意被觀察者(Observables)(也可以是該Repository的事件), 并把從其他數(shù)據(jù)源獲取的數(shù)據(jù)經(jīng)過同步或者異步內(nèi)部轉(zhuǎn)換處理后作為數(shù)據(jù)倉庫(Repository)的產(chǎn)出值。 從響應事件中數(shù)據(jù)倉庫(Repository)的數(shù)據(jù)提供者總保持數(shù)據(jù)最新的,但由于處理的復雜性,在數(shù)據(jù)倉庫(Repository)不活動時,可以選擇不保持數(shù)據(jù)為最新的。 任何數(shù)據(jù)消費者都需要通過注冊觀察者(Updatable)來表示自己需要讀取數(shù)據(jù)的意圖。 數(shù)據(jù)倉庫(Repository)進入活動狀態(tài),但數(shù)據(jù)不用立即更新,消費者看到的數(shù)據(jù)仍然是舊的,直到數(shù)據(jù)倉庫(Repository)分發(fā)第一個事件

這里所說的復雜的 repositories 就是在文章一開始給大家看的示例中的 Repository。由于復雜 Repository 相關(guān)知識點比較多,我會在后續(xù)文章中詳細介紹

Repository 的創(chuàng)建和使用

Agera 提供了一個 Repositories 工具類來幫助我們創(chuàng)建 Repository

Repositories 類提供個三個靜態(tài)方法,分別對應上面介紹的三種 Repository 的創(chuàng)建

//根據(jù)傳入的值創(chuàng)建一個Repository
public static <T> Repository<T> repository(@NonNull final T object)

//根據(jù)傳入的值創(chuàng)建一個可修改值的Repository(即 MutableRepository )
public static <T> MutableRepository<T> mutableRepository(@NonNull final T object)

//根據(jù)傳入值作為初始值,聲明一個復雜Repository(即CompiledRepository)的創(chuàng)建的開始
public static <T> REventSource<T, T> repositoryWithInitialValue(@NonNull final T initialValue)

簡單Repository 的創(chuàng)建和使用

//創(chuàng)建一個靜態(tài)的Rpository(即倉庫中的值不能改變)
 Repository<Integer> integerRepository = Repositories.repository(666);
  //輸出值為 666
 Log.d("tag", "integerRepository " + integerRepository.get());


 //創(chuàng)建可修改值的Repository(即 MutableRepository )
 MutableRepository<Integer> mutableRepository = Repositories.mutableRepository(888);
 //輸出為 888
 Log.d("tag", "mutableRepository " + mutableRepository.get());

 //通過accept方法改變倉庫中的值
 mutableRepository.accept(999);
 //輸出為 999
 Log.d("tag", "mutableRepository " + mutableRepository.get());

到這里,我們學到了如何去創(chuàng)建一個Repository ,并且根據(jù)我們的需要去 更新 / 獲取 Repository 中的值

還記得上面點擊按鈕改變文字那個例子嗎?現(xiàn)在我們用 Repository 改進一下: 當我們點擊按鈕的時候,文字上顯示出當前的時間,精確到秒

實現(xiàn)思路:

  1. 創(chuàng)建一個 Updatable (觀察者) 和一個 MutableRepository (被觀察者&& 數(shù)據(jù)提供者)
  2. 為數(shù)據(jù)倉庫 MutableRepository 添加一個觀察者 Updatable
  3. 當點擊按鈕時,更新倉庫 MutableRepository 中的值
  4. 由于 MutableRepository 中的值發(fā)送了改變,此時 Updatable 會收到更新事件,在 update() 方法中獲取到 MutableRepository 中的最新值,并顯示出來
  5. 在不使用時注銷 Updatable

代碼如下:

public class SimpleActivity extends Activity {
    Button btnTest;
    TextView tvHello;
    DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);

    //1. 創(chuàng)建一個 Updatable (觀察者)
    Updatable updatable = new Updatable() {
        @Override
        public void update() {
            //4. 從數(shù)據(jù)倉庫中獲取數(shù)據(jù)
            String currentTime = mutableRepository.get();
            tvHello.setText(currentTime);
        }
    };

    //1. 創(chuàng)建一個 MutableRepository (被觀察者&& 數(shù)據(jù)提供者)
    MutableRepository<String> mutableRepository = Repositories.mutableRepository(getCurrentTime());

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);

        btnTest = findViewById(R.id.btnTest);
        tvHello = findViewById(R.id.tvHello);

        //3. 當點擊按鈕時,更新倉庫 MutableRepository  中的值
        btnTest.setOnClickListener(v -> mutableRepository.accept(getCurrentTime()));
    }

    @Override
    protected void onResume() {
        super.onResume();
        //2. 為數(shù)據(jù)倉庫 MutableRepository 添加一個觀察者 Updatable
        mutableRepository.addUpdatable(updatable);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //5. 移除觀察者
        mutableRepository.removeUpdatable(updatable);
    }

    private String getCurrentTime() {
        return format.format(new Date());
    }
}

運行看一下效果,當我們點擊按鈕時,會顯示當前的時間:


顯示時間.gif

看到這里不知道小伙伴有沒有疑問,就是為什么當MutableRepository 的值改變的時候(也就是我們點擊按鈕),Updatable 會接收到更新事件?

其實是這樣的,MutableRepository 只是一個接口。當我們用Repositories.mutableRepository(getCurrentTime())的時候,真正創(chuàng)建的是一個 SimpleRepository 對象。 這個對象繼承了 BaseObservable 類,并且實現(xiàn)了 MutableRepository接口。BaseObservable 類中幫我們做了處理,當倉庫中的值改變的時候(也就是調(diào)用了 mutableRepository.accept() ) 方法后,如果接收的新值與原有的舊值不相同的話,就去通知注冊過的 Updatable 更新

這一部分內(nèi)容如果你還不了解的話,可以先不用深究,知道如何使用就好。后續(xù)文章會解析相關(guān)源碼

注意: BaseObservable 中判斷新舊值是否相等使用的是 boolean equals(Object obj) 方法

注意:Updatable 的注冊和注銷必須配對使用。在有聲明周期的地方,比如 Activity,就可以在 onResume、onPause 等回調(diào)方法去添加/注銷 Updatable

這里在給大家留一個小的思考題,上面的例子,我們是點擊按鈕后更新當前時間并顯示出來,現(xiàn)在要求不用點擊按鈕,并且時間一直在更新(類似電子鐘表)。該如何實現(xiàn)呢?你可以嘗試自己實現(xiàn)一下。如果有疑問可以留言與我交流

五、 總結(jié)

這里我們做一下總結(jié):

  1. Agera 是基于 觀察者模式 的。在 Agera 中,Updatable 指代的是觀察者模式中的觀察者, Observable 所指代的就是觀察者模式中的被觀察者
  2. Supplier 接口定義了 提供 一個數(shù)據(jù)的能力
  3. Receiver 定義了 接收 一個數(shù)據(jù)的能力
  4. Repository 是一個 擁有提供數(shù)據(jù)能力的被觀察者
  5. MutableRepository 是一個 擁有 提供/更新 數(shù)據(jù)能力的被觀察者
  6. 簡單的 Repository 通過 Repositories 類中的靜態(tài)方法去創(chuàng)建(創(chuàng)建出來的對象實際上是 SimpleRepository 對象)
  7. 當 MutableRepository 中的值 改變 的時候(值的改變是通過equals方法判斷的),會通知所有注冊過的 Updatable 更新(這個操作是在 BaseObservable 中處理的)

下面我用一張表格對本文中所講到的類/接口再做一個總結(jié):

類/接口 描述 作用
Updatable 觀察者 更新事件
Observable 被觀察者 添加/刪除Updatable
Supplier 數(shù)據(jù)提供者 提供一個數(shù)據(jù)
Receiver 數(shù)據(jù)接收者 接收一個數(shù)據(jù)
Repository 擁有提供數(shù)據(jù)能力的被觀察者 添加/刪除Updatable、提供一個數(shù)據(jù)
MutableRepository 擁有 提供/更新 數(shù)據(jù)能力的被觀察者 添加/刪除Updatable、提供/更新一個數(shù)據(jù)

六、相關(guān)代碼

https://github.com/smashinggit/Study

注:此工程包含多個module,本文所用代碼均在AgeraDemo下

七、預告

由于篇幅有限,這篇文章只是給小伙伴們介紹了 Agera 的基本概念和基本使用。在后面的文章中,將會詳細介紹 Repository 的具體使用,敬請期待~

后續(xù)文章已經(jīng)更新啦~
Android官方響應式框架Agera詳解:二、Repository的創(chuàng)建和操作符

注:由于本人水平有限,所以難免會有理解偏差或者使用不正確的問題。如果小伙伴們有更好的理解或者發(fā)現(xiàn)有什么問題,歡迎留言批評指正~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容