国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ARouter路由解析

stormgens / 3649人閱讀

摘要:跳轉完了找到了被攔截了找不到了攔截器在模塊的時候講述的使用,如果本次路由跳轉不是走的綠色通道那么則會觸發攔截器進行過濾。部分代碼省略攔截器的初始化在剛開始初始化的時候,就已經做了這個操作。

目錄介紹

01.原生跳轉實現

02.實現組件跳轉方式

2.1 傳統跳轉方式

2.2 為何需要路由

03.ARouter配置與優勢

04.跨進程組件通信

4.1 URLScheme

4.2 AIDL

4.3 BroadcastReceiver

4.4 路由通信注意要點

05.ARouter的結構

06.ARouter的工作流程

6.1 初始化流程

6.2 跳轉頁面流程

07.ARouter簡單調用api

7.1 最簡單調用

7.2 build源碼分析

7.3 navigation分析

08.Postcard信息攜帶

09.LogisticsCenter

10.DegradeService降級容錯服務

11.Interceptor攔截器

12.數據傳輸和自動注入

13.多dex的支持

14.InstantRun支持

15.生成的編譯代碼

好消息

博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請注明出處,謝謝!

鏈接地址:https://github.com/yangchong2...

如果覺得好,可以star一下,謝謝!當然也歡迎提出建議或者問題,萬事起于忽微,量變引起質變!

注解學習小案例

注解學習小案例,比較系統性學習注解并且應用實踐。簡單應用了運行期注解,通過注解實現了setContentView功能;簡單應用了編譯器注解,通過注解實現了防暴力點擊的功能,同時支持設置時間間隔;使用注解替代枚舉;使用注解一步步搭建簡單路由案例。結合相應的博客,在來一些小案例,從此應該對注解有更加深入的理解……

開源項目地址:https://github.com/yangchong2...

01.原生跳轉實現

Google提供的原聲路由主要是通過intent,可以分成顯示和隱式兩種。顯示的方案會導致類之間的直接依賴問題,耦合嚴重;隱式intent需要的配置清單中統一聲明,首先有個暴露的問題,另外在多模塊開發中協作也比較困難。只要調用startActivity后面的環節我們就無法控制了,在出現錯誤時無能為力。

02.實現組件跳轉方式 2.1 傳統跳轉方式

第一種,通過intent跳轉

第二種,通過aidl跳轉

第三種,通過scheme協議跳轉

2.2 為何需要路由

顯示Intent:項目龐大以后,類依賴耦合太大,不適合組件化拆分

隱式Intent:協作困難,調用時候不知道調什么參數

每個注冊了Scheme的Activity都可以直接打開,有安全風險

AndroidMainfest集中式管理比較臃腫

無法動態修改路由,如果頁面出錯,無法動態降級

無法動態攔截跳轉,譬如未登錄的情況下,打開登錄頁面,登錄成功后接著打開剛才想打開的頁面

H5、Android、iOS地址不一樣,不利于統一跳轉

03.ARouter配置與優勢 3.1 ARouter的優勢

如下所示

直接解析URL路由,解析參數并賦值

支持多模塊項目

支持InstantRun

允許自定義攔截器

ARouter可以提供IoC容器

映射關系自動注冊

靈活的降級策略

3.2 至于配置和使用

直接看https://www.jianshu.com/p/fed...

04.跨進程組件通信 4.1 URLScheme【例如:ActivityRouter、ARouter等】

優勢有:

基因中自帶支持從webview中調用

不用互相注冊(不用知道需要調用的app的進程名稱等信息)

劣勢有:

只能單向地給組件發送信息,適用于啟動Activity和發送指令,不適用于獲取數據(例如:獲取用戶組件的當前用戶登錄信息)

需要有個額外的中轉Activity來統一處理URLScheme

如果設備上安裝了多個使用相同URLScheme的app,會彈出選擇框(多個組件作為app同時安裝到設備上時會出現這個問題)

無法進行權限設置,無法進行開關設置,存在安全性風險

4.2 AIDL

優勢有:

可以傳遞Parcelable類型的對象

效率高

可以設置跨app調用的開關

劣勢有:

調用組件之前需要提前知道該組件在那個進程,否則無法建立ServiceConnection

組件在作為獨立app和作為lib打包到主app時,進程名稱不同,維護成本高

4.3 BroadcastReceiver

BroadcastReceiver + Service + LocalSocket。該方案是參考cc路由框架!

跨組件間通信實現的同時,應該滿足以下條件:

每個app都能給其它app調用

app可以設置是否對外提供跨進程組件調用的支持

組件調用的請求發出去之后,能自動探測當前設備上是否有支持此次調用的app

支持超時、取消

4.4 路由通信注意要點 05.ARouter的結構

ARouter主要由三部分組成,包括對外提供的api調用模塊、注解模塊以及編譯時通過注解生產相關的類模塊。

arouter-annotation注解的聲明和信息存儲類的模塊

arouter-compiler編譯期解析注解信息并生成相應類以便進行注入的模塊

arouter-api核心調用Api功能的模塊

annotation模塊

Route、Interceptor、Autowired都是在開發是需要的注解。

compiler模塊

AutoWiredProcessor、InterceptorProcessor、RouteProcessor分別為annotation模塊對應的Autowired、Interceptor、Route在項目編譯時產生相關的類文件。

api模塊

主要是ARouter具體實現和對外暴露使用的api。

06.ARouter的工作流程 6.1 初始化流程

初始化代碼如下所示

/**

 */
public static void init(Application application) {
    //如果沒有初始化,則
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        
        //做初始化工作
        hasInit = _ARouter.init(application);

        if (hasInit) {
            _ARouter.afterInit();
        }

        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
    }
}
```

之后接著看_ARouter.init(application)這行代碼,點擊去查看

protected static synchronized boolean init(Application application) {
    //賦值上下文
    mContext = application;
    //初始化LogisticsCenter
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;
    mHandler = new Handler(Looper.getMainLooper());
    return true;
}

接下來看看LogisticsCenter里面做了什么

public class LogisticsCenter {
    /**

     */
    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;
        try {
            long startInit = System.currentTimeMillis();
            Set routerMap;
            //debug或者版本更新的時候每次都重新加載router信息
            // It will rebuild router map every times when debuggable.
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                // These class was generate by arouter-compiler.
                //加載alibaba.android.arouter.routes包下載的類
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                if (!routerMap.isEmpty()) {
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                }
                PackageUtils.updateVersion(context);    // Save new version name when router map update finish.
            } else {
                logger.info(TAG, "Load router map from cache.");
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet()));
            }

            logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
            startInit = System.currentTimeMillis();

            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // This one of root elements, load root. 
                    //導入ARouter$$Root$$app.java,初始化Warehouse.groupsIndex集合
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // Load interceptorMeta
                    //導入ARouter$$Interceptors$$app.java,初始化Warehouse.interceptorsIndex集合
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // Load providerIndex
                    //導入ARouter$$Providers$$app.java,初始化Warehouse.providersIndex集合
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }

            /*******部分代碼省略********/
        } catch (Exception e) {
            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
        }
    }
}
```

綜上所述,整個初始化的流程大概就是:

初始化運行時的上下文環境

初始化日志logger

尋找router相關的類

解析并且緩存路由相關信息

初始化攔截服務

6.2 跳轉頁面流程

07.ARouter調用api 7.1 最簡單調用

最簡單的調用方式

ARouter.getInstance()
                .build("/user/UserFragment")
                .navigation();

7.2 build源碼分析

這個主要是添加跳轉的路徑

public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}

然后把這個路徑添加到默認的組中

/**

 */
protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        if (null != pService) {
            path = pService.forString(path);
        }
        return build(path, extractGroup(path));
    }
}
```

7.3 navigation分析

如下所示

final class _ARouter {
    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            /**************部分代碼省略***************/
            if (null != callback) {
                callback.onLost(postcard);
            } else {    // No callback for this invoke, then we use the global degrade service.
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }
            return null;
        }

        if (null != callback) {
            callback.onFound(postcard);
        }
        //是否為綠色通道,是否進過攔截器處理
        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }
                @Override
                public void onInterrupt(Throwable exception) {
                    //中斷處理
                    if (null != callback) {
                        callback.onInterrupt(postcard);
                    }
                }
            });
        } else {
            return _navigation(context, postcard, requestCode, callback);
        }

        return null;
    }

    private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        //沒有上下文環境,就用Application的上下文環境
        final Context currentContext = null == context ? mContext : context;
        switch (postcard.getType()) {
            case ACTIVITY:
                // Build intent 構建跳轉的intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());
                // Set flags. 設置flag
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                    //如果上下文不是Activity,則添加FLAG_ACTIVITY_NEW_TASK的flag
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                // Navigation in main looper.  切換到主線程中
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        if (requestCode > 0) {  // Need start for result
                            ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
                        } else {
                            ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
                        }

                        if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
                            ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
                        }

                        if (null != callback) { // Navigation over.
                            callback.onArrival(postcard);
                        }
                    }
                });

                break;
            case PROVIDER:
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                Class fragmentMeta = postcard.getDestination();
                try {
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }

                    return instance;
                } catch (Exception ex) {
                    logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }
}

08.Postcard信息攜帶

Postcard主要為信息的攜帶者,內容是在構造一次路由信息的時候生產的,其繼承于RouteMeta。RouteMeta是在代碼編譯時生成的內容,主要在初始化WareHouse時對跳轉信息做了緩存。

看看代碼如下所示

//Postcard繼承于RouteMeta
public final class Postcard extends RouteMeta


//然后看看編譯生成的文件
/**

public class ARouter$$Group$$me implements IRouteGroup {
  @Override
  public void loadInto(Map atlas) {
    atlas.put("/me/ExperienceCouponActivity", RouteMeta.build(RouteType.ACTIVITY, ExperienceCouponActivity.class, "/me/experiencecouponactivity", "me", null, -1, -2147483648));
    atlas.put("/me/ServiceActivity", RouteMeta.build(RouteType.ACTIVITY, ServiceActivity.class, "/me/serviceactivity", "me", null, -1, -2147483648));
    atlas.put("/me/SettingActivity", RouteMeta.build(RouteType.ACTIVITY, SettingActivity.class, "/me/settingactivity", "me", null, -1, -2147483648));
    atlas.put("/me/UdeskServiceActivity", RouteMeta.build(RouteType.ACTIVITY, UdeskServiceActivity.class, "/me/udeskserviceactivity", "me", null, -1, -2147483648));
  }
}
```
10.DegradeService降級容錯服務

首先,自定義一個類,需要繼承DegradeService類,如下所示

/**
 * 
 *     @author 楊充
 *     blog  : https://github.com/yangchong211
 *     time  : 2018/08/24
 *     desc  : ARouter路由降級處理
 *     revise:

 */
@Route(path = DegradeServiceImpl.PATH)
public class DegradeServiceImpl implements DegradeService {

    static final String PATH = "/service/DegradeServiceImpl";

    @Override
    public void onLost(Context context, Postcard postcard) {
        if (context != null && postcard.getGroup().equals("activity")) {
            Intent intent = new Intent(context, WebViewActivity.class);
            intent.putExtra(Constant.URL, Constant.GITHUB);
            intent.putExtra(Constant.TITLE, "github地址");
            ActivityCompat.startActivity(context, intent, null);
        }
    }

    @Override
    public void init(Context context) {

    }
}
```

如何使用該降級方案,十分簡單。

NavigationCallback callback = new NavCallback() {
    @Override
    public void onArrival(Postcard postcard) {
        LogUtils.i("ARouterUtils"+"---跳轉完了");
    }

    @Override
    public void onFound(Postcard postcard) {
        super.onFound(postcard);
        LogUtils.i("ARouterUtils"+"---找到了");
    }

    @Override
    public void onInterrupt(Postcard postcard) {
        super.onInterrupt(postcard);
        LogUtils.i("ARouterUtils"+"---被攔截了");
    }

    @Override
    public void onLost(Postcard postcard) {
        super.onLost(postcard);
        LogUtils.i("ARouterUtils"+"---找不到了");
        DegradeServiceImpl degradeService = new DegradeServiceImpl();
        degradeService.onLost(Utils.getApp(),postcard);
    }
};

11.Interceptor攔截器

在ARouter模塊的時候講述Interceptor的使用,如果本次路由跳轉不是走的綠色通道那么則會觸發攔截器進行過濾。

final class _ARouter {
    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        /************部分代碼省略************/

        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                /**

                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @Override
                public void onInterrupt(Throwable exception) {
                    if (null != callback) {
                        callback.onInterrupt(postcard);
                    }

                    logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
        } else {
            return _navigation(context, postcard, requestCode, callback);
        }
        return null;
    }
}
```

攔截器的初始化

在剛開始初始化的時候,就已經做了這個操作。

final class _ARouter {
    static void afterInit() {
        // Trigger interceptor init, use byName.
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }
}

InterceptorServiceImpl的init方法:

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
    @Override
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    //循環遍歷倉庫中的攔截器
                    for (Map.Entry> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class interceptorClass = entry.getValue();
                        try {
                            //反射機制構造自定義的每一個攔截器實例
                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            iInterceptor.init(context);
                            //并將其添加在緩存中
                            Warehouse.interceptors.add(iInterceptor);
                        } catch (Exception ex) {
                            throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                        }
                    }
                    interceptorHasInit = true;
                    logger.info(TAG, "ARouter interceptors init over.");
                    synchronized (interceptorInitLock) {
                        interceptorInitLock.notifyAll();
                    }
                }
            }
        });
    }
}

攔截器的工作過程

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
    @Override
    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
            //檢測是否初始化完所有的爛機器
            checkInterceptorsInitStatus();
            //沒有完成正常的初始化,拋異常
            if (!interceptorHasInit) {
                callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
                return;
            }
            //順序遍歷每一個攔截器,
            LogisticsCenter.executor.execute(new Runnable() {
                @Override
                public void run() {
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {
                        _excute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        //攔截器的遍歷終止之后,如果有還有沒有遍歷的攔截器,則表示路由事件被攔截
                        if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn"t return anythings.
                            callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                        } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.
                            callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                        } else {
                            callback.onContinue(postcard);
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }

    //執行攔截器的過濾事件
    private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
        if (index < Warehouse.interceptors.size()) {
            IInterceptor iInterceptor = Warehouse.interceptors.get(index);
            iInterceptor.process(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    // Last interceptor excute over with no exception.
                    counter.countDown();
                    //如果當前沒有攔截過濾,那么使用下一個攔截器
                    _excute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    // Last interceptor excute over with fatal exception.
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    counter.cancel();
                }
            });
        }
    }
}

12.數據傳輸和自動注入 13.多dex的支持

可查看multidex源碼:

public class ClassUtils {  
  /**
     * Identifies if the current VM has a native support for multidex, meaning there is no need for

     *
     * @return true if the VM handles multidex
     */
    private static boolean isVMMultidexCapable() {
        boolean isMultidexCapable = false;
        String vmName = null;

        try {
            if (isYunOS()) {    // YunOS需要特殊判斷
                vmName = ""YunOS"";
                isMultidexCapable = Integer.valueOf(System.getProperty("ro.build.version.sdk")) >= 21;
            } else {    // 非YunOS原生Android
                vmName = ""Android"";
                String versionString = System.getProperty("java.vm.version");
                if (versionString != null) {
                    Matcher matcher = Pattern.compile("(d+).(d+)(.d+)?").matcher(versionString);
                    if (matcher.matches()) {
                        try {
                            int major = Integer.parseInt(matcher.group(1));
                            int minor = Integer.parseInt(matcher.group(2));
                            isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)
                                    || ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)
                                    && (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));
                        } catch (NumberFormatException ignore) {
                            // let isMultidexCapable be false
                        }
                    }
                }
            }
        } catch (Exception ignore) {

        }

        Log.i(Consts.TAG, "VM with name " + vmName + (isMultidexCapable ? " has multidex support" : " does not have multidex support"));
        return isMultidexCapable;
    }
}
```

14.InstantRun支持

什么是InstantRun支持?

Android Studio 2.0 中引入的 Instant Run 是 Run 和 Debug 命令的行為,可以大幅縮短應用更新的時間。盡管首次構建可能需要花費較長的時間,Instant Run 在向應用推送后續更新時則無需構建新的 APK,因此,這樣可以更快地看到更改。

15.生成的編譯代碼

如下所示

關于其他內容介紹 01.關于博客匯總鏈接

1.技術博客匯總

2.開源項目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

02.關于我的博客

我的個人站點:www.yczbj.org,www.ycbjie.cn

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

簡書:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜馬拉雅聽書:http://www.ximalaya.com/zhubo...

開源中國:https://my.oschina.net/zbj161...

泡在網上的日子:http://www.jcodecraeer.com/me...

郵箱:yangchong211@163.com

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault頭條:https://segmentfault.com/u/xi...

掘金:https://juejin.im/user/593943...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73348.html

相關文章

  • 關于Apt注解實踐與總結【包含20篇博客】

    摘要:使用實現功能運行期注解案例使用簡單的注解,便可以設置布局,等效于使用實現路由綜合型案例比較全面的介紹從零起步,一步一步封裝簡易的路由開源庫。申明注解用的就是。返回值表示這個注解里可以存放什么類型值。 YCApt關于apt方案實踐與總結 目錄介紹 00.注解系列博客匯總 01.什么是apt 02.annotationProcessor和apt區別 03.項目目錄結構 04.該案例作用 ...

    gnehc 評論0 收藏0
  • MVPArms官方快速組件化方案開源,來自5K star的信賴

    摘要:原文地址前言起源組件化方案分析業務組件的劃分和代碼隔離路由框架基礎庫的優勢簡介什么是組件化為什么要組件化分析現有的組件化方案如何選擇組件化方案組件化方案描述架構圖一覽架構圖詳解宿主層業務層業務模塊的拆分基礎層核心基礎業務公共服務基礎組件其他 原文地址: https://www.jianshu.com/p/f67... 0 前言 0.1 起源 0.2 組件化方案分析 0.2....

    aikin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<