安卓開發(fā)技術(shù)發(fā)展到現(xiàn)在已經(jīng)非常成熟,有很多的技術(shù)專項如插件盔沫,熱修医咨,加固,瘦身架诞,性能優(yōu)化拟淮,自動化測試等已經(jīng)在業(yè)界有了完善的或者開源的解決方案。
作為一枚多年的安卓研發(fā)谴忧,有必要學(xué)習(xí)或了解下這些優(yōu)秀的解決方案很泊,領(lǐng)略那些行業(yè)開創(chuàng)者的思想魅力角虫,然后轉(zhuǎn)化為自己的技術(shù)技能,爭取應(yīng)用到日常的開發(fā)中去撑蚌,提高自己研發(fā)水平上遥。
Lifecycle+Retrofit+Room 云端漫步飛一般的感覺
安卓項目的開發(fā)結(jié)構(gòu)搏屑,有原來最初的mvc争涌,到后來有人提出的mvp,然后到mvvm的發(fā)展辣恋,無非就是依著六大設(shè)計原則的不斷解耦亮垫,不斷演變,使得項目的開發(fā)高度組件化伟骨,滿足日常復(fù)雜多變的項目需求饮潦。
- 依賴倒置原則-Dependency Inversion Principle (DIP)
- 里氏替換原則-Liskov Substitution Principle (LSP)
- 接口分隔原則-Interface Segregation Principle (ISP)
- 單一職責(zé)原則-Single Responsibility Principle (SRP)
- 開閉原則-The Open-Closed Principle (OCP)
- 迪米特法則-Law of Demeter (LOD)
目前針對MVVM框架結(jié)構(gòu),安卓官方也給出了穩(wěn)定的架構(gòu)版本1.0携狭。
本文也是圍繞者官方思想继蜡,試著從源碼角度總結(jié)學(xué)習(xí)經(jīng)驗,最后將這些控件二次封裝逛腿,更加便于理解使用稀并。其也會涉及到其他優(yōu)秀的的庫,如Gson,Glide,BaseRecyclerViewAdapterHelper等
一.起因
去年還在手機(jī)衛(wèi)士團(tuán)隊做垃圾清理模塊時候单默,趕上模塊化二次代碼重構(gòu)技術(shù)需求碘举,項目分為多個進(jìn)程,其中后臺進(jìn)程負(fù)責(zé)從DB中獲取數(shù)據(jù)(DB可云端更新升級)搁廓,然后結(jié)合云端配置信息做相關(guān)邏輯操作引颈,將數(shù)據(jù)回傳到UI進(jìn)程,UI進(jìn)程中在后臺線程中二次封裝境蜕,最后post到主UI線程Update到界面展示給用戶蝙场。
當(dāng)時就和團(tuán)隊的同學(xué)溝通交流,面對這種數(shù)據(jù)多層次復(fù)雜的處理邏輯粱年,設(shè)想可以做一種機(jī)制售滤,將UI層的更新綁定到一個數(shù)據(jù)源,數(shù)據(jù)源數(shù)據(jù)的更新可以自動觸發(fā)UI更新逼泣,實現(xiàn)與UI的解耦趴泌。
數(shù)據(jù)源控制著數(shù)據(jù)的來源,每種來源有著獨立的邏輯分層拉庶,共享底層一些公共lib庫嗜憔。
后來想想,其實差不多就是MVVM思想氏仗,直到谷歌官方宣布Android 架構(gòu)組件 1.0 穩(wěn)定版的發(fā)布吉捶,才下決心學(xué)習(xí)下官方這一套思想夺鲜,感受優(yōu)秀的架構(gòu)。
引用官方一張結(jié)構(gòu)圖如下:
二.各組件庫原理及基本用法
這里主要探究下主要組件庫的基本用法和原理呐舔,以理解其優(yōu)秀思想為主币励。
谷歌官方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
一個安卓組件生命周期感知回調(diào)的控件,可以感知activity或fragment的生命周期變化珊拼,并回調(diào)相應(yīng)接口食呻。
可以先從源碼抽象類分析,如下:
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抽象類有三個方法澎现,可以添加仅胞、刪除觀察者,并且可以獲取當(dāng)前組件的生命周期的狀態(tài)。
而LifecycleObserver接口只是個空接口做類型校驗剑辫,具體事件交給了OnLifecycleEvent干旧,通過注入回調(diào)相應(yīng)事件。
二在最新的support V4包中妹蔽,Activity和Fragment都實現(xiàn)了LifecycleOwner接口椎眯,意味著我們可以直接使用getLifecyle()獲取當(dāng)前Activity或Fragment的Lifecycle對象,很方便的添加我們的監(jiān)聽方法胳岂。
@SuppressWarnings({"WeakerAccess", "unused"})
public interface LifecycleOwner {
@NonNull
Lifecycle getLifecycle();
}
LiveData
LiveData是一個持有范型類型的數(shù)據(jù)組件编整,將App生命組件與數(shù)據(jù)關(guān)聯(lián)到一起,可以感知生命變更旦万,規(guī)則回調(diào)數(shù)據(jù)變化闹击。
public abstract class LiveData<T> {
private volatile Object mData = NOT_SET;//數(shù)據(jù)容器類
//一個寫死的處于Resume狀態(tài)的LifecycleOwner,用于數(shù)據(jù)回調(diào)無限制情況
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);//最終的回調(diào)方法地方
}
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());//可以添加做個監(jiān)聽者成艘,這里遍歷分發(fā)數(shù)據(jù)到每個監(jiān)聽者
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);
}
//實現(xiàn)類回調(diào)方法
protected void onActive() {
}
//實現(xiàn)類回調(diào)方法
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) {//只有生命組件處于前臺時赏半,才觸發(fā)數(shù)據(jù)的變更通知
dispatchingValue(this);
}
}
}
static boolean isActiveState(State state) {
return state.isAtLeast(STARTED);
}
}
看源碼,會發(fā)現(xiàn)LiveData有個重要的方法observe(LifecycleOwner owner, Observer<T> observer), 在數(shù)據(jù)源數(shù)據(jù)有變更時淆两,遍歷分發(fā)數(shù)據(jù)到所有監(jiān)聽者断箫,最后會回調(diào)onChanged()方法。
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(@Nullable T t);
}
LiveData有兩個實現(xiàn)類:MediatorLiveData和MediatorLiveData秋冰,繼承關(guān)系如下:
MutableLiveData類很簡單仲义,只是暴露了兩個方法:postData()和setData()。
MediatorLiveData類有個addSource()方法剑勾,可以實現(xiàn)監(jiān)聽另一個或多個LiveData數(shù)據(jù)源變化埃撵,這樣我們就可以比較便捷且低耦合的實現(xiàn)多個數(shù)據(jù)源的邏輯,并且關(guān)聯(lián)到一個MediatorLiveData上虽另,實現(xiàn)多數(shù)據(jù)源的自動整合暂刘。
@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將數(shù)據(jù)與數(shù)據(jù),數(shù)據(jù)與UI生命綁定到了一起捂刺,實現(xiàn)了數(shù)據(jù)的自動管理和更新谣拣,那這些數(shù)據(jù)如何緩存呢募寨?能否在多個頁面共享這些數(shù)據(jù)呢?答案是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相當(dāng)于一層數(shù)據(jù)隔離層拔鹰,將UI層的數(shù)據(jù)邏輯全部抽離干凈,管理制底層數(shù)據(jù)的獲取方式和邏輯贵涵。
ViewModel viewModel = ViewModelProviders.of(this).get(xxxModel.class);
ViewModel viewModel = ViewModelProviders.of(this, factory).get(xxxModel.class);
可以通過以上方式獲取ViewModel實例列肢,如果有自定義ViewModel構(gòu)造器參數(shù),需要借助ViewModelProvider.NewInstanceFactory独悴,自己實現(xiàn)create方法例书。
那么锣尉,ViewMode是怎么被保存的呢刻炒? 可以順著ViewModelProviders源碼進(jìn)去看看。
@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;
}
發(fā)現(xiàn)get方法會先從緩存中獲取自沧,沒有的化就會通過Factory的create方法構(gòu)造一個ViewModel坟奥,然后放入緩存,下次直接使用拇厢。
Room
Room是一種ORM(對象關(guān)系映射)模式數(shù)據(jù)庫框架爱谁,對安卓SQlite的抽象封裝,從此操作數(shù)據(jù)庫提供了超便捷方式孝偎。
The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
同樣基于ORM模式封裝的數(shù)據(jù)庫访敌,比較有名還有GreenDao。而Room和其他ORM對比衣盾,具有編譯時驗證查詢語句正常性寺旺,支持LiveData數(shù)據(jù)返回等優(yōu)勢。
我們選擇room势决,更多是因為對LiveData的完美支持阻塑,可以動態(tài)的將DB數(shù)據(jù)變化自動更新到LiveData上,在通過LiveData自動刷新到UI上果复。
這里引用網(wǎng)絡(luò)上的一張Room與其他同類性能對比圖片:
Room用法:
- 繼承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();
}
- 獲取db實例
ppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
- 實現(xiàn)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);
}
- 添加一張表結(jié)構(gòu)
@Entity
public class User {
@PrimaryKey
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
public String date;//默認(rèn)columnInfo 為 date
}
就這么簡單,就可以實現(xiàn)數(shù)據(jù)庫的操作虽抄,完全隔離的底層復(fù)雜的數(shù)據(jù)庫操作走搁,大大節(jié)省項目研發(fā)重復(fù)勞動力。
從使用說明分析迈窟,UserDao和Db一個是接口私植,一個是抽象類,這些邏輯的實現(xiàn)完全是由annotationProcessor依賴注入幫我們實現(xiàn)的, annotationProcessor其實就是開源的android-apt的官方替代品菠隆。
那么編譯項目后兵琳,可以在build目錄下看到生成相應(yīng)的類xxx_impl.class狂秘。
既然Room支持LiveData數(shù)據(jù),那么有可以分析下源碼,了解下具體原理躯肌,方便以后填坑者春。
先選Demo中Dao層的insert方法,看看數(shù)據(jù)如何加載到內(nèi)存的清女。我們的query方法如下:
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertItem(ZhihuItemEntity products);
annotationProcessor幫我嗎生成后的實現(xiàn)主要代碼如下:
private final RoomDatabase __db;
private final EntityInsertionAdapter __insertionAdapterOfZhihuItemEntity;
public ZhuhuDao_Impl(RoomDatabase __db) {
this.__db = __db;
//EntityInsertionAdapter類的匿名內(nèi)部類實現(xiàn)方式钱烟,
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方法,可以很巧妙的將類對象數(shù)據(jù)轉(zhuǎn)化為數(shù)據(jù)庫要操作的數(shù)據(jù)類型嫡丙。
stmt.bindLong(1, (long)value.getId());//按順序依次放入SQLiteStatement對象拴袭。
if(value.date == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, value.date);
}
//通過DB類注入的自定義轉(zhuǎn)化器,我們可以將任何對象類型持久化到數(shù)據(jù)庫中曙博,并且很便捷的從數(shù)據(jù)庫反序列化出來
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類操作數(shù)據(jù)庫拥刻,既優(yōu)化了數(shù)據(jù)庫操作性能,又巧妙的bind了對象類型數(shù)據(jù)父泳。
this.__insertionAdapterOfZhihuItemEntity.insert(products);
this.__db.setTransactionSuccessful();
} finally {
//這里很重要般哼,我們平時操作數(shù)據(jù)庫或流必須要做 finally塊 關(guān)閉資源。
this.__db.endTransaction();
}
}
實現(xiàn)類中可以看出insert是通過EntityInsertionAdapter類完成操作的惠窄,而EntityInsertionAdapter內(nèi)部會持有個SupportSQLiteStatement蒸眠,其實就是SQLiteStatement類的抽象封裝。
其實例獲取是通過RoomData內(nèi)部方法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;//綁定數(shù)據(jù)變更監(jiān)聽器楞卡,如在數(shù)據(jù)變化時通知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();//每次數(shù)據(jù)庫操作檢查線程
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;//數(shù)據(jù)庫升級輔助類
@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) {
//默認(rèn)的SupportSQLiteOpenHelper創(chuàng)建工廠
mFactory = new FrameworkSQLiteOpenHelperFactory();//SupportSQLiteOpenHelper的實現(xiàn)類,通過mDelegate帶來類操作真正的SQLiteOpenHelper
}
DatabaseConfiguration configuration =
new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
mCallbacks, mAllowMainThreadQueries, mRequireMigration);
//最終通過反射加載系統(tǒng)幫我們實現(xiàn)的真正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設(shè)計模式獲取實例的脾歇,在build過程中蒋腮,可以添加CallBack抽象類回調(diào)數(shù)據(jù)的onCreate和onOpen。
這里發(fā)現(xiàn)個問題介劫,抽象層封裝那么深徽惋,onUpgrade()方法怎么回調(diào)呢?數(shù)據(jù)庫的升級怎么添加自己的邏輯呢座韵?奧秘在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()方法誉碴,設(shè)置多個或一個Migration宦棺。
在RoomOpenHelper的onUpgrade()方法中會依次調(diào)用升級范圍內(nèi)的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接口黔帕,對業(yè)務(wù)模塊依次分發(fā)onCreate代咸、onUpgrade方法,統(tǒng)一管理數(shù)據(jù)庫的創(chuàng)建和升級成黄。
Retrofit
當(dāng)前業(yè)界很流行呐芥,且很優(yōu)秀的開源網(wǎng)絡(luò)庫逻杖,基于OkHttp之前開發(fā)。
A type-safe HTTP client for Android and Java
個人理解Retrofit是高度抽象思瘟,且和業(yè)務(wù)耦合度很低的網(wǎng)絡(luò)庫荸百,通過各種數(shù)據(jù)轉(zhuǎn)化器或適配器,使得網(wǎng)絡(luò)返回數(shù)據(jù)可以很奇妙的直接轉(zhuǎn)化為我們想要的類型滨攻,與本地數(shù)據(jù)的緩存及持久化高度無縫對接够话,大大減少了開發(fā)投入。并且使得項目研發(fā)更易模塊化和迭代升級光绕。
基本用法可以移步官網(wǎng)學(xué)習(xí)研究女嘲,這里只分析下如何構(gòu)造自定義返回類型,默認(rèn)通用的請求返回如下:
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方法內(nèi)部通過java動態(tài)代理诞帐,鏈接接口方法欣尼,替換轉(zhuǎn)化范型類型及返回類型。
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接口的具體實現(xiàn)類型
*/
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
通過addConverterFactory方法,可以將網(wǎng)絡(luò)返回數(shù)據(jù)直接轉(zhuǎn)化為本地的具體實體類型谷徙,并且retrofit已經(jīng)為我們提供了常見協(xié)議數(shù)據(jù)類型的封裝庫,如下:
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每添加一個轉(zhuǎn)化器會保存在List<Converter.Factory>類型列表中去驯绎。通過以下代碼轉(zhuǎn)化為目標(biāo)類型完慧。
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;
}
}
當(dāng)然也可以自定義Converter類型:
public interface Converter<F, T> {
T convert(F value) throws IOException;
abstract class Factory {
// 這里創(chuàng)建從ResponseBody其它類型的Converter,如果不能處理返回null
// 主要用于對響應(yīng)體的處理
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
// 在這里創(chuàng)建 從自定類型到ResponseBody 的Converter,不能處理就返回null剩失,
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
//在這里實現(xiàn)具體轉(zhuǎn)化邏輯
}
// Retrfofit對于上面的幾個注解默認(rèn)使用的是調(diào)用toString方法
public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
//在這里實現(xiàn)具體轉(zhuǎn)化邏輯
}
}
}
Retrofit通過addCallAdapterFactory方法可以支持返回類型Java8或rxjava的處理(也需要添加gradle依賴庫)屈尼。
new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
三. 封裝、整合各框架到項目中去
主要是用LiveData將各框架的數(shù)據(jù)獲取及頁面更新拴孤,按照MVVM思想整合起來, 使得項目結(jié)構(gòu)符合官方給出的架構(gòu)圖建議脾歧,搭建一層邏輯結(jié)構(gòu),使得更加方便的使用各個組件庫演熟。
從上到下的邏輯順序鞭执,依次構(gòu)建各個業(yè)務(wù)層 需要的邏輯控件:
1.編寫需要數(shù)據(jù)初始化或更新UI的接口方法,并在Observer中更新芒粹。
viewModel.getEssayData().observe(this, new Observer<Resource<ZhihuItemEntity>>() {
@Override
public void onChanged(@Nullable Resource<ZhihuItemEntity> essayDayEntityResource) {
//數(shù)據(jù)源內(nèi)數(shù)據(jù)變動后自動回調(diào)該接口兄纺,然后更新到UI上
updateUI(essayDayEntityResource.data);
}
});
2.構(gòu)建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.實現(xiàn)Repository類化漆,管理數(shù)據(jù)獲取渠道估脆。
這里按照官方知道,寫了個抽象的數(shù)據(jù)源類座云,每次先從本地DB取數(shù)據(jù)疙赠,然后獲取網(wǎng)絡(luò)數(shù)據(jù)更新到數(shù)據(jù)庫付材,通過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數(shù)據(jù)庫使用輔助類
這里二次封裝了數(shù)據(jù)庫回調(diào)接口圃阳,便于多個邏輯模塊多數(shù)據(jù)庫的統(tǒng)一管理使用伞租。
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) {
//分發(fā)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() {
//每次數(shù)據(jù)庫升級配置這里就可以自動分發(fā)到各業(yè)務(wù)模塊的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.對網(wǎng)絡(luò)庫數(shù)據(jù)處理的二次封裝
定義一個范型的數(shù)據(jù)返回接口,便于抽象業(yè)務(wù)構(gòu)造及替換網(wǎng)絡(luò)請求方式限佩。
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<T>的類型包裝統(tǒng)一的傳遞數(shù)據(jù)葵诈,便于UI業(yè)務(wù)的統(tǒng)一處理。
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, 有興趣同學(xué)可以學(xué)習(xí)交流及Star祟同。
四. 總結(jié)
回顧官方的這一套框架結(jié)構(gòu)作喘,其中LivaData個人覺得最重要,她將數(shù)據(jù)與數(shù)據(jù)晕城、數(shù)據(jù)與UI鏈接在一起泞坦,起到了數(shù)據(jù)的自動管理,解耦多個業(yè)務(wù)邏輯砖顷,是一種優(yōu)秀的編程思想贰锁。
但是LiveData是否是最適合用到android架構(gòu)開發(fā)中取呢?官方給出了這樣一句話:
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).
同樣官方?jīng)]有忽略RxJava的優(yōu)秀滤蝠,但是由于個人對RxJava的認(rèn)識只是查看網(wǎng)絡(luò)資料了解豌熄,并未領(lǐng)略到其威力,有興趣同學(xué)可以了解下物咳。
RxJava就是一種用Java語言實現(xiàn)的響應(yīng)式編程锣险,來創(chuàng)建基于事件的異步程序
優(yōu)秀的框架重在學(xué)習(xí)其獨特的思想,了解其基本實現(xiàn)原理览闰,然后轉(zhuǎn)化為自己的編程思想芯肤。個人覺的這個過程是很緩慢的,只有不斷的感悟不同的優(yōu)秀框架压鉴,慢慢的才能產(chǎn)生質(zhì)變崖咨。
歡迎轉(zhuǎn)載,請標(biāo)明出處:常興E站 canking.win