流行框架源碼分析(18)-UnifyStorage統(tǒng)一的數(shù)據(jù)庫(kù)存儲(chǔ),key-value存儲(chǔ),mock網(wǎng)絡(luò)數(shù)據(jù)的一個(gè)庫(kù)

主目錄見(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ì)于用戶(hù)來(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)看看最基本的使用方法,依賴(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ù)接口類(lèi),這里的provideUStorage().create(apiDataBase)方法就是獲取接口類(lèi)的方法,那我們先看下接口類(lèi)的實(shí)現(xiàn),這里接口類(lèi)以一個(gè)查詢(xún)?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);
}

可以看到這里的接口類(lèi)跟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) {

                    }
                });

這就是查詢(xún)的使用方法了,我們以這個(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類(lèi)中的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類(lèi)的靜態(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類(lèi)中的方法:

    static StorageFactory parseAnnotations(UStorage storage, Method method) {
        return new Builder(storage, method).build();
    }

我們看到這個(gè)方法又調(diào)用了Builder類(lèi)中的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)類(lèi)中的方法,我們還是以查詢(xú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ù)類(lèi)型,方法的參數(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)類(lèi)似,這個(gè)地方也是分別判斷注解是哪個(gè)注解,然后進(jìn)行什么樣的操作,這里因?yàn)槲覀兪侨〔樵?xú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類(lèi)中的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è)地方所有的查詢(xún)操作都是用異步的方式
            result.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults>() {
                @Override
                public void onChange(RealmResults realmResults, OrderedCollectionChangeSet changeSet) {
                    dbResult.setDbFindCallBack(realmResults, changeSet);
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }

        return dbResult;
    }

這里面就是最重要的查詢(xún)操作了,首先我們回顧下我們查詢(xún)的業(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);

查詢(xún)方法里面主要就是解析出來(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條件是否為空,如果不為空才需要添加條件查詢(xún)
            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ù)合條件查詢(xún)
            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)建查詢(xún)
                    String whereCondition = whereArray[i];
                    Object parameter = args[i];
                    Type parameterType = parameterTypes[i];
                    //構(gòu)造查詢(xún)條件
                    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ù)查詢(xún)的功能代碼講解已經(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)我,或者加我都可以。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容