1. 程式人生 > >淺談移動架構

淺談移動架構

原文地址:

幾十億的裝置都在用 Android 系統,從高階手機到飛機上的影音娛樂系統,應有盡有,不一而足。而 Android OS 則為這幾十億的裝置保駕護航,高效管理資源,保障執行流暢,較高的相容性, 然而有時候卻增加了開發卓越 App 的難度。 我一直堅持“框架能解決的事 最好從源頭解決”,團隊每位同事的開發習慣與技術儲備都不一樣,在生命週期與邏輯控制都截然不同,元件為空,缺少判斷,記憶體洩漏不關心,資料庫連線不釋放,view的命名千差萬別,mvc邏輯冗餘,mvp介面太多,資料重新整理與ui重新整理不同步, 怎樣做到獨一無二的體驗呢?怎麼做到行業的標杆(微信)呢? 這是我一直深思的問題! 當你看到這篇部落格的同時,想必你也同樣困惱許久,繼續看,這篇部落格可能改變你的思維方式:iCourt_Android_MVVM; 移動端mvc->mvp->mvvm,不斷演變,我們推陳出新,適應時代,完善了這款mvvm框架, 關於這款框架,我有三個詞來總結:"樣板","生命週期","響應式mvvm", 為什麼這麼說呢?以前我們在重新整理控制的時候, 會寫很多很多程式碼:比如:

 public void getData(final boolean isRefresh) {
        //資料為空 展示載入佈局
        if (isRefresh && approveAdapter.isDataEmpty()) {
            alphaStateLayout.setViewState(ViewState.VIEW_STATE_LOADING);
        }
        RetrofitServiceFactory
                .getApproveApiService().approvesQueryMyApplyObservable(0, 20, 1)
                .map(new ResEntitySimpleFunction<ItemPageEntity<ApproveEntity>>())
                .compose(this.<ItemPageEntity<ApproveEntity>>bindToLifecycle())
                .observeOn(AndroidSchedulers.mainThread())//不習慣寫 資料流在主執行緒接收->引發UI崩潰
                .subscribe(new Consumer<ItemPageEntity<ApproveEntity>>() {
                    @Override
                    public void accept(ItemPageEntity<ApproveEntity> approveEntityItemPageEntity) throws Exception {
                        approveAdapter.bindData(isRefresh, approveEntityItemPageEntity.items);
                        refreshLayout.finishRefresh();
                        refreshLayout.finishLoadmore();
                        alphaStateLayout.setViewState(approveAdapter.isDataEmpty() ? ViewState.VIEW_STATE_EMPTY : ViewState.VIEW_STATE_CONTENT);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        //不習慣寫 錯誤ui接收
                        refreshLayout.finishRefresh();
                        refreshLayout.finishLoadmore();
                        alphaStateLayout.setViewState(approveAdapter.isDataEmpty() ? ViewState.VIEW_STATE_ERROR : ViewState.VIEW_STATE_CONTENT);
                    }
                });
    }


上面的程式碼 邏輯看著複雜 團隊成員會引發很多問題:

1. 不繫結生命週期(非同步請求),記憶體洩漏,引發崩潰,記憶體佔用高,卡頓...

2. 忘記在主執行緒接收資料流來更新ui(引發崩潰)

3. 忘記寫錯誤ui展示與toast提示(互動不完善)

4. 網路邏輯與ui繫結得太厲害了(是展示loading佈局,還是loading dialog呢?),複用性太低

.....

先透個底:最新方式:

   getApproves(approveQueryType, selectedApproveTypes, pageIndex)
                .compose(this.<ItemPageEntity<ApproveEntity>>bindToLifecycle())
                .compose(this.<ItemPageEntity<ApproveEntity>>bindToStateRecyclerview(refreshStateRecyclerviewVM))
                .subscribe(new Consumer<ItemPageEntity<ApproveEntity>>() {
                    @Override
                    public void accept(ItemPageEntity<ApproveEntity> approveEntityItemPageEntity) throws Exception {
                        pageIndex++;
                        approveAdapter.bindData(isRefresh, approveEntityItemPageEntity.items);
                        refreshStateRecyclerviewVM.setLoadMoreEnable(approveEntityItemPageEntity.hasMoreHttpData());
                    }
                });
優勢
1: 具體請求邏輯不關心
2: 完全跟view 沒有建立任何訪問關係
3: 即使記憶體洩漏,也不會引發崩潰(這裡已經繫結生命週期啦)
4: 多狀態佈局(先生載入loading佈局,顯示error錯誤佈局,顯示頁面資料為空的佈局) 控制十分簡潔
5: 重新整理控制(重新整理結束方法呢?)無須呼叫(這尼瑪這麼做到的?)

業界 app架構設計,五花八門,沒有最好的架構設計,脫離業務的架構設計是沒有意義的,適合業務的才是好架構,而架構也不是一成不變的,隨著業務的發展,當初的架構已經不適合,這對團隊提出了更高的要求

元件化
    元件化的核心思想就是:分而治之,降低耦合,如何實現元件化?我們需要考慮3個問題:
    1:  元件單獨執行
    2:  元件間通訊
    3:  元件拆分粒度

元件化實施步驟:
    as 自帶兩種module外掛方式
    1:  apply plugin: ‘com.android.application’(作為app獨立執行)
     2:  apply plugin: ‘com.android.library’(作為依賴庫存在)
     我們在開發階段可以採用application獨立執行的方式,到提測的時候 合併到主專案中去

MVVM 
     mvvm的思想和目標與mvp類似:view 和model 不直接互動

    

但是mvvm 比mvp 有更好的優勢:
1: 資料驅動

在MVVM中,以前的開發模式是必須先梳理業務資料,然後根據資料去重新整理UI,提交資料也是通過view來獲取,然而在mvvm中,資料和業務邏輯處於一個獨立的ViewModel中,ViewModel只關係資料和業務邏輯,不需要和UI或者控制元件打交到,同時移動開發可以拆分前端和後端開發,由資料去驅動UI自動更新,UI的改變同時有反饋到資料的改變,這就是雙向繫結的思想,讓資料成為主導因素,這樣使得業務邏輯處理只關係資料,簡單,隔離,最重要的是再也不會出現更新UI空指標問題,與再自執行緒更新UI導致崩潰的問題!


2: 低耦合度

      MVVM模式中,資料是獨立於UI的,ViewModel只負責處理和提供資料,UI想怎麼處理資料都由UI自己決定,ViewModel 不涉及任何和UI相關的事也不持有UI控制元件的引用,即使控制元件改變(TextView 換成 EditText)ViewModel 幾乎不需要更改任何程式碼,專注自己的資料處理就可以了,如果是MVP遇到UI更改,就可能需要改變獲取UI的方式,改變更新UI的介面,改變從UI上獲取輸入的程式碼,可能還需要更改訪問UI物件的屬性程式碼等等
3: 動態增量重新整理UI

      在MVVM中,我們可以在工作執行緒中直接修改View Model的資料(只要資料是執行緒安全的),剩下的資料繫結框架幫你搞定,很多事情都不需要你去關心。
4: 團隊協作
      MVVM的分工是非常明顯的,由於View和View Model之間是鬆散耦合的。一個是處理業務和資料,一個是專門的UI處理。完全有兩個人分工來做,一個做UI(xml 和 Activity)一個寫ViewModel,效率更高。
5: 可複用性
       一個View Model複用到多個View中,同樣的一份資料,用不同的UI去做展示,對於版本迭代頻繁的UI改動,只要更換View層就行,對於如果想在UI上的做AbTest 更是方便的多。
6: 單元測試

      View Model裡面是資料和業務邏輯,View中關注的是UI,這樣的做測試是很方便的,完全沒有彼此的依賴,不管是UI的單元測試還是業務邏輯的單元測試,都是低耦合的。


  特性之一 模版
  這套框架能幫你解決很多問題,如重新整理,顯示loading對話方塊,展示空狀態或者錯誤佈局

package com.icourt.alpha.module.approve.source;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.icourt.alpha.R;
import com.icourt.alpha.utils.ActionConstants;
import com.icourt.architecture.util.JsonUtils;
import com.icourt.alpha.utils.StringUtils;
import com.icourt.api.RequestUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.functions.Function;

/**
 * @author youxuan  E-mail:[email protected]
 * @version 2.2.2
 * @Description 審批模組資料倉庫
 * @Company Beijing icourt
 * @date createTime:2017/12/6
 */
public class ApproveRepository
        extends AlphaBaseViewModel
        implements
        ApproveDataSource {
......
    /**
     * 輸入專案編號
     *
     * @param projectId
     * @param projectNumber
     * @return
     */
    @Override
    public Observable<JsonElement> approveProjectNumberInputHUD(String projectId, String projectNumber) {
        return RetrofitServiceFactory
                .getProjectApiService()
                .projectFillProjectNumber(projectId, projectNumber)
                .map(new ResEntitySimpleFunction<JsonElement>())
                .compose(this.<JsonElement>bindToProgressHUD())// 載入進度對話方塊
                .compose(this.<JsonElement>bindToLifecycle());//繫結生命週期 防止記憶體洩漏
    }
......
}
上面的程式碼 既實現了網路請求,又在資料上游處理了loading對話方塊的互動
很關鍵的一句:
.compose(this.<JsonElement>bindToProgressHUD())
我們知道RxJava 有變換一說,可以資料變換,可以流呼叫變換,還可以執行緒變換,那麼我們怎麼將loading的載入,消失等狀態與網路請求繫結呢?
package com.icourt.ui.binding;

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.icourt.architecture.mvvm.viewmodel.ICourtViewModel;
import com.icourt.architecture.util.LogUtils;
import com.icourt.ui.AlphaActionHelper;

/**
 * @author youxuan  E-mail:[email protected]
 * @version 2.3.0
 * @Description
 * @Company Beijing icourt
 * @date createTime:2017/12/13
 */
public class AlphaBaseViewModel extends ICourtViewModel {
    

    @Override
    public <T> SubscribeLifeTransformer<T> bindToProgressHUD() {
        return new AlphaProgressHUDTransformer<>(this, null, null, null);
    }

    @Override
    public <T> SubscribeLifeTransformer<T> bindToProgressHUD(String loadingNotice) {
        return new AlphaProgressHUDTransformer<>(this, loadingNotice, null, null);
    }

    @Override
    public <T> SubscribeLifeTransformer<T> bindToProgressHUD(@Nullable String loadingNotice, @Nullable String successNotice) {
        return new AlphaProgressHUDTransformer<>(this, loadingNotice, null, successNotice);
    }

    @Override
    public <T> SubscribeLifeTransformer<T> bindToProgressHUD(@Nullable String loadingNotice, @Nullable String errorNotice, @Nullable String successNotice) {
        return new AlphaProgressHUDTransformer<>(this, loadingNotice, errorNotice, successNotice);
    }
}

可以看到 我綁定了一個transformer,原始碼如下:

package com.icourt.architecture.rxjava;

import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.icourt.architecture.binding.IICourtProgressHUD;

import org.reactivestreams.Publisher;

import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.Single;
import io.reactivex.SingleSource;

/**
 * @author youxuan  E-mail:[email protected]
 * @version 2.3.0
 * @Description 載入與loading提示結合
 * @Company Beijing icourt
 * @date createTime:2018/1/4
 */
public class ProgressHUDTransformer<T> extends SubscribeLifeTransformer<T> {

    protected IICourtProgressHUD iiCourtProgressHUD;
    protected String loadingNotice;
    protected String errorNotice;
    protected String successNotice;

    /**
     * @param iiCourtProgressHUD
     * @param loadingNotice      可空
     * @param errorNotice        如果為空,直接展示預設的error,否則展示errorNotice
     * @param successNotice      空 不展示正確對勾符號
     */
    public ProgressHUDTransformer(IICourtProgressHUD iiCourtProgressHUD,
                                  @Nullable String loadingNotice,
                                  @Nullable String errorNotice,
                                  @Nullable String successNotice) {
        this.iiCourtProgressHUD = iiCourtProgressHUD;
        this.loadingNotice = loadingNotice;
        this.errorNotice = errorNotice;
        this.successNotice = successNotice;
    }

    @Override
    protected void onSubscribe() {
        if (iiCourtProgressHUD != null) {
            iiCourtProgressHUD.showICourtLoadingDialog(loadingNotice);
        }
    }

    @Override
    protected void onComplete() {
        if (iiCourtProgressHUD != null) {
            iiCourtProgressHUD.dismissICourtLoadingDialogWithSuccess(successNotice);
        }
    }

    @Override
    protected void onError(Throwable throwable) {
        if (iiCourtProgressHUD != null) {
            if (TextUtils.isEmpty(errorNotice)) {
                iiCourtProgressHUD.dismissICourtLoadingDialogWithFail(throwable.getMessage());
            } else {
                iiCourtProgressHUD.dismissICourtLoadingDialogWithFail(errorNotice);
            }
        }
    }

    @Override
    protected void onCancel() {
        if (iiCourtProgressHUD != null) {
            iiCourtProgressHUD.dismissICourtLoadingDialog();
        }
    }

}

在這個轉換器中 實現了三個方法

1.onSubscribe() 流式響應執行開始

2. onComplete() 動作執行完成

3. onError(Throwable t) 流動作執行錯誤,如網路請求伺服器500
4. onCancel() 流動作執行取消,如網路請求取消

我們繼續看一下基類:

package com.icourt.architecture.rxjava;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;

import io.reactivex.Flowable;
import io.reactivex.FlowableTransformer;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.MaybeTransformer;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.SingleTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;

/**
 * @author youxuan  E-mail:[email protected]
 * @version 2.3.0
 * @Description <p>
 * <p>
 * <p>
 * 特性:
 * 1: 處理UI操作
 * 2:接收處理預設分發在主執行緒
 * @Company Beijing icourt
 * @date createTime:2017/12/24
 * </p>
 */
public abstract class SubscribeLifeTransformer<T>
        implements
        ObservableTransformer<T, T>,
        FlowableTransformer<T, T>,
        SingleTransformer<T, T>,
        MaybeTransformer<T, T> {

    /**
     * 開始執行
     */
    protected abstract void onSubscribe();

    /**
     * 執行結束
     */
    protected abstract void onComplete();

    /**
     * 執行失敗
     *
     * @param throwable
     */
    protected abstract void onError(Throwable throwable);

    /**
     * 執行取消
     */
    protected void onCancel() {

    }

    @Override
    public Publisher<T> apply(Flowable<T> upstream) {
        return upstream
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(new Consumer<Subscription>() {
                    @Override
                    public void accept(Subscription subscription) throws Exception {
                        onSubscribe();
                    }
                }).doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        onError(throwable);
                    }
                }).doOnComplete(new Action() {
                    @Override
                    public void run() throws Exception {
                        onComplete();
                    }
                }).doOnCancel(new Action() {
                    @Override
                    public void run() throws Exception {
                        onCancel();
                    }
                });
    }

    @Override
    public MaybeSource<T> apply(Maybe<T> upstream) {
        return upstream
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) throws Exception {
                        onSubscribe();
                    }
                }).doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        onError(throwable);
                    }
                }).doOnComplete(new Action() {
                    @Override
                    public void run() throws Exception {
                        onComplete();
                    }
                }).doOnDispose(new Action() {
                    @Override
                    public void run() throws Exception {
                        onCancel();
                    }
                });
    }

    @Override
    public ObservableSource<T> apply(Observable<T> upstream) {
        return upstream
                .observeOn(AndroidSchedulers.mainThread())//流下游接收資料 在主執行緒執行
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) throws Exception {
                        onSubscribe();
                    }
                }).doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        onError(throwable);
                    }
                }).doOnComplete(new Action() {
                    @Override
                    public void run() throws Exception {
                        onComplete();
                    }
                }).doOnDispose(new Action() {
                    @Override
                    public void run() throws Exception {
                        onCancel();
                    }
                });
    }

    @Override
    public SingleSource<T> apply(Single<T> upstream) {
        return upstream
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) throws Exception {
                        onSubscribe();
                    }
                }).doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        onError(throwable);
                    }
                }).doOnSuccess(new Consumer<T>() {
                    @Override
                    public void accept(T t) throws Exception {
                        onComplete();
                    }
                }).doOnDispose(new Action() {
                    @Override
                    public void run() throws Exception {
                        onCancel();
                    }
                });
    }
}
subscribeLifeTransformer 監聽了流的生命週期,並且將資料下游的接收放到主執行緒(需要與UI元件互動),不僅支援Observable,還支援Flowable 等流式響應物件

重新整理控制 我們需要關注四個問題:
1. 重新整理開始

2. 重新整理結束

3.能不能重新整理

4.能不能上拉載入

5. 是手勢觸發的重新整理,還是自動重新整理呢?
package com.icourt.ui.binding.alphamodulevm;

import android.databinding.ObservableBoolean;

import com.scwang.smartrefresh.layout.listener.OnRefreshLoadmoreListener;

/**
 * @author youxuan  E-mail:[email protected]
 * @version 2.3.0
 * @Description 包括多狀態佈局 與重新整理控制
 * @Company Beijing icourt
 * @date createTime:2017/12/24
 */
public interface IRefreshStateRecyclerviewVM
        extends OnRefreshLoadmoreListener, IAlphaStateRecyclerviewVM {

    /**
     * 是否觸發的
     *
     * @return
     */
    ObservableBoolean isFromGesture();

    /**
     * 設定手勢觸發
     *
     * @param isFromGesture
     */
    void setFromGesture(boolean isFromGesture);

    /**
     * 是否可以重新整理
     *
     * @return
     */
    ObservableBoolean getIsRefreshEnable();


    /**
     * 是否可以重新整理
     *
     * @param refreshEnable
     */
    void setRefreshEnable(boolean refreshEnable);

    /**
     * 是否正在重新整理
     *
     * @return
     */
    ObservableBoolean getIsRefreshing();


    /**
     * 是否在重新整理
     *
     * @param refreshing
     */
    void setRefreshing(boolean refreshing);

    /**
     * 是否結束重新整理
     *
     * @return
     */
    ObservableBoolean getIsLoadMoreEnable();

    /**
     * 是否能夠重新整理
     *
     * @param loadMoreEnable
     */
    void setLoadMoreEnable(boolean loadMoreEnable);


}

區域性VM 控制著重新整理 我們只需要改變其中的值 就可以控制重新整理過程,這個跟資料驅動的理念非常貼合,我們實現了重新整理控制的ViewModel 那怎麼繫結呢?

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:bind="http://schemas.android.com/tools">

    <!--  標準模版佈局 -->
    <data>

        <variable
            name="vm"
            type="com.icourt.ui.binding.alphamodulevm.IRefreshStateRecyclerviewVM" />
    </data>

    <com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:srlEnableLoadmore="false"
        bind:isRefreshEnable="@{vm.getIsRefreshEnable()}"
        bind:isRefreshing="@{vm.getIsRefreshing()}"
        bind:loadMoreEnable="@{vm.getIsLoadMoreEnable()}"
        bind:refreshListener="@{vm}">

        <com.icourt.loading.AlphaStateLayout
            style="@style/style_alpha_state"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            bind:emptyDesc="@{vm.getEmptyDesc()}"
            bind:emptyIcon="@{vm.getEmptyIcon()}"
            bind:errorDesc="@{vm.getErrorDesc()}"
            bind:loadingViewState="@{vm.getLoadingViewState()}"
            bind:retryListener="@{vm}">

            <!-- 預設白色 -->
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:nestedScrollingEnabled="false"
                android:overScrollMode="never"
                bind:adapter="@{vm.getRecyclerAdapter()}"
                bind:itemDecoration="@{vm.getItemDecorationFactory()}"
                bind:layoutManager="@{vm.getLayoutManagerFactory()}" />
        </com.icourt.loading.AlphaStateLayout>
    </com.scwang.smartrefresh.layout.SmartRefreshLayout>
</layout>
在XML 中我們綁定了View與ViewModel 也就是資料改變View自動增量重新整理,對於binding標籤語法比較陌生的朋友可以參考這裡

特性之二:生命週期

      在移動開發中,很大一個特性需要知道元件的生命週期,場景(鎖屏的時候同步資料,螢幕可見的時候拉取資料...),有生命週期的元件只是限於系統原生元件,在mvp中會寫很多介面方法(onStart,onDestroy...)然後在活動或者碎片的基類裡面來控制,不太靈活,這時候有一個新東西:Lifecycle

Lifecycle

Lifecycle 是一個持有元件(比如 activity 或者 fragment)生命週期狀態資訊的類,並且允許其它物件觀察這個狀態。

Lifecycle 主要使用兩個列舉來跟蹤相關元件的生命週期狀態。

Event

framework和Lifecycle類發出的生命週期事件。這些事件對應Activity和Fragment中的回撥事件。

正因為lifecycle 基於註解,比較靈活,所以我這樣封裝了,避免方法名字在不同成員間產生命名差異

package com.icourt.architecture.binding;

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.OnLifecycleEvent;

/**
 * @author youxuan  E-mail:[email protected]
 * @version 2.2.2
 * @Description 元件的生命週期
 * @Company Beijing icourt
 * @date createTime:2017/12/6
 */
public class ICourtLifecycleAllObserver implements ICourtLifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onComponentLifecycleCreate(LifecycleOwner owner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onComponentLifecycleStart(LifecycleOwner owner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onComponentLifecycleResume(LifecycleOwner owner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onComponentLifecyclePuase(LifecycleOwner owner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onComponentLifecycleStop(LifecycleOwner owner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onComponentLifecycleDestroy(LifecycleOwner owner) {

    }

    @Override
    public void onComponentLifecycleAny(LifecycleOwner owner, Lifecycle.Event event) {

    }
}
這樣的模版方法 能建立團隊成員的共識,找尋程式碼更加迅速



欲知更多,請聽下回分解

原文地址