Android路由

您所在的位置:网站首页 自考答题卡考生承诺忘签名了 Android路由

Android路由

2023-08-19 06:46| 来源: 网络整理| 查看: 265

一、模块化和组件化

Android开发中,随着功能的不断增加,整个项目越来约庞大,导致代码结构混乱,修改繁琐,管理困难,android模块化开发的思想应运而生。模块化主要时为了解耦,将功能以模块的形式进行封装,模块间通过接口调用形进行通信,不同功能可以由不同的开发人员去开发、维护,每个开发人员只需关注自己负责的模块即可。 简单的模块化开发只需将不同的模块代码放在不同的文家夹路径下即可区分,但是复杂的项目或者便于独立依赖引用的需要可能需要将不同的模块功能放置在不同的library中,此时依赖和接触依赖只需在build.gradle中配置即可。

那么什么时组件化呢?组件化和模块化有什么区别呢?其实组件化是模块化的延伸,模块化是以android library的形式等待其他模块依赖调用,组件化也有这个功能,区别就是组件化可以根据build.gradle中配置将自己作为android application,在自身功能调试时将自己设为android application称为一个独立的app方便开发调试,当功能调试完成后正式发布时再将自身设为android library供第三方调用,如下面设置

//build.gradle if (!DEBUG) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } …… //gradle.properties …… DEBUG = true

 

二、ARouter

由于观察者模式的大量使用,模块之前的通信就更加频繁,为了不至于出现模块间相互依赖的混乱场面,各种路由通信机制也产生了。EventBus可以用于模块间的消息传递,在EventBus使用姿势和源码分析一文中有提到EventBus-2.4使用观察者订阅以及反射的方式来实现功能,也可以通过android的广播机制来通信,今天主要介绍阿里的路由框架ARouter。

2.1 使用 2.1.1 gradle配置

要使用ARouter的moudle所在的build.gradle中添加依赖(最新版本号可以查看ARouter/gradle.properties文件中arouter_main_version的值),添加module参数(主module和library中都需要添加)

//build.gradle android { …… defaultConfig { …… javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } } } …… } …… implementation 'com.alibaba:arouter-api:1.5.1' annotationProcessor 'com.alibaba:arouter-compiler:1.5.1' 2.1.2初始化

越早越好,最好在Application的onCreate方法中去初始化 

public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); ARouter.init(this); //初始化 ARouter.openDebug(); //打开debug开关 ARouter.openLog(); //打开log开关 } } 2.1.3添加module依赖

一个android工程有一个主module和多个library module。这里library module想被编译到apk中的话必须被主module依赖或者传递被主module依赖。比如本文中的主module依赖了两个library module

implementation project(path: ':mylibrary2') implementation project(path: ':mylibrary')

 

这里可能会有人问了,既然主module都直接依赖library module,那么直接调用不就完了,还整路由干啥?这里需要注意,虽然我这里主module依赖两个library module,但是两个library module并没有相互依赖哦,两个library module是不能相互调用的,但是由于它们都依赖了路由库,现在它们可以通过路由库相互调用了。路由的另外一个优点是可以进行一些拦截处理,中间过程可控。

2.1.4 定义路由

在library的Activity中声明路由路径

//Lib1Activity.java package com.shan.mylibrary; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; @Route(path = "/lib1/Lib1Activity") //路由路径 public class Lib1Activity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lib1); Toast.makeText(this,"this is Lib1Activity toast",Toast.LENGTH_SHORT).show(); ARouter.getInstance().build("/lib2/Lib2Activity") //跳转到Lib2Activity .navigation(); } } //Lib2Activity.java package com.shan.mylibrary2; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import com.alibaba.android.arouter.facade.annotation.Route; @Route(path = "/lib2/Lib2Activity") //路由路径 public class Lib2Activity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lib2); } } 2.1.5路由跳转和拦截

现在通过主moudle跳转到Lib1Activity,由于有Lib1Activity的onCreate又执行了跳转Lib2Activity的操作,虽然mylibrary没有依赖mylibrary2,但是通过路由库的中转, 最终跳转到了Lib2Activity界面

public class MainActivity extends AppCompatActivity { private static final String TAG = "MyRouterApp"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "onCreate: "); ARouter.getInstance().build("/lib1/Lib1Activity") .navigation(); } }

在mylibrary2中创建Lib2Interceptor.java,使用@Interceptor注解标注类名,拦截是app全局的,这里使用onContinue放任了跳转继续执行,也可以使用onInterrupt来阻止跳转。

package com.shan.mylibrary2; import android.content.Context; import android.util.Log; import com.alibaba.android.arouter.facade.Postcard; import com.alibaba.android.arouter.facade.annotation.Interceptor; import com.alibaba.android.arouter.facade.callback.InterceptorCallback; import com.alibaba.android.arouter.facade.template.IInterceptor; @Interceptor(priority = 8) public class Lib2Interceptor implements IInterceptor { private Context mContext; private static final String TAG = "Lib2Interceptor"; @Override public void process(Postcard postcard, InterceptorCallback callback) { Log.d(TAG, "process: "+postcard.getPath()+",本次放行"); callback.onContinue(postcard); // callback.onInterrupt(postcard);//放行 } @Override public void init(Context context) { mContext = context; Log.d(TAG, "init: "); } } 2.1.6 路由调用

前面提到了通过path方式来指定module之间activity的跳转,那么两个module间怎么调用不同module的类方法呢?此时就需要IProvider上场了。首先定义一个基本库baselibrary定义接口方法供其他module去实现,baselibrary也需要依赖路由库并且指定相关参数。然后在baselibrary创建一个BaseService接口,并创建一个接口方法。

//BaseService.java package com.shan.baselibrary; import com.alibaba.android.arouter.facade.template.IProvider; public interface BaseService extends IProvider { void sayHello(); }

 这里为了演示,我在mylibrary中创建Lib1ServiceImpl.java去实现BaseService接口方法 

 

//Lib1ServiceImpl.java package com.shan.mylibrary; import android.content.Context; import android.util.Log; import android.widget.Toast; import com.alibaba.android.arouter.facade.annotation.Route; import com.shan.baselibrary.BaseService; @Route(path = "/Lib1ServiceImpl/hello") public class Lib1ServiceImpl implements BaseService { private Context mContext; @Override public void sayHello() { Toast.makeText(mContext,"Lib1ServiceImpl sayHello",Toast.LENGTH_SHORT).show(); } @Override public void init(Context context) { Log.d("Lib1ServiceImpl", "init: "); mContext = context; } }

然后在mylibrary2的Lib2Activity的点击事件中去调用Lib1ServiceImpl.sayHello方法,点击执行go方法后就有toast提示了,跨module类方法调用,真他么香。

package com.shan.mylibrary2; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.shan.baselibrary.BaseService; @Route(path = "/lib2/Lib2Activity") //路由路径 public class Lib2Activity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lib2); } public void go(View view) { ((BaseService) ARouter.getInstance().build("/Lib1ServiceImpl/hello").navigation()).sayHello(); } } 2.2 原理分析 2.2.1 路由库jar

上面提到需要在build.gardle中依赖路由库com.alibaba:arouter-api和com.alibaba:arouter-compiler,可以在as的External Libraries中找到已下载到本地的arouter-annotation-1.0.6.jar和jetified-arouter-api-1.5.1.aar

(1)arouter-annotation-1.0.6.jar

arouter-annotation-1.0.6.jar中声明了注解元数据,Autowired在上面用到过,可以通过注解的方式传递一些成员变量;Interceptor用于声明跳转拦截器,注意这里的拦截器是全局的,路由库初始化时拦截器就会初始化;Route声明activity和服务的跳转路径。 

 

(2)jetified-arouter-api-1.5.1.aar

jetified-arouter-api-1.5.1.aar中主要涉及一些api接口供三方app调用,路由库的具体实现逻辑都在这里展开。

2.2.2 注解生成类

路由库使用了注解来自动生成相关辅助类,可以在module的build/generated/ap_generated_sources/debug/out/com.alibaba.android.arouter.routes目录下看到自动生成的java文件

这些注解的作用就是搜集被路由库元数据注解的类或成员变量,等待后续被路由库具体实现jetified-arouter-api-1.5.1.aar调用。

2.2.3路由库初始化

ARouter.init(this)调用了_ARouter的init方法,实际上这里使用了门面模式,_ARouter的init方法调用了LogisticsCenter的init方法。

 

//_ARouter.java protected static synchronized boolean init(Application application) { mContext = application; LogisticsCenter.init(mContext, executor); logger.info(Consts.TAG, "ARouter init success!"); hasInit = true; mHandler = new Handler(Looper.getMainLooper()); return true; }

LogisticsCenter的init方法根据是否是debug模式选择不同的加载方式:如果是debug模式则获取dex文件中以ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"开头的的class;否则,从sp文件中读取。然后遍历含有路由库集合routerMap,将注解类添加到Warehouse.groupsIndex中,实际上会调用2.2.2中生成的com.alibaba.android.arouter.routes开头的类,比如ARouter\$\$Root$$mylibrary.java的loadInto方法会被调用。这样就完成了所有module中注解生成类的添加。

//LogisticsCenter.java public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { if (registerByPlugin) { logger.info(TAG, "Load router map by arouter-auto-register plugin."); } else { Set routerMap; // 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 generated by arouter-compiler. 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 finishes. } 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. ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); //加载IRouteRoot } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) { // Load interceptorMeta ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); //加载IInterceptorGroup } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) { // Load providerIndex ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); //加载IProviderGroup } } } } /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class ARouter$$Root$$mylibrary implements IRouteRoot { @Override public void loadInto(Map parent) { Class clazz = null == parent ? instance.getClass() : parent; ISyringe syringe = getSyringe(clazz); if (null != syringe) { syringe.inject(instance); //绑定 } Class superClazz = clazz.getSuperclass(); // has parent and its not the class of framework. if (null != superClazz && !superClazz.getName().startsWith("android")) { doInject(instance, superClazz); } } //Lib1Activity$$ARouter$$Autowired.java package com.shan.mylibrary; import com.alibaba.android.arouter.facade.service.SerializationService; import com.alibaba.android.arouter.facade.template.ISyringe; import com.alibaba.android.arouter.launcher.ARouter; import java.lang.Object; import java.lang.Override; /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class Lib1Activity$$ARouter$$Autowired implements ISyringe { private SerializationService serializationService; @Override public void inject(Object target) { serializationService = ARouter.getInstance().navigation(SerializationService.class); Lib1Activity substitute = (Lib1Activity)target; substitute.age = substitute.getIntent().getIntExtra("age", substitute.age); } }

 getSyringe方法先从classCache中判断该类是否已经添加过,如果没有则通过clazz.getName() + SUFFIX_AUTOWIRED格式创建一个syringeHelper 出来,看下常量的值就可以和Lib1Activity\$\$ARouter$$Autowired.java联系起来了,那么此时创建的就是一个Lib1Activity\$\$ARouter$$Autowired.java,将新建的Lib1Activity\$\$ARouter$$Autowired.java添加到classCache中,然后将Lib1Activity$$ARouter$$Autowired.java作为方法返回值返回,这就是路由库注解变量传参原理过程。

//Consts.java public final class Consts { public static final String SDK_NAME = "ARouter"; public static final String SEPARATOR = "$$"; public static final String SUFFIX_AUTOWIRED = SEPARATOR + SDK_NAME + SEPARATOR + "Autowired"; } //AutowiredServiceImpl.java private ISyringe getSyringe(Class clazz) { String className = clazz.getName(); try { if (!blackList.contains(className)) { ISyringe syringeHelper = classCache.get(className); if (null == syringeHelper) { // No cache. syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance(); } classCache.put(className, syringeHelper); return syringeHelper; } } catch (Exception e) { blackList.add(className); // This instance need not autowired. } return null; }

 (2)跳转

刚才的Autowired实际上讲解的是withInt("age",66)方法,那么本小节的跳转主要讲解navigation()方法。Postcard.java的navigation()方法最终会调用ARouter.java的navigation()方法,ARouter.java的navigation()方法会调用门面具体实现_ARouter.java的navigation()方法。

//Postcard.java public Object navigation(Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this, -1, callback); }

ARouter.java的navigation()方法先通过LogisticsCenter.completion方法添加一些参数配置,然后根据拦截情况执行_navigation方法或者打断。

//_ARouter.java protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class); if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) { // Pretreatment failed, navigation canceled. return null; } try { LogisticsCenter.completion(postcard);//判断此路由信息是否已添加到Warehouse.groupsIndex中,如果没有则调用loadInto方法添加 } catch (NoRouteFoundException ex) { logger.warning(Consts.TAG, ex.getMessage()); if (debuggable()) { // Show friendly tips for user. runInMainThread(new Runnable() { @Override public void run() { Toast.makeText(mContext, "There's no route matched!\n" + " Path = [" + postcard.getPath() + "]\n" + " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show(); } }); } 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() { /** * 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; }

 LogisticsCenter.completion方法先检查该路由是否已加载到Warehouse.routes,如果没有则调用loadInto方法加载到Warehouse.routes,然后添加一些目的地、优先级等参数,对于接口服务使用IOC创建具体实例。

//LogisticsCenter.java public synchronized static void completion(Postcard postcard) { if (null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!"); } RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); if (null == routeMeta) { // Maybe its does't exist, or didn't load.该路由还未加载到Warehouse.routes Class


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3