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

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

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

Lifecycle+Retrofit+Room 雲端漫步飛通常的感受

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

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

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

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

DEMO涉及庫

一.原由

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

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

後來想一想,其實差很少就是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<Converter.Factory>*類型列表中去。經過如下代碼轉化爲目標類型。

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

相關文章
相關標籤/搜索