Skip to content

Commit

Permalink
add
Browse files Browse the repository at this point in the history
  • Loading branch information
yangchong211 committed Feb 23, 2019
1 parent fd7d777 commit 68c7d0d
Showing 1 changed file with 169 additions and 3 deletions.
172 changes: 169 additions & 3 deletions android/技术架构/14.框架设计与初始化与配置.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
- 01.看阿里ARouter整体设计
- 02.这样设计框架目的
- 03.仿照ARouter设计自己框架
- 04.为何需要初始化操作
- 05.初始化操作逻辑
- 06.如何得到得到路由表的类名



Expand Down Expand Up @@ -44,9 +47,172 @@






### 04.为何需要初始化操作
- 上一篇博客介绍生成注解代码,那么首先需要在用户使用路由跳转之前把这些路由映射关系拿到手,拿到这些路由关系最好的时机就是应用程序初始化的时候,通过apt生成的路由映射关系文件,在程序启动的时候扫描这些生成的类文件,然后获取到映射关系信息,保存起来。
```
public class ARouter_Root_app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("main", ARouter_Group_main.class);
routes.put("yc", ARouter_Group_yc.class);
}
}
public class ARouter_Group_main implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/main/FiveActivity",RouteMeta.build(RouteMeta.Type.ACTIVITY,FiveActivity.class,"/main/FiveActivity","main"));
}
}
public class ARouter_Group_yc implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/yc/SixActivity",RouteMeta.build(RouteMeta.Type.ACTIVITY,SixActivity.class,"/yc/SixActivity","yc"));
}
}
```
- 如图所示
- ![image](https://upload-images.jianshu.io/upload_images/4432347-94db5b239f2aafef.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 得出结论
- 实现了IRouteRoot接口的类都是保存了group分组映射信息,实现了IRouteGroup接口的类都保存了单个分组下的路由映射信息。
### 05.初始化操作逻辑
- 上面已经说了,在启动的时候初始化,并且拿到这些类是最好的。看一下初始化的代码
```
public static void init(Application application) {
mContext = application;
try {
loadInfo();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "初始化失败!", e);
}
}
```
- 在初始化的时候,得到实现IRouteRoot接口的所有类文件,便能通过循环调用它的loadInfo()方法得到所有实现IRouteGroup接口的类,而所有实现IRouteGroup接口的类里面保存了项目的所有路由信息。IRouteGroup的loadInfo()方法,通过传入一个map,便会将这个分组里的映射信息存入map里。
```
/**
* 分组表制作
*/
private static void loadInfo() throws PackageManager.NameNotFoundException,
InterruptedException, ClassNotFoundException,
NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//获得所有 apt生成的路由类的全类名 (路由表)
Set<String> routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PACKAGE);
for (String className : routerMap) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(ROUTE_ROOT_PACKAGE);
stringBuilder.append(".");
stringBuilder.append(SDK_NAME);
stringBuilder.append(SEPARATOR);
stringBuilder.append(SUFFIX_ROOT);
String s = stringBuilder.toString();
Log.d(TAG,"className-----------"+s);
//root中注册的是分组信息 将分组信息加入仓库中
if (className.startsWith(s)) {
//拿到反射字节码
Class<?> aClass = Class.forName(className);
//获取对象
Object o = aClass.getConstructor().newInstance();
((IRouteRoot) o).loadInto(Warehouse.groupsIndex);
}
}
Set<Map.Entry<String, Class<? extends IRouteGroup>>> entries =
Warehouse.groupsIndex.entrySet();
for (Map.Entry<String, Class<? extends IRouteGroup>> stringClassEntry : entries) {
Log.d(TAG, "Root映射表[ " + stringClassEntry.getKey() + " : "
+ stringClassEntry.getValue() + "]");
}
}
```
- 打印映射表日志可知
- map集合key:"/yc/SixActivity"
- map集合value:RouteMeta.build(RouteMeta.Type.ACTIVITY,SixActivity.class,"/yc/SixActivity","yc")
- 看一下RouteMeta.build源码
- RouteMeta里面便保存着ActivityClass的所有信息。第一个功能需求,便是在app进程启动的时候进行框架的初始化(或者在你开始用路由跳转之前进行初始化都可以),在初始化中拿到映射关系信息,保存在map里,以便程序运行中可以快速找到路由映射信息实现跳转。
```
public static RouteMeta build(Type type, Class<?> destination, String path, String group) {
return new RouteMeta(type, null, destination, path, group);
}
private RouteMeta(Type type, Element element, Class<?> destination, String path, String group) {
this.type = type;
this.destination = destination;
this.element = element;
this.path = path;
this.group = group;
}
```
### 06.如何得到得到路由表的类名
- 通过ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)得到apt生成的所有实现IRouteRoot接口的类文件集合
- 拿到这些类后,便可以得到所有的routerAddress---activityClass映射关系。
```
/**
* 得到路由表的类名
* @param context 上下文
* @param packageName 包名
* @return 集合
* @throws PackageManager.NameNotFoundException
* @throws InterruptedException
*/
public static Set<String> getFileNameByPackageName(Application context,
@NonNull final String packageName)
throws PackageManager.NameNotFoundException, InterruptedException {
//创建一个set集合,set集合元素不会重复
final Set<String> classNames = new HashSet<>();
//获得程序所有的apk
List<String> paths = getSourcePaths(context);
//使用同步计数器判断均处理完成
final CountDownLatch countDownLatch = new CountDownLatch(paths.size());
//创建线程池
ThreadPoolExecutor threadPoolExecutor = DefaultPoolExecutor
.newDefaultPoolExecutor(paths.size());
for (final String path : paths) {
if (threadPoolExecutor != null) {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
DexFile dexFile = null;
try {
//加载 apk中的dex 并遍历 获得所有包名为 {packageName} 的类
dexFile = new DexFile(path);
Enumeration<String> dexEntries = dexFile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (!TextUtils.isEmpty(className) &&
className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != dexFile) {
try {
dexFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//释放一个
countDownLatch.countDown();
}
}
});
}
}
//等待执行完成
countDownLatch.await();
return classNames;
}
```
### 关于其他内容介绍
Expand Down

0 comments on commit 68c7d0d

Please sign in to comment.