Komponentenbasiertes Routing von Android

1. Was ist Komponentisierung:

Komponenten sind, wie der Name schon sagt, zusammengesetzte Teile, sogenannte Softwareeinheiten, die zum Zusammenbau in einer Anwendung verwendet werden können

Aus dieser Sicht konzentriert sich die Komponentisierung auf Wiederverwendbarkeit, Trennung, Einzelfunktion und hohe Kohäsion; es ist eine kleine Einheit, die im Unternehmen geteilt werden kann

2. Die Vorteile der Komponentisierung:

  • Komponenten, die sowohl als Bibliothek als auch als separate Anwendung verwendet werden können, lassen sich einfach kompilieren und separat testen, was die Effizienz der Kompilierung und Entwicklung erheblich verbessert;
  • Komponenten können ihre eigenen unabhängigen Versionen haben, Geschäftslinien stören sich nicht gegenseitig und können unabhängig voneinander kompiliert, getestet, gepackt und bereitgestellt werden
  • Die gemeinsamen Module, die von jedem Geschäftsbereich gemeinsam genutzt werden, werden in Komponenten gekapselt und als Abhängigkeitsbibliothek für jeden Geschäftsbereich zum Aufrufen verwendet, wodurch das wiederholte Schreiben von Code reduziert, Redundanzen reduziert und die Wartung erleichtert werden

3. Komponentenarchitektur

Darunter sind die „Business-Komponenten“ separat als apk paketierbar und können je nach Bedarf als Bibliothek zu umfassenden Anwendungen kombiniert werden

Beispielsweise ist „moudle_main“ gemäß dem spezifischen Geschäft, wie in der folgenden Abbildung gezeigt, die Hauptgeschäftskomponente mit derselben Logik und demselben Code, und „moudle1“ und „moudle2“ sind getrennte Geschäftskomponenten, um ihre eigene private Logik zu verwalten. Und der Code, es gibt keine Abhängigkeit voneinander, die Version ist unterschiedlich

\

4. Wie man ohne Abhängigkeiten zwischen Komponenten interagiert

\

\

\

Dies erfordert eine Brücke, die zu einer Route führt, die derzeit viel verwendet wird. Alibaba hat Arouter als Open Source ( Dokumentation verwendend ), und Meituan hat Open Source: ;WMrouter_Source

5. Durch Routing zu lösende Probleme

  • Entkopplung
  • Kommunikation
  • Fügen Sie nach Bedarf erforderliche Module hinzu

6. Verwendung von ARouter

  1. wie benutzt man:
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. Wie man Daten zwischen Modulen interagiert

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. Wie man Daten transportiert

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. ARouter-Analyse

  1. Über die Registrierung und Initialisierung der Route der Route:

Routing enthält viele Informationen. ARouter unterteilt Routing in mehrere Typen. Aktivität, Fragment und sogar IProvider für die Kommunikation zwischen Modulen verwenden ebenfalls Routing-Anmerkungen.

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生成的类,在应用启动的时候注入指定的方法,用于优化)

\

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

Ich denke du magst

Origin juejin.im/post/7116451169282555940
Empfohlen
Rangfolge