ARouter路由使用與源碼分析(二)

彙總篇章:

ARouter路由源碼分析彙總

簡介

上一篇,我們將ARouter分成五步處理,上面我們瞭解了Arouter的init處理方法,對interceptor/provider等的加載邏輯,下面我們從剩下的路由和跳轉取值完成對Arouter的瞭解

第三步:發起路由操作

// 1. 應用內簡單的跳轉(通過URL跳轉在'進階用法'中)
ARouter.getInstance().build("/test/activity").navigation();

// 2. 跳轉並攜帶參數
ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .withObject("key4", new Test("Jack", "Rose"))
            .navigation();
ARouter的Builder

ARouter.getInstance().build 將路徑信息傳遞進ARouter內,進入方法中後,看到build方法的返回值Postcard類,由於Postcard類屬性相對較多,這裏只羅列部分

public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;
    private Object tag;             // A tag prepare for some thing wrong.
    private Bundle mBundle;         // Data to transform
    private int flags = -1;         // Flags of route
    private int timeout = 300;      // Navigation timeout, TimeUnit.Second
    private IProvider provider;     // It will be set value, if this postcard was provider.
    private boolean greenChannel;
    private SerializationService serializationService;

    // Animation
    private Bundle optionsCompat;    // The transition animation of activity
    private int enterAnim;
    private int exitAnim;

    ...省略部分代碼...
    
    public Object navigation(Context context, NavigationCallback callback) {
        return ARouter.getInstance().navigation(context, this, -1, callback);
    }
}
Postcard即是我們的盛放的參數信息

進入關鍵方法navigation方法在這裏插入圖片描述

navigation的第一個completion方法

public synchronized static void completion(Postcard postcard) {
        ...省略部分代碼...
        /**
         * 通過Warehouse的routers中獲取RouteMeta信息
         * Postcard就是RouteMeta實現
         */
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
  			/**
  			 * routeMeta爲null,有可能是未加載進Warehouse的
  			 * 補充
  			 */
        if (null == routeMeta) {
          	/**
          	 * 先加載分組信息
          	 */
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
            if (null == groupMeta) {
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                try {
                    ...省略部分代碼...
										/**
										 * 加載分組信息和分組內的routes信息
										 */
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());
										...省略部分代碼...
                } catch (Exception e) {
                    ...省略部分代碼...
                }

                completion(postcard);   // Reload
            }
        } else {
          	/**
						 * 從routeMeta獲取信息,補充postcard,完善postcard信息
						 */
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            Uri rawUri = postcard.getUri();
            if (null != rawUri) {   // Try to set params into bundle.
              	/**
                 * 設置參數進bundle
                 */
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                Map<String, Integer> paramsType = routeMeta.getParamsType();

                if (MapUtils.isNotEmpty(paramsType)) {
                    // Set value by its type, just for params which annotation by @Param
                    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                        setValue(postcard,
                                params.getValue(),
                                params.getKey(),
                                resultMap.get(params.getKey()));
                    }

                    /**
                     * 補充Extras
                     */
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }

                // Save raw uri
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }
						/**
						 * 對PROVIDER類型和FRAGMENT類型,設置Green channel,跳過所有攔截器設置
						 */
            switch (routeMeta.getType()) {
                case PROVIDER:
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) { // There's no instance of this provider
                        IProvider provider;
                        try {
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

當onCompletion處理postcard匹配失敗時,

如果NavigationCallback不爲null則通過NavigationCallback的onLost通知失敗;

如果NavigationCallback爲null則嘗試查找全局降級策略DegradeService,執行全局策略的onLost失敗通知;

緊跟着LogisticsCenter.completion 對postcard處理後,如果postcard非greenChannel,則通過interceptor執行doInterceptions方法,做攔截器攔截業務處理

在這裏插入圖片描述
最後 進駐實現_navigation處理,也就是真正跳轉方式的intent處理:

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = null == context ? mContext : context;

        switch (postcard.getType()) {
            case ACTIVITY:
            		/**
            		 * Activity的跳轉intent處理
            		 */
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {
                    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) {
                            ((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:
            		/**
            		 * 廣播/contentProvider/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) {
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

第四步:跳轉過程監聽和參數解析

  • 跳轉過程監聽
    @Interceptor(priority = 8, name = "測試用攔截器")
    public class TestInterceptor implements IInterceptor {
        @Override
        public void process(Postcard postcard, InterceptorCallback callback) {
          //攔截邏輯
        }
    
        @Override
        public void init(Context context) {
        }
    }
    
    ARouter.getInstance().build(Uri.parse("arouter://beer.test/app/routeActivity"))
                    .navigation(this, new NavCallback() {
                        @Override
                        public void onArrival(Postcard postcard) {
                            Log.e("beerlib","==onArrival==");
                        }
    
                        @Override
                        public void onInterrupt(Postcard postcard) {
                            Log.e("beerlib","==onInterrupt==");
                        }
    
                        @Override
                        public void onFound(Postcard postcard) {
                            Log.e("beerlib","==onFound==");
                        }
    
                        @Override
                        public void onLost(Postcard postcard) {
                            Log.e("beerlib","==onLost==");
                        }
                    });
    

    在_navigation中,Activity類型:在跳轉時通過NavigationCallback調用onArrival方法

    在navigation的completion方法異常時,通過NavigationCallback調用onLost方法;

    在navigation的completion方法正常執行時,通過NavigationCallback調用onFound方法;

  • 參數解析
    @Route(path = "/test/activity")
    public class Test1Activity extends Activity {
        @Autowired
        public String name;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        	super.onCreate(savedInstanceState);
          ARouter.getInstance().inject(this);
    
          Log.d("param", name);
        }
    }
    
    @Route(path = "/yourservicegroupname/json")
    public class JsonServiceImpl implements SerializationService {
        @Override
        public void init(Context context) {
        }
    
        @Override
        public <T> T json2Object(String text, Class<T> clazz) {
            return JSON.parseObject(text, clazz);
        }
    
        @Override
        public String object2Json(Object instance) {
            return JSON.toJSONString(instance);
        }
    }
    
    ARouter的Inject

    ARouter的init方法結束,基本上屬於初始化操作就結束了,接下來就是我們Activity等目標跳轉的處理

    也就是我們使用的ARouter.getInstance().inject(this);

    也就從inject方法開始分析其對Activity的處理

    static void inject(Object thiz) {
            AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
            if (null != autowiredService) {
                autowiredService.autowire(thiz);
            }
        }
    
    
    對於"/arouter/service/autowired"這個的聲明類是AutowiredServiceImpl
      
    @Route(path = "/arouter/service/autowired")
    public class AutowiredServiceImpl implements AutowiredService {
      	/**
      	 * 注入器的緩存集合
      	 */
        private LruCache<String, ISyringe> classCache;
      	/**
      	 * 黑名單集合
      	 */
        private List<String> blackList;
    
        @Override
        public void init(Context context) {
            classCache = new LruCache<>(66);
            blackList = new ArrayList<>();
        }
    
        @Override
        public void autowire(Object instance) {
            String className = instance.getClass().getName();
            try {
                if (!blackList.contains(className)) {
                  	/**
                  	 * 首先從緩存中通過全屬性名獲取對應的注入器
                  	 */
                    ISyringe autowiredHelper = classCache.get(className);
                    if (null == autowiredHelper) {  // No cache.
                        autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                    }
                  	/**
                  	 * 通過注入器,注入當前頁面信息(爲什麼是當前頁面信息,可以看下面的實現部分)
                  	 */
                    autowiredHelper.inject(instance);
                    classCache.put(className, autowiredHelper);
                }
            } catch (Exception ex) {
                blackList.add(className);    // This instance need not autowired.
            }
        }
    }
    
    我通過一個插件生成的器中一個注入器實例:
      examples:
    		public class RouteActivity$$ARouter$$Autowired implements ISyringe {
          private SerializationService serializationService;
    
          @Override
          public void inject(Object target) {
            /**
             * SerializationService這個方法內總共有三個方法
             * json2Object[該方法標記過時]/object2Json/parseObject
             * 如字面一個意思,就是對傳遞數據的json處理,也就是我們在頁面中可以SerializationService獲取傳遞的參數
             */
            serializationService = ARouter.getInstance().navigation(SerializationService.class);
            /**
             * 到這裏豁然開朗,通過AutowiredServiceImpl的autowire執行此inject傳入的當前頁面信息
             * 通過獲取getIntent,對我們Autowired的屬性複製
             */
            RouteActivity substitute = (RouteActivity)target;
            substitute.name = substitute.getIntent().getStringExtra("name");
          }
        }
    
    

ARouter的整個jar代碼層次的處理到這裏就結束了,接下來通過新的一篇對ARouter插件的源碼處理,輔助理解ARouter

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章