_MutableLiveData_类很简单,只是暴露了两个方法:postData()和setData()。 _MediatorLiveData_类有个**addSource()**方法,可以实现监听另一个或多个LiveData数据源变化,如许我们就可以比较便捷且低耦合的实现多个数据源的逻辑,并且关联到一个MediatorLiveData上,实现多数据源的自动整合。
@MainThread
public void addSource(@NonNull LiveData source, @NonNull Observer onChanged) {
Source 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 get(@NonNull String key, @NonNull Class 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用法:
继承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();
实现Dao层逻辑
@Dao
public interface ZhuhuDao {
@Query(“SELECT * FROM zhuhulist order by id desc, id limit 0,1”)
LiveData loadZhuhu();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertItem(ZhihuItemEntity products);
}
添加一张表结构
@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。
既然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(__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 {
private MigrationContainer mMigrationContainer;//数据库升级辅助类
@NonNull
public Builder 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抽象类回调数据的_onCreate_和_onOpen_。 这里发现个问题,抽象层封装那么深,*onUpgrade()*方法怎么回调呢?数据库的升级怎么添加自己的逻辑呢?奥秘在MigrationContainer类。
public static class MigrationContainer {
private SparseArrayCompat<SparseArrayCompat> 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 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 findMigrationPath(int start, int end) {
if (start == end) {
return Collections.emptyList();
}
boolean migrateUp = end > start;
List 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 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> repos = service.listRepos(“xxx”);
public T create(final Class 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 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
viewModel.getEssayData().observe(this, new Observer<Resource>() {
@Override
public void onChanged(@Nullable Resource essayDayEntityResource) {
//数据源内数据变动后自动回调该接口,然后更新到UI上
updateUI(essayDayEntityResource.data);
}
});
2.构建UI层需要的ViewModel
public class EssayViewModel extends AndroidViewModel {
private EssayRepository mRepository;
private MediatorLiveData<Resource> mCache;
public EssayViewModel(Application app) {
super(app);
mRepository = new EssayRepository(app);
}
public LiveData<Resource> getEssayData() {
if (mCache == null) {
//初始化后,从缓存读取
mCache = mRepository.loadEssayData();
}
return mCache;
}
public void updateCache() {
final LiveData<Resource> update = mRepository.update();
mCache.addSource(update, new Observer<Resource>() {
@Override
public void onChanged(@Nullable Resource zhihuItemEntityResource) {
mCache.setValue(zhihuItemEntityResource);
}
});
}
public void addMore(){
//TODO: 加载更多
}
}
3.实现Repository类,管理数据获取渠道。
这里按照官方知道,写了个抽象的数据源类,每次先从本地DB取数据,然后获取网络数据更新到数据库,通过LiveData更新到UI层。
public abstract class AbsDataSource<ResultType, RequestType> {
private final MediatorLiveData<Resource> 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 loadFromDb();
@NonNull
@MainThread
protected abstract LiveData<IRequestApi> createCall();
@MainThread
protected abstract void onFetchFailed();
@MainThread
public AbsDataSource() {
final LiveData dbSource = loadFromDb();
result.setValue(Resource.loading(dbSource.getValue()));
result.addSource(dbSource, new Observer() {
@Override
public void onChanged(@Nullable ResultType resultType) {
result.removeSource(dbSource);
if (shouldFetch(resultType)) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource, new Observer() {
@Override
public void onChanged(@Nullable ResultType resultType) {
result.setValue(Resource.success(resultType));
}
});
}
}
});
}
private void fetchFromNetwork(final LiveData dbSource) {
final LiveData<IRequestApi> apiResponse = createCall();
result.addSource(dbSource, new Observer() {
@Override
public void onChanged(@Nullable ResultType resultType) {
result.setValue(Resource.loading(resultType));
}
});
result.addSource(apiResponse, new Observer<IRequestApi>() {
@Override
public void onChanged(@Nullable final IRequestApi requestTypeRequestApi) {
result.removeSource(apiResponse);
result.removeSource(dbSource);
//noinspection ConstantConditions
if (requestTypeRequestApi.isSuccessful()) {
saveResultAndReInit(requestTypeRequestApi);
} else {
onFetchFailed();
result.addSource(dbSource, new Observer() {
@Override
public void onChanged(@Nullable ResultType resultType) {
result.setValue(
Resource.error(requestTypeRequestApi.getErrorMsg(), resultType));
}
});
}
}
});
}
@MainThread
private void saveResultAndReInit(final IRequestApi 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() {
@Override
public void onChanged(@Nullable ResultType resultType) {
result.setValue(Resource.success(resultType));
}
});
}
}.execute();
}
public final MediatorLiveData<Resource> 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 mDbCallbacks = new ArrayList<>();
public static void init() {
mDbCallbacks.add(new EssayDbCallback());
总结:
《Android学习条记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
一管理使用。
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 mDbCallbacks = new ArrayList<>();
public static void init() {
mDbCallbacks.add(new EssayDbCallback());
总结: