1. Qu'est-ce que la composition :
Composants, comme leur nom l'indique, pièces assemblées, appelées unités logicielles, qui peuvent être utilisées pour s'assembler dans une application
De ce point de vue, la composition se concentre sur la réutilisabilité, la séparation, la fonction unique et la cohésion élevée ; c'est une petite unité qui peut être divisée en business
2. Les avantages de la composition :
- Les composants, qui peuvent être utilisés à la fois comme une bibliothèque et une application distincte, sont faciles à compiler et à tester séparément, ce qui améliore considérablement l'efficacité de la compilation et du développement ;
- Les composants peuvent avoir leurs propres versions indépendantes, les secteurs d'activité n'interfèrent pas les uns avec les autres et peuvent être compilés, testés, packagés et déployés indépendamment
- Les modules communs partagés par chaque ligne métier sont encapsulés dans des composants et utilisés comme une bibliothèque de dépendances pour chaque ligne métier à appeler, réduisant l'écriture répétitive de code, réduisant la redondance et facilitant la maintenance
3. Architecture en composants
Parmi eux, les "composants métier" peuvent être emballés séparément sous forme d'apk et peuvent être combinés en tant que bibliothèque dans des applications complètes selon les besoins.
Par exemple, selon l'activité spécifique, comme illustré dans la figure ci-dessous, "moudle_main" est le composant métier principal avec la même logique et le même code, et "moudle1" et "moudle2" sont des composants métier séparés pour gérer leur propre logique privée. Et le code, il n'y a pas de dépendance l'un sur l'autre, la version est différente
\
4. Comment interagir sans dépendances entre les composants
\
\
\
Cela nécessite un pont, qui mène à une route, qui est actuellement très utilisée. Alibaba a open source Arouter ( en utilisant la documentation ), et Meituan a open source : ;WMrouter_Source
5. Problèmes à résoudre par routage
- découplage
- la communication
- Ajoutez les modules requis au besoin
6. Utilisation d'ARouter
- comment utiliser:
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. Comment interagir les données entre les modules
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. Comment transporter des données
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. Analyse externe
- A propos de l'enregistrement et de l'initialisation de la Route de la route :
Le routage contient beaucoup d'informations. ARouter divise le routage en plusieurs types. Activity, Fragment et même IProvider pour la communication inter-module utilisent également des annotations de routage.
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注解的参数里面定义的。
- 初始化
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());
}
\
- 初始化流程:
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生成的类,在应用启动的时候注入指定的方法,用于优化)
\
用用你的金手指,评论一下:(链接)