主目錄見(jiàn):Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)
項(xiàng)目目錄:https://github.com/yuzhijun/UnifyStorage
這里先給大家道歉一下具练,最近因?yàn)橐獙W(xué)習(xí)的方向?qū)嵲谑潜容^大,所以文章已經(jīng)好久沒(méi)有更新甜无,如果有什么需要可以留言問(wèn)我扛点,有什么東西很想要了解的也可以交流,文筆生疏了岂丘,見(jiàn)諒陵究。
一.目標(biāo)
寫(xiě)這個(gè)庫(kù)的開(kāi)始是源于一個(gè)小的需求,當(dāng)然奥帘,這個(gè)庫(kù)也是小巧的铜邮。而且做這個(gè)庫(kù)的初衷就是為了能將網(wǎng)絡(luò),數(shù)據(jù)庫(kù)存儲(chǔ)翩概,本地key-value存儲(chǔ)統(tǒng)一起來(lái)牲距,這樣管理和擴(kuò)展都會(huì)更加方便,對(duì)于用戶來(lái)說(shuō)是統(tǒng)一的接口钥庇。
如果不知道怎么使用牍鞠,這個(gè)庫(kù)有詳細(xì)的入門(mén)文檔:https://www.kancloud.cn/sharkchao/unifystorage/864979#65_summinmaxaverage_324,看完文檔你一定知道是怎么操作的评姨,所以我這里就講講怎么實(shí)現(xiàn)的吧难述,其實(shí)也是簡(jiǎn)單的,主要是借助retrofit的方式吐句,結(jié)合realm數(shù)據(jù)庫(kù)和mmkv來(lái)做的胁后。
二.源碼分析
1.基本使用
首先還是跟其他源碼的入手點(diǎn)一樣,我們先來(lái)看看最基本的使用方法嗦枢,依賴這方面文檔里面已經(jīng)很清楚了攀芯,我就直接講使用部分:
public class ApiServiceModule {
private volatile static ApiServiceModule mInstance;
private ApiServiceModule(){
}
public static ApiServiceModule getInstance(){
if (null == mInstance){
synchronized (ApiServiceModule.class){
if (null == mInstance){
mInstance = new ApiServiceModule();
}
}
}
return mInstance;
}
private UStorage provideUStorage(){
return new UStorage.Builder()
.setSchemaVersion(1)
.build();
}
<T> T provideApiService(Class<T> apiDataBase){
return provideUStorage().create(apiDataBase);
}
}
很簡(jiǎn)單,用過(guò)retrofit的人都知道文虏,需要先獲取業(yè)務(wù)接口類侣诺,這里的provideUStorage().create(apiDataBase)
方法就是獲取接口類的方法,那我們先看下接口類的實(shí)現(xiàn)氧秘,這里接口類以一個(gè)查詢?yōu)槔暝В剂信e出來(lái)太多了,不利于查看:
public interface ApiDataBase {
@DB(table = User.class)
@FIND(where = "name = ? and (age > ? or sex = ?)",limit = 10,orderBy = "age desc")
DbResult<User> findUser(String name, int age, String sex);
}
可以看到這里的接口類跟retrofit極其相似丸相,不同的是這里進(jìn)行的是數(shù)據(jù)庫(kù)的存儲(chǔ)搔确,然后我們看最終的調(diào)用使用代碼:
mApiDataBase.findUser("sharkchao", 20, "男")
.registerDbFindCallBack(new DbResult.DbFindCallBack<User>() {
@Override
public void onFirstFindResult(RealmResults<User> realmResults) {
UserData.setmResults(realmResults);
Toast.makeText(MainActivity.this, "成功!"+ realmResults.size(), Toast.LENGTH_SHORT).show();
}
@Override
public void onChange(RealmResults<User> realmResults) {
}
});
這就是查詢的使用方法了,我們以這個(gè)入口點(diǎn)進(jìn)行源碼分析。
2.代碼框架分析
我們看到使用的時(shí)候先要進(jìn)行UStorage的建造,這里使用的是建造者模式膳算,主要是為了構(gòu)造數(shù)據(jù)庫(kù)所需的必要參數(shù)座硕,如代碼所示:
new UStorage.Builder()
.setSchemaVersion(1)
.build();
我們直接跟進(jìn)代碼里面查看,首先查看UStorage
類中的Builder
:
public static final class Builder {
private static final String DEFAULT_DB_NAME = "winningStorage.realm";
private String dbName;
private int schemaVersion = 0;
private BaseMigration migration;
private Realm realmDefault;
public Builder() {
}
public Builder setDbName(String dbName) {
this.dbName = dbName;
return this;
}
public Builder setSchemaVersion(int schemaVersion) {
this.schemaVersion = schemaVersion;
return this;
}
public Builder setMigration(BaseMigration migration) {
this.migration = migration;
return this;
}
public UStorage build() {
configDB();
return new UStorage(this);
}
private void configDB(){
RealmConfiguration.Builder otherConfigBuilder = new RealmConfiguration.Builder()
.name(CommonUtil.isEmptyStr(dbName) ? DEFAULT_DB_NAME : dbName)
.schemaVersion(schemaVersion);
if (null == migration){
otherConfigBuilder.deleteRealmIfMigrationNeeded();
}else {
otherConfigBuilder.migration(migration);
}
RealmConfiguration otherConfig = otherConfigBuilder.build();
Realm.setDefaultConfiguration(otherConfig);
realmDefault = Realm.getDefaultInstance();
}
}
上面的代碼主要是對(duì)realm數(shù)據(jù)庫(kù)的初始化,沒(méi)有什么有難度的代碼畦幢,接著我們看provideUStorage().create(apiDataBase)
中的create()
方法:
//這里的代碼跟retrofit是一摸一樣的
public <T> T create(final Class<T> service) {
CommonUtil.validateServiceInterface(service);
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
new InvocationHandler() {
private final Object[] emptyArgs = new Object[0];
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//這里是主要的解析注解的代碼
result = ServiceMethod.parseAnnotations(this, method);
if (result == null){
throw new IllegalArgumentException("annotation is not exits! please check your code");
}
serviceMethodCache.put(method, result);
}
}
return result;
}
我們看到上面的代碼用的跟retrofit一樣的代理模式坎吻,關(guān)于代理模式的文章可以查看流行框架源碼分析(14)-Proxy代理設(shè)計(jì)模式,我們接著看關(guān)鍵的代碼ServiceMethod.parseAnnotations(this, method)
,這個(gè)代碼調(diào)用了ServiceMethod
類的靜態(tài)方法:
@SuppressWarnings("unchecked")
static <T> ServiceMethod<T> parseAnnotations(UStorage storage, Method method) {
StorageFactory storageFactory = StorageFactory.parseAnnotations(storage, method);
return storageFactory.getServiceMethod();
}
我們看到這里代碼又調(diào)用了StorageFactory.parseAnnotations(storage, method)
方法,這個(gè)方法主要是根據(jù)注解@DB@JSON@GETJSON來(lái)創(chuàng)建不同的ServiceMethod
對(duì)象宇葱,進(jìn)行不同的策略選擇瘦真,這也是策略模式的一個(gè)變種,我們跟進(jìn)StorageFactory
類中的方法:
static StorageFactory parseAnnotations(UStorage storage, Method method) {
return new Builder(storage, method).build();
}
我們看到這個(gè)方法又調(diào)用了Builder
類中的build()
方法:
static final class Builder {
final UStorage storage;
final Method method;
final Annotation[] methodAnnotations;
ServiceMethod<?> serviceMethod;
Builder(UStorage storage, Method method) {
this.storage = storage;
this.method = method;
this.methodAnnotations = method.getAnnotations();
}
StorageFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
return new StorageFactory(this);
}
private void parseMethodAnnotation(Annotation annotation){
if (annotation instanceof DB){
DB db = (DB) annotation;
this.serviceMethod = DBServiceMethod.parseAnnotations(this.storage, this.method, db.table());
}else if (annotation instanceof JSON){
JSON json = (JSON) annotation;
this.serviceMethod = JSONServiceMethod.parseAnnotations(this.storage, this.method, json.key(), json.convert());
}else if (annotation instanceof GETJSON){
GETJSON json = (GETJSON) annotation;
this.serviceMethod = JSONServiceMethod.parseAnnotations(this.storage, this.method, json.key(), json.convert());
}
}
我們看到這里parseMethodAnnotation
方法里面就是判斷是哪個(gè)注解,然后分別初始化serviceMethod這個(gè)全局變量黍瞧,最終會(huì)返回回去這個(gè)變量給外部的create
方法中存進(jìn)map
中诸尽。因?yàn)楂@取到了ServiceMethod
對(duì)象了,我們看create
方法后面干了啥loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
,這里直接就調(diào)用了方法invoke
方法印颤,這個(gè)方法就是具體的每個(gè)ServiceMethod
實(shí)現(xiàn)類中的方法您机,我們還是以查詢?yōu)槔?/p>
private void parseMethodAnnotation(Annotation annotation){
if (annotation instanceof DB){
DB db = (DB) annotation;
this.serviceMethod = DBServiceMethod.parseAnnotations(this.storage, this.method, db.table());
}else if (annotation instanceof JSON){
JSON json = (JSON) annotation;
this.serviceMethod = JSONServiceMethod.parseAnnotations(this.storage, this.method, json.key(), json.convert());
}else if (annotation instanceof GETJSON){
GETJSON json = (GETJSON) annotation;
this.serviceMethod = JSONServiceMethod.parseAnnotations(this.storage, this.method, json.key(), json.convert());
}
}
從上面的代碼入手,我們走DB注解這條路徑年局,調(diào)用DBServiceMethod.parseAnnotations(this.storage, this.method, db.table());
方法:
static <ReturnT> DBServiceMethod<ReturnT> parseAnnotations(
UStorage storage, Method method, Class<? extends RealmObject> table) {
return new DBServiceMethod<>(method, table);
}
這里就是一句簡(jiǎn)單的代碼际看,初始化了DBServiceMethod
對(duì)象,我們跟進(jìn)構(gòu)造函數(shù)看看:
private DBServiceMethod(Method method, Class<? extends RealmObject> table){
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
this.table = table;
if (null != method){
for (Annotation annotation : method.getAnnotations()){
parseHandler(annotation, method.getAnnotations());
}
}
}
我們看到前幾行都是賦值操作矢否,將方法里面的參數(shù)類型仲闽,方法的參數(shù)注解,table注解里面的表名這些基本信息賦值僵朗,然后遍歷方法上面的注解赖欣,調(diào)用parseHandler
方法:
private void parseHandler(Annotation annotation, Annotation[] annotations) {
if (annotation instanceof FIND){
this.storageHandler = FindHandler.parseAnnotations(annotations, this.table);
}else if(annotation instanceof SAVE){
this.storageHandler = SaveHandler.parseAnnotations(annotations, this.table);
}else if(annotation instanceof SAVEORUPDATE){
this.storageHandler = SaveOrUpdateHandler.parseAnnotations(annotations, this.table);
}else if(annotation instanceof UPDATE){
this.storageHandler = UpdateHandler.parseAnnotations(annotations, this.table);
}else if(annotation instanceof DELETE){
this.storageHandler = DeleteHandler.parseAnnotations(annotations, this.table);
}
}
這個(gè)代碼和前面的代碼有點(diǎn)類似,這個(gè)地方也是分別判斷注解是哪個(gè)注解验庙,然后進(jìn)行什么樣的操作顶吮,這里因?yàn)槲覀兪侨〔樵優(yōu)槔晕覀兛吹谝粋€(gè)FindHandler.parseAnnotations(annotations, this.table);
方法:
private FindHandler(Annotation[] annotations, Class<? extends RealmObject> table){
this.table = table;
buildField(annotations);
}
private void buildField(Annotation[] annotations) {
if (null != annotations){
for (Annotation annotation : annotations){
if (annotation instanceof FIND){
FIND find = (FIND) annotation;
this.orderBy = find.orderBy();
this.where = find.where();
this.distinct = find.distinct();
this.limit = find.limit();
this.eager = find.eager();
}
}
}
}
public static HandlerAdapter parseAnnotations(Annotation[] annotations,final Class<? extends RealmObject> table){
return new FindHandler(annotations, table);
}
因?yàn)榇a較為簡(jiǎn)單粪薛,所以我都貼出來(lái)悴了,這里就是取到注解里面的東西,進(jìn)行賦值违寿,最重要的方法還是在FindHandler
類中的invoke
方法让禀,因?yàn)檫@個(gè)就是create
方法里面調(diào)用的invoke
方法:
@SuppressWarnings("unchecked")
@Override
public DbResult invoke(Object[] args, Type[] parameterTypes, Annotation[][] parameterAnnotationsArray) {
dbResult = new DbResult();
try{
RealmQuery<? extends RealmObject> query = UStorage.realm.where(this.table);
RealmQuery<? extends RealmObject> whereFilteredQuery = FindConditionUtil.whereFilter(where, query, args , parameterTypes);
RealmQuery<? extends RealmObject> otherFilteredQuery = FindConditionUtil.otherFilter(whereFilteredQuery, orderBy, limit, distinct);
RealmResults result = otherFilteredQuery.findAllAsync();//這個(gè)地方所有的查詢操作都是用異步的方式
result.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults>() {
@Override
public void onChange(RealmResults realmResults, OrderedCollectionChangeSet changeSet) {
dbResult.setDbFindCallBack(realmResults, changeSet);
}
});
}catch (Exception e){
e.printStackTrace();
}
return dbResult;
}
這里面就是最重要的查詢操作了,首先我們回顧下我們查詢的業(yè)務(wù)接口實(shí)現(xiàn):
@DB(table = User.class)
@FIND(where = "name = ? and (age > ? or sex = ?)",limit = 10,orderBy = "age desc")
DbResult<User> findUser(String name, int age, String sex);
查詢方法里面主要就是解析出來(lái)where條件陨界,主要的解析方法是應(yīng)用了正則表達(dá)式,方法實(shí)現(xiàn)主要在whereFilter
方法痛阻,這個(gè)方法會(huì)再調(diào)用setFilter
方法菌瘪,具體如下:
public static RealmQuery<? extends RealmObject> setFilter(String set, String where, RealmQuery<? extends RealmObject> query, Object[] args, Type[] parameterTypes){
linkCondition.clear();
if (!CommonUtil.isEmptyStr(where)){//判斷where條件是否為空,如果不為空才需要添加條件查詢
Pattern linkPattern = Pattern.compile(AND_OR);
Matcher linkMatcher = linkPattern.matcher(where);
while (linkMatcher.find()){//查找出來(lái)看有多少個(gè)and或者or,存儲(chǔ)到ArrayList中
linkCondition.add(linkMatcher.group());
}
int whereLength;
//說(shuō)明有復(fù)合條件查詢
if (linkCondition.size() > 0){
String[] whereArray = where.split(AND_OR);//將And或者or兩邊的條件分割出來(lái)
whereLength = whereArray.length;
if (CommonUtil.isEmptyStr(set)){
if (args.length != whereArray.length || parameterTypes.length != whereArray.length){
throw new IllegalArgumentException("parameter size is not equal to ?");
}
}
for (int i = 0;i < whereArray.length;i ++){//對(duì)每一個(gè)語(yǔ)句進(jìn)行構(gòu)建查詢
String whereCondition = whereArray[i];
Object parameter = args[i];
Type parameterType = parameterTypes[i];
//構(gòu)造查詢條件
buildWhereCondition(query, whereCondition, parameter, parameterType);
if (linkCondition.size() - 1 >= i){
String condition = linkCondition.get(i);
if ("and".equalsIgnoreCase(condition)){
query.and();
}else {
query.or();
}
}
if (!CommonUtil.isEmptyStr(whereCondition) && whereCondition.contains(")")){
query.endGroup();
}
}
}else {//說(shuō)明是單一條件
buildWhereCondition(query, where, args.length == 0 ? null : args[0],parameterTypes.length == 0 ? null : parameterTypes[0]);
whereLength = 1;
}
buildSetFilter(set, args,whereLength,parameterTypes);
}
return query;
}
這個(gè)方法比較長(zhǎng)俏扩,實(shí)現(xiàn)也比較負(fù)責(zé)糜工,我這里簡(jiǎn)要說(shuō)下思路,這里首先用AND_OR
這個(gè)正則表達(dá)式把where條件拆分開(kāi)來(lái)录淡,這樣多個(gè)and和or中間的部分拆出來(lái)了捌木,然后把這個(gè)中間部分分別用正則表達(dá)式去匹配,主要在buildWhereCondition(query, whereCondition, parameter, parameterType);
方法里面嫉戚,當(dāng)然有時(shí)會(huì)有()
這個(gè)操作為了區(qū)別優(yōu)先級(jí)的操作刨裆,我們這里會(huì)判斷遇到(
或者)
的時(shí)候進(jìn)行添加上query.beginGroup();
或者query.endGroup();
用來(lái)包裝成是一個(gè)模塊的條件。
到這里數(shù)據(jù)庫(kù)查詢的功能代碼講解已經(jīng)說(shuō)完彬檀,代碼算是比較簡(jiǎn)單帆啃,以后我們改變數(shù)據(jù)庫(kù),或者key-value的實(shí)現(xiàn)窍帝,只要在各種的Handler中實(shí)現(xiàn)就可以努潘,這個(gè)庫(kù)實(shí)現(xiàn)簡(jiǎn)單,但是思路還是非常好的坤学,易于擴(kuò)展疯坤,降低了耦合。當(dāng)然代碼還有mock網(wǎng)絡(luò)的方式深浮,這里就不分析了压怠,因?yàn)樯婕暗揭v解retrofit的源碼,這里推薦講解retrofit源碼的地方:流行框架源碼分析(9)-Retrofit2源碼解析看完這篇應(yīng)該就可以看懂工程UnifyStorage里面的unifystorage_mock
子庫(kù)的代碼了略号,當(dāng)然不會(huì)的可以留言問(wèn)我刑峡,或者加我都可以。