Enrutamiento por componentes de Android

1. ¿Qué es la fragmentación?

Componentes, como sugiere el nombre, piezas ensambladas, denominadas unidades de software, que se pueden usar para ensamblar en una aplicación

Desde este punto de vista, la componentización se centra en la reutilización, la separación, la función única y la alta cohesión; es una unidad pequeña que se puede dividir en negocios.

2. Las ventajas de la fragmentación:

  • Los componentes, que se pueden usar como biblioteca y como aplicación separada, son fáciles de compilar y probar por separado, lo que mejora en gran medida la eficiencia de la compilación y el desarrollo;
  • Los componentes pueden tener sus propias versiones independientes, las líneas de negocio no interfieren entre sí y se pueden compilar, probar, empaquetar e implementar de forma independiente.
  • Los módulos comunes compartidos por cada línea comercial se encapsulan en componentes y se utilizan como una biblioteca de dependencias para que llame cada línea comercial, lo que reduce la escritura de códigos repetitivos, reduce la redundancia y facilita el mantenimiento.

3. Arquitectura por componentes

Entre ellos, los "componentes comerciales" se pueden empaquetar por separado como apk y se pueden combinar como una biblioteca en aplicaciones integrales según sea necesario

Por ejemplo, según el negocio específico, como se muestra en la figura a continuación, "moudle_main" es el componente comercial principal con la misma lógica y código, y "moudle1" y "moudle2" son componentes comerciales divididos para administrar su propia lógica privada. Y el código, no hay dependencia entre sí, la versión es diferente

\

4. Cómo interactuar sin dependencias entre componentes

\

\

\

Esto requiere un puente, que conduce a una ruta, que actualmente se usa mucho. Alibaba tiene Arouter de código abierto ( usando documentación ), y Meituan tiene código abierto: ;WMrouter_Source

5. Problemas a resolver mediante el enrutamiento

  • desacoplamiento
  • comunicación
  • Agregue los módulos requeridos según sea necesario

6. Uso de ARouter

  1. cómo utilizar:
apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}

dependencies {
    compile 'com.alibaba:arouter-api:x.x.x'
    kapt 'com.alibaba:arouter-compiler:x.x.x'
    ...
}
if (true) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
            ARouter.openLog();     // 打印日志
            ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
        }
        ARouter.init(this); // 尽可能早,推荐在Application中初始化

\

@Route(path = AROUTER_MODULE_1)
class Module1Activity : BaseActivity() {}
@Route(path = ArouterConfig.AROUTER_MODULE_2)
class Module2Activity:BaseActivity() {}
ARouter.getInstance().build(ArouterConfig.AROUTER_MODULE_2).navigation()

2. Cómo interactuar datos entre módulos

interface BaseService :IProvider {
}
interface Module1Service :BaseService {
    override fun init(p0: Context?) {

    }
}
interface Module2Service :BaseService {
    override fun init(p0: Context?) {

    }


    fun setInfo(info:String)


    fun  getInfo():String


    fun  start2Activity(activity: Activity)

}

@Route(path = ArouterConfig.SERVICE_MODULE_1)
class ImlModule1Service:Module1Service {

    fun setInfo(info:String){
        Log.d("ImlModule1Service",info)
    }

    fun  getInfo():String{
        return "ImlModule1Service"
    }
}
@Route(path = ArouterConfig.SERVICE_MODULE_2)
class ImlModule2Service:Module2Service {

    override fun setInfo(info:String){
        Log.d("ImlModule2Service",info)
    }

    override fun  getInfo():String{
        return "ImlModule2Service"
    }

    override fun  start2Activity(activity: Activity){
        activity.startActivity(Intent(activity,Module2Activity::class.java))
    }
}



var service2 =
            ARouter.getInstance().build(ArouterConfig.SERVICE_MODULE_2).navigation() as Module2Service
        var btn = findViewById<Button>(R.id.btn_open)
        btn.setOnClickListener {
            //service2.start2Activity(mActivity)
            service2.setInfo("嘻嘻,你是个傻子呀")
            var btnStr = service2.getInfo()
            btn.setText(btnStr)
        }

3. Cómo transportar datos

ARouter.getInstance().build(ArouterConfig.AROUTER_MODULE_1).withString("key","Nihaoya").navigation()

//数据自动解析
@Route(path = AROUTER_MODULE_1)
class Module1Activity : BaseActivity() {
    @Autowired
    @JvmField
    var key :String ?=null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.module_1)
        ARouter.getInstance().inject(this)
        var tv = findViewById<TextView>(R.id.tv_module1)
        key?.apply { tv.setText(key) }

        var service2 =
            ARouter.getInstance().build(ArouterConfig.SERVICE_MODULE_2).navigation() as Module2Service
        var btn = findViewById<Button>(R.id.btn_open)
        btn.setOnClickListener {
            //service2.start2Activity(mActivity)
            service2.setInfo("嘻嘻,你是个傻子呀")
            var btnStr = service2.getInfo()
            btn.setText(btnStr)
        }
    }
}

7. Análisis de enrutador

  1. Sobre el registro e inicialización de la Ruta de la ruta:

El enrutamiento contiene mucha información. ARouter divide el enrutamiento en varios tipos. Actividad, Fragmento e incluso IProvider para la comunicación entre módulos también usan anotaciones de enrutamiento.

public enum RouteType {
    ACTIVITY(0, "android.app.Activity"),
    SERVICE(1, "android.app.Service"),
    PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
    CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
    BOARDCAST(-1, ""),
    METHOD(-1, ""),
    FRAGMENT(-1, "android.app.Fragment"),
    UNKNOWN(-1, "Unknown route type");
}

然后收集,所有被Route注解的Activity,Provider,Fragment都会按照不同分组收集到不同的类里面。类似下面这样:

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$module1 implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/module1/Module1Activity", RouteMeta.build(RouteType.ACTIVITY, Module1Activity.class, "/module1/module1activity", "module1", new java.util.HashMap<String, Integer>(){{put("key", 8); }}, -1, -2147483648));
  }
}

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$name implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/name/service1", RouteMeta.build(RouteType.PROVIDER, ImlModule1Service.class, "/name/service1", "name", null, -1, -2147483648));
  }
}

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$comluoli implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("module1", ARouter$$Group$$module1.class);
    routes.put("name", ARouter$$Group$$name.class);
  }
}

这里特别需要注意的是路由是按组分开收集的,也就是说一个模块里面的Route可以定义多个分组,会生成多个路由表,分组名是/module1/Module1Activity里面的module1,当然换成别的就是别的分组了。这也是为什么不同的模块不能使用同一个分组的原因。

说明:固定包名com.alibaba.android.arouter.routes

ARouter$$Group$$+分组名,分组名称是在Route注解的参数里面定义的。

  1. 初始化
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;

        try {
            long startInit = System.currentTimeMillis();
            //load by plugin first
            loadRouterMap();
            if (registerByPlugin) {
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
                Set<String> 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<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.
                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        // Load interceptorMeta
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        // Load providerIndex
                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                    }
                }
            }

            logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");

            if (Warehouse.groupsIndex.size() == 0) {
                logger.error(TAG, "No mapping files were found, check your configuration please!");
            }

            if (ARouter.debuggable()) {
                logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
            }
        } catch (Exception e) {
            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
        }
    }

上面的就是进行注解数据的加载与解析,存储在缓存中,我们发现如果一个应用几百个页面,这样加载的时候是不是会比较慢;所以上面做了一些列的优化

    • 分组加载优化
//然后收集,所有当前模块的对应的ARouter$$Group$$+分组名类,以分组名为key。
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$comluoli implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("module1", ARouter$$Group$$module1.class);
    routes.put("name", ARouter$$Group$$name.class);
  }
}
    • 使用gradle plugin生成代码调用
apply plugin: 'com.alibaba.arouter'

repositories {
        google()
        mavenCentral()
    }
    dependencies {
          classpath "com.alibaba:arouter-register:1.0.2"

        // NOTE: Do not place your application dependencies here; they belong
        // in th
private static void loadRouterMap() {
    registerByPlugin = false;
    //auto generate register code by gradle plugin: arouter-auto-register
    // looks like below:
    // registerProvider(new ARouter$$Providers$$news());
}

\

  1. 初始化流程:

4.跳转的解析:

ARouter.getInstance().build(ArouterConfig.AROUTER_MODULE_2).navigation()

\

 private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = postcard.getContext();

        switch (postcard.getType()) {
            case ACTIVITY:
                // Build intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                // Set flags.
                int flags = postcard.getFlags();
                if (0 != flags) {
                    intent.setFlags(flags);
                }

                // Non activity, need FLAG_ACTIVITY_NEW_TASK
                if (!(currentContext instanceof Activity)) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

                // Navigation in main looper.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

                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;
    }

5.数据的自动解析

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class Module1Activity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    Module1Activity substitute = (Module1Activity)target;
    substitute.key = substitute.getIntent().getExtras() == null ? substitute.key : substitute.getIntent().getExtras().getString("key", substitute.key);
  }
}

6.拦截的拓展(...)

总结:

组件化比较适合大型,模块化明显的项目,还是得看公司项目的实际需要

路由实现三板斧

  • 注解(收集)
  • apt(根据注解收集生成对应的类)
  • gradle plugin(根据apt生成的类,在应用启动的时候注入指定的方法,用于优化)

\

用用你的金手指,评论一下:(链接)

Supongo que te gusta

Origin juejin.im/post/7116451169282555940
Recomendado
Clasificación