Lifecycle+Retrofit+Room完美結合 領略架構之美

安卓開發技術發展到現在已經非常成熟,有很多的技術專項如插件,熱修,加固,瘦身,性能優化,自動化測試等已經在業界有了完善的或者開源的解決方案。

作爲一枚多年的安卓研發,有必要學習或瞭解下這些優秀的解決方案,領略那些行業開創者的思想魅力,然後轉化爲自己的技術技能,爭取應用到日常的開發中去,提高自己研發水平。

Lifecycle+Retrofit+Room 雲端漫步飛一般的感覺

安卓項目的開發結構,有原來最初的mvc,到後來有人提出的mvp,然後到mvvm的發展,無非就是依着六大設計原則的不斷解耦,不斷演變,使得項目的開發高度組件化,滿足日常複雜多變的項目需求。

  • 依賴倒置原則-Dependency Inversion Principle (DIP)
  • 里氏替換原則-Liskov Substitution Principle (LSP)
  • 接口分隔原則-Interface Segregation Principle (ISP)
  • 單一職責原則-Single Responsibility Principle (SRP)
  • 開閉原則-The Open-Closed Principle (OCP)

目前針對MVVM框架結構,安卓官方也給出了穩定的架構版本1.0。

本文也是圍繞者官方思想,試着從源碼角度總結學習經驗,最後將這些控件二次封裝,更加便於理解使用。其也會涉及到其他優秀的的庫,如Gson,Glide,BaseRecyclerViewAdapterHelper等

一.起因

去年還在手機衛士團隊做垃圾清理模塊時候,趕上模塊化二次代碼重構技術需求,項目分爲多個進程,其中後臺進程負責從DB中獲取數據(DB可雲端更新升級),然後結合雲端配置信息做相關邏輯操作,將數據回傳到UI進程,UI進程中在後臺線程中二次封裝,最後post到主UI線程Update到界面展示給用戶。

當時就和團隊的同學溝通交流,面對這種數據多層次複雜的處理邏輯,設想可以做一種機制,將UI層的更新綁定到一個數據源,數據源數據的更新可以自動觸發UI更新,實現與UI的解耦。
數據源控制着數據的來源,每種來源有着獨立的邏輯分層,共享底層一些公共lib庫。

後來想想,其實差不多就是MVVM思想,直到谷歌官方宣佈Android 架構組件 1.0 穩定版的發佈,才下決心學習下官方這一套思想,感受優秀的架構。

引用官方一張結構圖如下:
architecture

二.各組件庫原理及基本用法

這裏主要探究下主要組件庫的基本用法和原理,以理解其優秀思想爲主。

谷歌官方Android Architecture Components

Lifecycle+LiveData+ViewMode+Room

A collection of libraries that help you design robust, testable, and maintainable apps. Start with classes for managing your UI component lifecycle and handling data persistence.

Lifecyle

一個安卓組件生命週期感知回調的控件,可以感知activity或fragment的生命週期變化,並回調相應接口。

可以先從源碼抽象類分析,如下:

public abstract class Lifecycle {
    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);

    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);

    @MainThread
    public abstract State getCurrentState();

    @SuppressWarnings("WeakerAccess")
    public enum Event {

        ON_CREATE,

        ON_START,

        ON_RESUME,

        ON_PAUSE,

        ON_STOP,

        ON_DESTROY,

        ON_ANY
    }

    @SuppressWarnings("WeakerAccess")
    public enum State {

        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;

        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }
}

/**
 * Marks a class as a LifecycleObserver. It does not have any methods, instead, relies on
 * {@link OnLifecycleEvent} annotated methods.
 * <p>
 * @see Lifecycle Lifecycle - for samples and usage patterns.
 */
@SuppressWarnings("WeakerAccess")
public interface LifecycleObserver {

}

Lifecycle抽象類有三個方法,可以添加、刪除觀察者,並且可以獲取當前組件的生命週期的狀態。
LifecycleObserver接口只是個空接口做類型校驗,具體事件交給了OnLifecycleEvent,通過注入回調相應事件。

二在最新的support V4包中,ActivityFragment都實現了LifecycleOwner接口,意味着我們可以直接使用getLifecyle()獲取當前ActivityFragment的Lifecycle對象,很方便的添加我們的監聽方法。

@SuppressWarnings({"WeakerAccess", "unused"})
public interface LifecycleOwner {

    @NonNull
    Lifecycle getLifecycle();
}

LiveData

LiveData是一個持有範型類型的數據組件,將App生命組件與數據關聯到一起,可以感知生命變更,規則回調數據變化。

public abstract class LiveData<T> {
            private volatile Object mData = NOT_SET;//數據容器類

    //一個寫死的處於Resume狀態的LifecycleOwner,用於數據回調無限制情況
    private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {

        private LifecycleRegistry mRegistry = init();

        private LifecycleRegistry init() {
            LifecycleRegistry registry = new LifecycleRegistry(this);
            registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
            registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
            registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
            return registry;
        }

        @Override
        public Lifecycle getLifecycle() {
            return mRegistry;
        }
    };
    private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
            new SafeIterableMap<>();

    private void considerNotify(LifecycleBoundObserver observer) {
        if (!observer.active) {
            return;
        }

        if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.lastVersion >= mVersion) {
            return;
        }
        observer.lastVersion = mVersion;
        //noinspection unchecked
        observer.observer.onChanged((T) mData);//最終的回調方法地方
    }

    private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());//可以添加做個監聽者,這裏遍歷分發數據到每個監聽者
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && existing.owner != wrapper.owner) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

    @MainThread
    public void observeForever(@NonNull Observer<T> observer) {
        observe(ALWAYS_ON, observer);
    }

    @MainThread
    public void removeObserver(@NonNull final Observer<T> observer) {
        assertMainThread("removeObserver");
        LifecycleBoundObserver removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        removed.owner.getLifecycle().removeObserver(removed);
        removed.activeStateChanged(false);
    }

    //實現類回調方法
    protected void onActive() {

    }

    //實現類回調方法
    protected void onInactive() {

    }

    class LifecycleBoundObserver implements GenericLifecycleObserver {

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(observer);
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
        }

        void activeStateChanged(boolean newActive) {
            if (newActive == active) {
                return;
            }
            active = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += active ? 1 : -1;
            if (wasInactive && active) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !active) {
                onInactive();
            }
            if (active) {//只有生命組件處於前臺時,才觸發數據的變更通知
                dispatchingValue(this);
            }
        }
    }

    static boolean isActiveState(State state) {
        return state.isAtLeast(STARTED);
    }
}

看源碼,會發現LiveData有個重要的方法observe(LifecycleOwner owner, Observer observer), 在數據源數據有變更時,遍歷分發數據到所有監聽者,最後會回調onChanged()方法。

public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(@Nullable T t);
}

LiveData有兩個實現類:MediatorLiveDataMediatorLiveData,繼承關係如下:

LiveData

MutableLiveData類只是暴露了兩個方法:postData()和setData()。
MediatorLiveData類有個addSource()方法,可以實現監聽另一個或多個LiveData數據源變化,這樣我們就可以比較便捷且低耦合的實現多個數據源的邏輯,並且關聯到一個MediatorLiveData上,實現多數據源的自動整合。

    @MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<S> onChanged) {
        Source<S> e = new Source<>(source, onChanged);
        Source<?> existing = mSources.putIfAbsent(source, e);
        if (existing != null && existing.mObserver != onChanged) {
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if (existing != null) {
            return;
        }
        if (hasActiveObservers()) {
            e.plug();
        }
    }

ViewModel

LiveData和LiveCycle將數據與數據,數據與UI生命綁定到了一起,實現了數據的自動管理和更新,那這些數據如何緩存呢?能否在多個頁面共享這些數據呢?答案是ViewMode。

A ViewModel is always created in association with a scope (an fragment or an activity) and will be retained as long as the scope is alive. E.g. if it is an Activity, until it is finished.

ViewMode相當於一層數據隔離層,將UI層的數據邏輯全部抽離乾淨,管理制底層數據的獲取方式和邏輯。

         ViewModel   viewModel = ViewModelProviders.of(this).get(xxxModel.class);

         ViewModel   viewModel = ViewModelProviders.of(this, factory).get(xxxModel.class);

可以通過以上方式獲取ViewModel實例,如果有自定義ViewModel構造器參數,需要藉助ViewModelProvider.NewInstanceFactory,自己實現create方法。

那麼,ViewMode是怎麼被保存的呢? 可以順着ViewModelProviders源碼進去看看。

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

發現get方法會先從緩存中獲取,沒有的化就會通過Factory的create方法構造一個ViewModel,然後放入緩存,下次直接使用。

Room

Room是一種ORM(對象關係映射)模式數據庫框架,對安卓SQlite的抽象封裝,從此操作數據庫提供了超便捷方式。

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

同樣基於ORM模式封裝的數據庫,比較有名還有GreenDao。而Room和其他ORM對比,具有編譯時驗證查詢語句正常性,支持LiveData數據返回等優勢。
我們選擇room,更多是因爲對LiveData的完美支持,可以動態的將DB數據變化自動更新到LiveData上,在通過LiveData自動刷新到UI上。

這裏引用網絡上的一張Room與其他同類性能對比圖片:

性能對比
Room用法:

    1. 繼承RoomDatabase的抽象類, 暴露抽象方法getxxxDao()。
@Database(entities = {EssayDayEntity.class, ZhihuItemEntity.class}, version = 1)
@TypeConverters(DateConverter.class)
public abstract class AppDB extends RoomDatabase {
    private static AppDB sInstance;

    @VisibleForTesting
    public static final String DATABASE_NAME = "canking.db";
    public abstract EssayDao essayDao();
 }
    1. 獲取db實例
ppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();
    1. 實現Dao層邏輯
@Dao
public interface ZhuhuDao {
    @Query("SELECT * FROM zhuhulist  order by id desc, id limit 0,1")
    LiveData<ZhihuItemEntity> loadZhuhu();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertItem(ZhihuItemEntity products);
}
    1. 添加一張表結構
@Entity
public class User {
    @PrimaryKey
    private int uid;

    @ColumnInfo(name = "first_name")
    private String firstName;

    public String date;//默認columnInfo 爲 date

}

就這麼簡單,就可以實現數據庫的操作,完全隔離的底層複雜的數據庫操作,大大節省項目研發重複勞動力。

從使用說明分析,UserDao和Db一個是接口,一個是抽象類,這些邏輯的實現完全是由annotationProcessor依賴注入幫我們實現的, annotationProcessor其實就是開源的android-apt的官方替代品。
那麼編譯項目後,可以在build目錄下看到生成相應的類xxx_impl.class。
impl

既然Room支持LiveData數據,那麼有可以分析下源碼,瞭解下具體原理,方便以後填坑。

先選Demo中Dao層的insert方法,看看數據如何加載到內存的。我們的query方法如下:

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertItem(ZhihuItemEntity products);

annotationProcessor幫我嗎生成後的實現主要代碼如下:

    private final RoomDatabase __db;
    private final EntityInsertionAdapter __insertionAdapterOfZhihuItemEntity;

    public ZhuhuDao_Impl(RoomDatabase __db) {
        this.__db = __db;
        //EntityInsertionAdapter類的匿名內部類實現方式,
        this.__insertionAdapterOfZhihuItemEntity = new EntityInsertionAdapter<ZhihuItemEntity>(__db) {
            public String createQuery() {
                return "INSERT OR REPLACE INTO `zhuhulist`(`id`,`date`,`stories`,`top_stories`) VALUES (nullif(?, 0),?,?,?)";
            }

            public void bind(SupportSQLiteStatement stmt, ZhihuItemEntity value) {
                //通過SQLiteStatement的bind方法,可以很巧妙的將類對象數據轉化爲數據庫要操作的數據類型。
                stmt.bindLong(1, (long)value.getId());//按順序依次放入SQLiteStatement對象。
                if(value.date == null) {
                    stmt.bindNull(2);
                } else {
                    stmt.bindString(2, value.date);
                }

                //通過DB類注入的自定義轉化器,我們可以將任何對象類型持久化到數據庫中,並且很便捷的從數據庫反序列化出來
                String _tmp = DateConverter.toZhihuStoriesEntity(value.stories);
                if(_tmp == null) {
                    stmt.bindNull(3);
                } else {
                    stmt.bindString(3, _tmp);
                }

                String _tmp_1 = DateConverter.toZhihuStoriesEntity(value.top_stories);
                if(_tmp_1 == null) {
                    stmt.bindNull(4);
                } else {
                    stmt.bindString(4, _tmp_1);
                }

            }
        };
    }


    public void insertItem(ZhihuItemEntity products) {
        this.__db.beginTransaction();

        try {
            //藉助SQLiteStatement類操作數據庫,既優化了數據庫操作性能,又巧妙的bind了對象類型數據。
            this.__insertionAdapterOfZhihuItemEntity.insert(products);
            this.__db.setTransactionSuccessful();
        } finally {
            //這裏很重要,我們平時操作數據庫或流必須要做 finally塊 關閉資源。
            this.__db.endTransaction();
        }
    }

實現類中可以看出insert是通過EntityInsertionAdapter類完成操作的,而EntityInsertionAdapter內部會持有個SupportSQLiteStatement,其實就是SQLiteStatement類的抽象封裝。
其實例獲取是通過RoomData內部方法compileStatement()得到的。

研究下RoomData抽象類源碼:

public abstract class RoomDatabase {
    // set by the generated open helper.
    protected volatile SupportSQLiteDatabase mDatabase;//SQLiteDatabase類的封裝抽象層
    private SupportSQLiteOpenHelper mOpenHelper;//SQLiteOpenHelper類的封裝抽象層
    private final InvalidationTracker mInvalidationTracker;//綁定數據變更監聽器,如在數據變化時通知LiveData

    protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
    protected abstract InvalidationTracker createInvalidationTracker();

    public Cursor query(String query, @Nullable Object[] args) {
        return mOpenHelper.getWritableDatabase().query(new SimpleSQLiteQuery(query, args));
    }

    public Cursor query(SupportSQLiteQuery query) {
        assertNotMainThread();//每次數據庫操作檢查線程
        return mOpenHelper.getWritableDatabase().query(query);
    }


    public SupportSQLiteStatement compileStatement(String sql) {
        assertNotMainThread();
        return mOpenHelper.getWritableDatabase().compileStatement(sql);
    }

    public void beginTransaction() {
        assertNotMainThread();
        mInvalidationTracker.syncTriggers();
        mOpenHelper.getWritableDatabase().beginTransaction();
    }

    public void endTransaction() {
        mOpenHelper.getWritableDatabase().endTransaction();
        if (!inTransaction()) {
            // enqueue refresh only if we are NOT in a transaction. Otherwise, wait for the last
            // endTransaction call to do it.
            mInvalidationTracker.refreshVersionsAsync();
        }
    }

    public static class Builder<T extends RoomDatabase> {
        private MigrationContainer mMigrationContainer;//數據庫升級輔助類

    @NonNull
        public Builder<T> addCallback(@NonNull Callback callback) {
            if (mCallbacks == null) {
                mCallbacks = new ArrayList<>();
            }
            mCallbacks.add(callback);
            return this;
        }

    @NonNull
        public T build() {
            //noinspection ConstantConditions
            if (mContext == null) {
                throw new IllegalArgumentException("Cannot provide null context for the database.");
            }
            //noinspection ConstantConditions
            if (mDatabaseClass == null) {
                throw new IllegalArgumentException("Must provide an abstract class that"
                        + " extends RoomDatabase");
            }
            if (mFactory == null) {
            //默認的SupportSQLiteOpenHelper創建工廠
                mFactory = new FrameworkSQLiteOpenHelperFactory();//SupportSQLiteOpenHelper的實現類,通過mDelegate帶來類操作真正的SQLiteOpenHelper
            }
            DatabaseConfiguration configuration =
                    new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
                            mCallbacks, mAllowMainThreadQueries, mRequireMigration);
            //最終通過反射加載系統幫我們實現的真正RoomData
            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
            db.init(configuration);
            return db;
        }

        public abstract static class Callback {

        public void onCreate(@NonNull SupportSQLiteDatabase db) {
        }

        public void onOpen(@NonNull SupportSQLiteDatabase db) {
        }
    }
    }

DB是通過Build設計模式獲取實例的,在build過程中,可以添加CallBack抽象類回調數據的onCreateonOpen
這裏發現個問題,抽象層封裝那麼深,onUpgrade()方法怎麼回調呢?數據庫的升級怎麼添加自己的邏輯呢?奧祕在MigrationContainer類。

    public static class MigrationContainer {
        private SparseArrayCompat<SparseArrayCompat<Migration>> mMigrations =
                new SparseArrayCompat<>();

        public void addMigrations(Migration... migrations) {
            for (Migration migration : migrations) {
                addMigration(migration);
            }
        }

        private void addMigration(Migration migration) {
            final int start = migration.startVersion;
            final int end = migration.endVersion;
            SparseArrayCompat<Migration> targetMap = mMigrations.get(start);
            if (targetMap == null) {
                targetMap = new SparseArrayCompat<>();
                mMigrations.put(start, targetMap);
            }
            Migration existing = targetMap.get(end);
            if (existing != null) {
                Log.w(Room.LOG_TAG, "Overriding migration " + existing + " with " + migration);
            }
            targetMap.append(end, migration);
        }

        @SuppressWarnings("WeakerAccess")
        @Nullable
        public List<Migration> findMigrationPath(int start, int end) {
            if (start == end) {
                return Collections.emptyList();
            }
            boolean migrateUp = end > start;
            List<Migration> result = new ArrayList<>();
            return findUpMigrationPath(result, migrateUp, start, end);
        }
    }

    public abstract class Migration {
        public final int startVersion;
        public final int endVersion;

        public Migration(int startVersion, int endVersion) {
         this.startVersion = startVersion;
         this.endVersion = endVersion;
        }

        public abstract void migrate(@NonNull SupportSQLiteDatabase database);
    }
}

在Room.databaseBuilder過程中,可以通過addMigration()方法,設置多個或一個Migration。

在RoomOpenHelper的onUpgrade()方法中會依次調用升級範圍內的Migration:

    @Override
    public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        boolean migrated = false;
        if (mConfiguration != null) {
            List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath(
                    oldVersion, newVersion);
            if (migrations != null) {
                for (Migration migration : migrations) {
                    migration.migrate(db);
                }
            }
        }
    }

分析Room到這裏基本原理已瞭解,並且我們可以封裝自己的Callback接口,對業務模塊依次分發onCreate、onUpgrade方法,統一管理數據庫的創建和升級。

Retrofit

當前業界很流行,且很優秀的開源網絡庫,基於OkHttp之前開發。

A type-safe HTTP client for Android and Java

個人理解Retrofit是高度抽象,且和業務耦合度很低的網絡庫,通過各種數據轉化器或適配器,使得網絡返回數據可以很奇妙的直接轉化爲我們想要的類型,與本地數據的緩存及持久化高度無縫對接,大大減少了開發投入。並且使得項目研發更易模塊化和迭代升級。

基本用法可以移步官網學習研究,這裏只分析下如何構造自定義返回類型,默認通用的請求返回如下:

    XXXService service = retrofit.create(XXXService.class);
    Call<List<Repo>> repos = service.listRepos("xxx");
 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

retrofit.create方法內部通過java動態代理,鏈接接口方法,替換轉化範型類型及返回類型。
Retrofit.Builder有兩個重要方法,影響着service.listRepos()方法的返回值類型及反序類型。它們分別是:

    /** Add converter factory for serialization and deserialization of objects. */
    //影響者Call接口中的範型類型
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    /**
     * Add a call adapter factory for supporting service method return types other than {@link
     * Call}.
     * 影響者Call接口的具體實現類型
     */
    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

通過addConverterFactory方法,可以將網絡返回數據直接轉化爲本地的具體實體類型,並且retrofit已經爲我們提供了常見協議數據類型的封裝庫,如下:

Converter 依賴
Gson com.squareup.retrofit2:converter-gson:xxx
Jackson com.squareup.retrofit2:converter-jackson:xxx
Moshi com.squareup.retrofit2:converter-moshi:xxx
Protobuf com.squareup.retrofit2:converter-protobuf:xxx
Wire com.squareup.retrofit2:converter-wire:xxx
Simple XML com.squareup.retrofit2:converter-simplexml:xxx
Scalars com.squareup.retrofit2:converter-scalars:xxx

Builder每添加一個轉化器會保存在List

    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter.Factory factory = converterFactories.get(i);
      Converter<?, RequestBody> converter =
          factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<T, RequestBody>) converter;
      }
    }

當然也可以自定義Converter類型:

public interface Converter<F, T> {
  T convert(F value) throws IOException;

  abstract class Factory {
    // 這裏創建從ResponseBody其它類型的Converter,如果不能處理返回null
    // 主要用於對響應體的處理
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
    Retrofit retrofit) {
      return null;
    }

    // 在這裏創建 從自定類型到ResponseBody 的Converter,不能處理就返回null,
    public Converter<?, RequestBody> requestBodyConverter(Type type,
    Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    //在這裏實現具體轉化邏輯
    }

    // Retrfofit對於上面的幾個註解默認使用的是調用toString方法
    public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
    Retrofit retrofit) {
        //在這裏實現具體轉化邏輯
    }
  }
}

Retrofit通過addCallAdapterFactory方法可以支持返回類型Java8rxjava的處理(也需要添加gradle依賴庫)。

    new Retrofit.Builder()
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
      .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 
      .build();

三. 封裝、整合各框架到項目中去

主要是用LiveData將各框架的數據獲取及頁面更新,按照MVVM思想整合起來, 使得項目結構符合官方給出的架構圖建議,搭建一層邏輯結構,使得更加方便的使用各個組件庫。

architecture

從上到下的邏輯順序,依次構建各個業務層 需要的邏輯控件:

1.編寫需要數據初始化或更新UI的接口方法,並在Observer中更新。

    viewModel.getEssayData().observe(this, new Observer<Resource<ZhihuItemEntity>>() {
            @Override
            public void onChanged(@Nullable Resource<ZhihuItemEntity> essayDayEntityResource) {
            //數據源內數據變動後自動回調該接口,然後更新到UI上
                        updateUI(essayDayEntityResource.data);
            }
        });

2.構建UI層需要的ViewModel

public class EssayViewModel extends AndroidViewModel {
    private EssayRepository mRepository;
    private MediatorLiveData<Resource<ZhihuItemEntity>> mCache;

    public EssayViewModel(Application app) {
        super(app);
        mRepository = new EssayRepository(app);
    }

    public LiveData<Resource<ZhihuItemEntity>> getEssayData() {
        if (mCache == null) {
        //初始化後,從緩存讀取
            mCache = mRepository.loadEssayData();
        }
        return mCache;
    }

    public void updateCache() {
        final LiveData<Resource<ZhihuItemEntity>> update = mRepository.update();
        mCache.addSource(update, new Observer<Resource<ZhihuItemEntity>>() {
            @Override
            public void onChanged(@Nullable Resource<ZhihuItemEntity> zhihuItemEntityResource) {
                mCache.setValue(zhihuItemEntityResource);
            }
        });

    }

    public void addMore(){
        //TODO: 加載更多
    }
}

3.實現Repository類,管理數據獲取渠道。

這裏按照官方知道,寫了個抽象的數據源類,每次先從本地DB取數據,然後獲取網絡數據更新到數據庫,通過LiveData更新到UI層。

public abstract class AbsDataSource<ResultType, RequestType> {
    private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();

    @WorkerThread
    protected abstract void saveCallResult(@NonNull RequestType item);


    @MainThread
    protected abstract boolean shouldFetch(@Nullable ResultType data);

    // Called to get the cached getDate from the database
    @NonNull
    @MainThread
    protected abstract LiveData<ResultType> loadFromDb();

    @NonNull
    @MainThread
    protected abstract LiveData<IRequestApi<RequestType>> createCall();


    @MainThread
    protected abstract void onFetchFailed();


    @MainThread
    public AbsDataSource() {
        final LiveData<ResultType> dbSource = loadFromDb();
        result.setValue(Resource.loading(dbSource.getValue()));

        result.addSource(dbSource, new Observer<ResultType>() {
            @Override
            public void onChanged(@Nullable ResultType resultType) {
                result.removeSource(dbSource);
                if (shouldFetch(resultType)) {
                    fetchFromNetwork(dbSource);
                } else {
                    result.addSource(dbSource, new Observer<ResultType>() {
                        @Override
                        public void onChanged(@Nullable ResultType resultType) {
                            result.setValue(Resource.success(resultType));
                        }
                    });
                }
            }
        });
    }


    private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
        final LiveData<IRequestApi<RequestType>> apiResponse = createCall();

        result.addSource(dbSource, new Observer<ResultType>() {
            @Override
            public void onChanged(@Nullable ResultType resultType) {
                result.setValue(Resource.loading(resultType));
            }
        });

        result.addSource(apiResponse, new Observer<IRequestApi<RequestType>>() {
            @Override
            public void onChanged(@Nullable final IRequestApi<RequestType> requestTypeRequestApi) {
                result.removeSource(apiResponse);
                result.removeSource(dbSource);
                //noinspection ConstantConditions
                if (requestTypeRequestApi.isSuccessful()) {
                    saveResultAndReInit(requestTypeRequestApi);
                } else {
                    onFetchFailed();

                    result.addSource(dbSource, new Observer<ResultType>() {
                        @Override
                        public void onChanged(@Nullable ResultType resultType) {
                            result.setValue(
                                    Resource.error(requestTypeRequestApi.getErrorMsg(), resultType));
                        }
                    });
                }
            }
        });

    }

    @MainThread
    private void saveResultAndReInit(final IRequestApi<RequestType> response) {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... voids) {
                saveCallResult(response.getBody());
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                // we specially request a new live getDate,
                // otherwise we will get immediately last cached value,
                // which may not be updated with latest results received from network.

                result.addSource(loadFromDb(), new Observer<ResultType>() {
                    @Override
                    public void onChanged(@Nullable ResultType resultType) {
                        result.setValue(Resource.success(resultType));
                    }
                });
            }
        }.execute();
    }

    public final MediatorLiveData<Resource<ResultType>> getAsLiveData() {
        return result;
    }
}

4.封裝Room數據庫使用輔助類

這裏二次封裝了數據庫回調接口,便於多個邏輯模塊多數據庫的統一管理使用。

public abstract class AbsDbCallback {
    public abstract void create(SupportSQLiteDatabase db);

    public abstract void open();

    public abstract void upgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion);
}

public class DbCallbackHelper {
    private static ArrayList<AbsDbCallback> mDbCallbacks = new ArrayList<>();

    public static void init() {
        mDbCallbacks.add(new EssayDbCallback());
    }

    public static void dispatchOnCreate(SupportSQLiteDatabase db) {
        for (AbsDbCallback callback : mDbCallbacks) {
        //分發onCreate接口
            callback.create(db);
        }
    }

    private static void dispatchUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        for (AbsDbCallback callback : mDbCallbacks) {
            callback.upgrade(db, oldVersion, newVersion);
        }
    }

    public static Migration[] getUpdateConfig() {
        //每次數據庫升級配置這裏就可以自動分發到各業務模塊的onUpgrade()方法
        return new Migration[]{
                new Migration(1, 2) {

                    @Override
                    public void migrate(@NonNull SupportSQLiteDatabase database) {
                        dispatchUpgrade(database, 1, 2);
                    }
                },
                new Migration(2, 3) {

                    @Override
                    public void migrate(@NonNull SupportSQLiteDatabase database) {
                        dispatchUpgrade(database, 2, 3);
                    }
                }
        };
    }
}

5.對網絡庫數據處理的二次封裝

定義一個範型的數據返回接口,便於抽象業務構造及替換網絡請求方式。

public interface IRequestApi<ResultType> {
    ResultType getBody();
    String getErrorMsg();
    boolean isSuccessful();
}


    @WorkerThread
    public <ResultType> LiveData<IRequestApi<ResultType>> getEssay(@EssayWebService.EssayType String type) throws IOException {
        EssayWebService api = mRetrofit.create(EssayWebService.class);

        Call<ZhihuItemEntity> essayCall = api.getZhihuList("latest");
        MediatorLiveData<IRequestApi<ResultType>> result = new MediatorLiveData<>();
        final Response<ZhihuItemEntity> response = essayCall.execute();

        IRequestApi<ResultType> requestApi = new IRequestApi<ResultType>() {
            @Override
            public ResultType getBody() {
                ZhihuItemEntity entity = response.body();
                return (ResultType) entity;
            }

            @Override
            public String getErrorMsg() {
                return response.message();
            }

            @Override
            public boolean isSuccessful() {
                return response.isSuccessful();
            }
        };
        result.postValue(requestApi);


        return result;
    }

定義一個Resource的類型包裝統一的傳遞數據,便於UI業務的統一處理。

public class Resource<T> {
    public enum Status {
        LOADING, MORE_ADD, SUCCEED, ERROR
    }

    @NonNull
    public final Status status;
    @Nullable
    public final T data;
    @Nullable
    public final String message;

    private Resource(@NonNull Status status, @Nullable T data, @Nullable String message) {
        this.status = status;
        this.data = data;
        this.message = message;
    }

    public static <T> Resource<T> success(@NonNull T data) {
        return new Resource<>(SUCCEED, data, null);
    }

    public static <T> Resource<T> error(String msg, @Nullable T data) {
        return new Resource<>(ERROR, data, msg);
    }

    public static <T> Resource<T> loading(@Nullable T data) {
        return new Resource<>(LOADING, data, null);
    }

    public static <T> Resource<T> moreSucceed(@Nullable T data) {
        return new Resource<>(MORE_ADD, data, null);
    }
}

以上二次封裝Demo源碼已上傳GitHub, 有興趣同學可以學習交流及Star。

四. 總結

回顧官方的這一套框架結構,其中LivaData個人覺得最重要,她將數據與數據、數據與UI鏈接在一起,起到了數據的自動管理,解耦多個業務邏輯,是一種優秀的編程思想。

但是LiveData是否是最適合用到android架構開發中取呢?官方給出了這樣一句話:

Note: If you are already using a library like RxJava or Agera, you can continue using them instead of LiveData. But when you use them or other approaches, make sure you are handling the lifecycle properly such that your data streams pause when the related LifecycleOwner is stopped and the streams are destroyed when the LifecycleOwner is destroyed. You can also add the android.arch.lifecycle:reactivestreams artifact to use LiveData with another reactive streams library (for example, RxJava2).

同樣官方沒有忽略RxJava的優秀,但是由於個人對RxJava的認識只是查看網絡資料瞭解,並未領略到其威力,有興趣同學可以瞭解下。

RxJava就是一種用Java語言實現的響應式編程,來創建基於事件的異步程序

優秀的框架重在學習其獨特的思想,瞭解其基本實現原理,然後轉化爲自己的編程思想。個人覺的這個過程是很緩慢的,只有不斷的感悟不同的優秀框架,慢慢的才能產生質變。


歡迎轉載,請標明出處:常興E站 canking.win

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章