ARouter路由解析

目錄介紹

  • 01.原生跳轉(zhuǎn)實現(xiàn)
  • 02.實現(xiàn)組件跳轉(zhuǎn)方式
    • 2.1 傳統(tǒng)跳轉(zhuǎn)方式
    • 2.2 為何需要路由
  • 03.ARouter配置與優(yōu)勢
  • 04.跨進程組件通信
    • 4.1 URLScheme
    • 4.2 AIDL
    • 4.3 BroadcastReceiver
    • 4.4 路由通信注意要點
  • 05.ARouter的結(jié)構(gòu)
  • 06.ARouter的工作流程
    • 6.1 初始化流程
    • 6.2 跳轉(zhuǎn)頁面流程
  • 07.ARouter簡單調(diào)用api
    • 7.1 最簡單調(diào)用
    • 7.2 build源碼分析
    • 7.3 navigation分析
  • 08.Postcard信息攜帶
  • 09.LogisticsCenter
  • 10.DegradeService降級容錯服務(wù)
  • 11.Interceptor攔截器
  • 12.數(shù)據(jù)傳輸和自動注入
  • 13.多dex的支持
  • 14.InstantRun支持
  • 15.生成的編譯代碼

好消息

  • 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點宪躯,Android技術(shù)博客笼呆,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善……開源的文件是markdown格式的必搞!同時也開源了生活博客,從12年起囊咏,積累共計N篇[近100萬字,陸續(xù)搬到網(wǎng)上]塔橡,轉(zhuǎn)載請注明出處梅割,謝謝!
  • 鏈接地址:https://github.com/yangchong211/YCBlogs
  • 如果覺得好葛家,可以star一下户辞,謝謝!當(dāng)然也歡迎提出建議或者問題癞谒,萬事起于忽微底燎,量變引起質(zhì)變刃榨!

注解學(xué)習(xí)小案例

  • 注解學(xué)習(xí)小案例,比較系統(tǒng)性學(xué)習(xí)注解并且應(yīng)用實踐双仍。簡單應(yīng)用了運行期注解枢希,通過注解實現(xiàn)了setContentView功能;簡單應(yīng)用了編譯器注解朱沃,通過注解實現(xiàn)了防暴力點擊的功能苞轿,同時支持設(shè)置時間間隔;使用注解替代枚舉逗物;使用注解一步步搭建簡單路由案例搬卒。結(jié)合相應(yīng)的博客,在來一些小案例翎卓,從此應(yīng)該對注解有更加深入的理解……
  • 開源項目地址:https://github.com/yangchong211/YCApt

01.原生跳轉(zhuǎn)實現(xiàn)

  • Google提供的原聲路由主要是通過intent契邀,可以分成顯示和隱式兩種。顯示的方案會導(dǎo)致類之間的直接依賴問題失暴,耦合嚴(yán)重坯门;隱式intent需要的配置清單中統(tǒng)一聲明,首先有個暴露的問題锐帜,另外在多模塊開發(fā)中協(xié)作也比較困難田盈。只要調(diào)用startActivity后面的環(huán)節(jié)我們就無法控制了,在出現(xiàn)錯誤時無能為力缴阎。

02.實現(xiàn)組件跳轉(zhuǎn)方式

2.1 傳統(tǒng)跳轉(zhuǎn)方式

  • 第一種允瞧,通過intent跳轉(zhuǎn)
  • 第二種,通過aidl跳轉(zhuǎn)
  • 第三種蛮拔,通過scheme協(xié)議跳轉(zhuǎn)

2.2 為何需要路由

  • 顯示Intent:項目龐大以后述暂,類依賴耦合太大,不適合組件化拆分
  • 隱式Intent:協(xié)作困難建炫,調(diào)用時候不知道調(diào)什么參數(shù)
  • 每個注冊了Scheme的Activity都可以直接打開畦韭,有安全風(fēng)險
  • AndroidMainfest集中式管理比較臃腫
  • 無法動態(tài)修改路由,如果頁面出錯肛跌,無法動態(tài)降級
  • 無法動態(tài)攔截跳轉(zhuǎn)艺配,譬如未登錄的情況下,打開登錄頁面衍慎,登錄成功后接著打開剛才想打開的頁面
  • H5转唉、Android、iOS地址不一樣稳捆,不利于統(tǒng)一跳轉(zhuǎn)

03.ARouter配置與優(yōu)勢

3.1 ARouter的優(yōu)勢

  • 如下所示
    • 直接解析URL路由赠法,解析參數(shù)并賦值
    • 支持多模塊項目
    • 支持InstantRun
    • 允許自定義攔截器
    • ARouter可以提供IoC容器
    • 映射關(guān)系自動注冊
    • 靈活的降級策略

3.2 至于配置和使用

04.跨進程組件通信

4.1 URLScheme【例如:ActivityRouter、ARouter等】

  • 優(yōu)勢有:
    • 基因中自帶支持從webview中調(diào)用
    • 不用互相注冊(不用知道需要調(diào)用的app的進程名稱等信息)
  • 劣勢有:
    • 只能單向地給組件發(fā)送信息乔夯,適用于啟動Activity和發(fā)送指令砖织,不適用于獲取數(shù)據(jù)(例如:獲取用戶組件的當(dāng)前用戶登錄信息)
    • 需要有個額外的中轉(zhuǎn)Activity來統(tǒng)一處理URLScheme
    • 如果設(shè)備上安裝了多個使用相同URLScheme的app款侵,會彈出選擇框(多個組件作為app同時安裝到設(shè)備上時會出現(xiàn)這個問題)
    • 無法進行權(quán)限設(shè)置,無法進行開關(guān)設(shè)置侧纯,存在安全性風(fēng)險

4.2 AIDL

  • 優(yōu)勢有:
    • 可以傳遞Parcelable類型的對象
    • 效率高
    • 可以設(shè)置跨app調(diào)用的開關(guān)
  • 劣勢有:
    • 調(diào)用組件之前需要提前知道該組件在那個進程新锈,否則無法建立ServiceConnection
    • 組件在作為獨立app和作為lib打包到主app時,進程名稱不同茂蚓,維護成本高

4.3 BroadcastReceiver

  • BroadcastReceiver + Service + LocalSocket壕鹉。該方案是參考cc路由框架!
  • 跨組件間通信實現(xiàn)的同時聋涨,應(yīng)該滿足以下條件:
    • 每個app都能給其它app調(diào)用
    • app可以設(shè)置是否對外提供跨進程組件調(diào)用的支持
    • 組件調(diào)用的請求發(fā)出去之后晾浴,能自動探測當(dāng)前設(shè)備上是否有支持此次調(diào)用的app
    • 支持超時、取消

4.4 路由通信注意要點

05.ARouter的結(jié)構(gòu)

  • ARouter主要由三部分組成牍白,包括對外提供的api調(diào)用模塊脊凰、注解模塊以及編譯時通過注解生產(chǎn)相關(guān)的類模塊。
    • arouter-annotation注解的聲明和信息存儲類的模塊
    • arouter-compiler編譯期解析注解信息并生成相應(yīng)類以便進行注入的模塊
    • arouter-api核心調(diào)用Api功能的模塊
  • annotation模塊
    • Route茂腥、Interceptor狸涌、Autowired都是在開發(fā)是需要的注解。
    • image
  • compiler模塊
    • AutoWiredProcessor最岗、InterceptorProcessor帕胆、RouteProcessor分別為annotation模塊對應(yīng)的Autowired、Interceptor般渡、Route在項目編譯時產(chǎn)生相關(guān)的類文件懒豹。
  • api模塊
    • 主要是ARouter具體實現(xiàn)和對外暴露使用的api。

06.ARouter的工作流程

6.1 初始化流程

  • 初始化代碼如下所示
    /**
     * Init, it must be call before used router.
     */
    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 {
        /**
         * LogisticsCenter init, load all metas in memory. Demand initialization
         */
        public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
            mContext = context;
            executor = tpe;
            try {
                long startInit = System.currentTimeMillis();
                Set<String> 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<String>()));
                }
    
                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. 
                        //導(dǎo)入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
                        //導(dǎo)入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
                        //導(dǎo)入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() + "]");
            }
        }
    }
    
  • 綜上所述记餐,整個初始化的流程大概就是:
    • 初始化運行時的上下文環(huán)境
    • 初始化日志logger
    • 尋找router相關(guān)的類
    • 解析并且緩存路由相關(guān)信息
    • 初始化攔截服務(wù)

6.2 跳轉(zhuǎn)頁面流程

  • image

07.ARouter調(diào)用api

7.1 最簡單調(diào)用

  • 最簡單的調(diào)用方式
    ARouter.getInstance()
                    .build("/user/UserFragment")
                    .navigation();
    

7.2 build源碼分析

  • 這個主要是添加跳轉(zhuǎn)的路徑
    public Postcard build(String path) {
        return _ARouter.getInstance().build(path);
    }
    
  • 然后把這個路徑添加到默認的組中
    /**
     * Build postcard by path and default group
     */
    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) {
            //沒有上下文環(huán)境薇正,就用Application的上下文環(huán)境
            final Context currentContext = null == context ? mContext : context;
            switch (postcard.getType()) {
                case ACTIVITY:
                    // Build intent 構(gòu)建跳轉(zhuǎn)的intent
                    final Intent intent = new Intent(currentContext, postcard.getDestination());
                    intent.putExtras(postcard.getExtras());
                    // Set flags. 設(shè)置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主要為信息的攜帶者,內(nèi)容是在構(gòu)造一次路由信息的時候生產(chǎn)的挖腰,其繼承于RouteMeta雕沿。RouteMeta是在代碼編譯時生成的內(nèi)容,主要在初始化WareHouse時對跳轉(zhuǎn)信息做了緩存曙聂。
  • 看看代碼如下所示
    //Postcard繼承于RouteMeta
    public final class Postcard extends RouteMeta
    
    
    //然后看看編譯生成的文件
    /**
     * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
    public class ARouter$$Group$$me implements IRouteGroup {
      @Override
      public void loadInto(Map<String, RouteMeta> 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降級容錯服務(wù)

  • 首先,自定義一個類鞠鲜,需要繼承DegradeService類宁脊,如下所示
    /**
     * <pre>
     *     @author 楊充
     *     blog  : https://github.com/yangchong211
     *     time  : 2018/08/24
     *     desc  : ARouter路由降級處理
     *     revise:
     * </pre>
     */
    @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"+"---跳轉(zhuǎn)完了");
        }
    
        @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的使用榆苞,如果本次路由跳轉(zhuǎn)不是走的綠色通道那么則會觸發(fā)攔截器進行過濾稳衬。
    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() {
                    /**
                     * Continue process
                     *
                     * @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;
        }
    }
    
  • 攔截器的初始化
    • 在剛開始初始化的時候,就已經(jīng)做了這個操作坐漏。
    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)) {
                        //循環(huán)遍歷倉庫中的攔截器
                        for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                            Class<? extends IInterceptor> interceptorClass = entry.getValue();
                            try {
                                //反射機制構(gòu)造自定義的每一個攔截器實例
                                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);
            }
        }
    
        //執(zhí)行攔截器的過濾事件
        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();
                        //如果當(dāng)前沒有攔截過濾,那么使用下一個攔截器
                        _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.數(shù)據(jù)傳輸和自動注入

13.多dex的支持

  • 可查看multidex源碼:
    public class ClassUtils {  
      /**
         * Identifies if the current VM has a native support for multidex, meaning there is no need for
         * additional installation by this library.
         *
         * @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 命令的行為板丽,可以大幅縮短應(yīng)用更新的時間。盡管首次構(gòu)建可能需要花費較長的時間趁尼,Instant Run 在向應(yīng)用推送后續(xù)更新時則無需構(gòu)建新的 APK埃碱,因此,這樣可以更快地看到更改酥泞。

15.生成的編譯代碼

  • 如下所示
    • image

關(guān)于其他內(nèi)容介紹

01.關(guān)于博客匯總鏈接

02.關(guān)于我的博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市芝囤,隨后出現(xiàn)的幾起案子似炎,更是在濱河造成了極大的恐慌,老刑警劉巖凡人,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件名党,死亡現(xiàn)場離奇詭異,居然都是意外死亡挠轴,警方通過查閱死者的電腦和手機传睹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岸晦,“玉大人欧啤,你說我怎么就攤上這事∑羯希” “怎么了邢隧?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冈在。 經(jīng)常有香客問我倒慧,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任纫谅,我火速辦了婚禮炫贤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘付秕。我一直安慰自己兰珍,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布询吴。 她就那樣靜靜地躺著掠河,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猛计。 梳的紋絲不亂的頭發(fā)上唠摹,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音有滑,去河邊找鬼跃闹。 笑死,一個胖子當(dāng)著我的面吹牛毛好,可吹牛的內(nèi)容都是我干的望艺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肌访,長吁一口氣:“原來是場噩夢啊……” “哼找默!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吼驶,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惩激,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蟹演,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體风钻,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年酒请,在試婚紗的時候發(fā)現(xiàn)自己被綠了骡技。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡羞反,死狀恐怖布朦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昼窗,我是刑警寧澤是趴,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站澄惊,受9級特大地震影響唆途,放射性物質(zhì)發(fā)生泄漏富雅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一肛搬、第九天 我趴在偏房一處隱蔽的房頂上張望吹榴。 院中可真熱鬧,春花似錦滚婉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扣溺,卻和暖如春骇窍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锥余。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工腹纳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驱犹。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓嘲恍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雄驹。 傳聞我的和親對象是個殘疾皇子佃牛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容

  • ARouter探究(一) 前言 ARouter 是 Alibaba 開源的一款 Android 頁面路由框架,特別...
    Jason騎蝸乓接撸看世界閱讀 1,333評論 1 3
  • ARouter源碼解讀 以前看優(yōu)秀的開源項目俘侠,看到了頁面路由框架ARouter,心想頁面路由是個啥東東蔬将,于是乎網(wǎng)上...
    陸元偉閱讀 525評論 0 1
  • Arouter框架適合項目比較大爷速,模塊多的時候,可以實現(xiàn)解耦霞怀,不需要知道跳轉(zhuǎn)的是哪個activity惫东,只需要知道配...
    破曉11閱讀 3,465評論 0 2
  • 開發(fā)一款A(yù)pp,總會遇到各種各樣的需求和業(yè)務(wù)里烦,這時候選擇一個簡單好用的輪子凿蒜,就可以事半功倍 前言 上面一段代碼,在...
    WangDeFa閱讀 65,752評論 44 199
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程胁黑,因...
    小菜c閱讀 6,419評論 0 17