- 在打开页面之前,提前去调用加载数据的方法(可能是网络数据或者数据库),这样页面打开以后数据可能已经准备好了,就可以马上展示给用户。页面的初始化一般比较耗时,这样可以节省掉初始化的时间,并行的去初始化页面和加载数据,缩短页面展示给用户的总时间。
- 解决思路举一个例子:例如在打开A页面Activity时,在调用startActivity方法的时候,就去调用A页面的加载数据方法loadData(),此时开始加载网络数据,同时Acitivity开始初始化加载布局等,等Activity准备好UI需要数据的时候去检查loadData()是否加载完毕,如果完毕了就直接显示数据,如果没有完成就弹出loading开始等待它执行完成。
Activity/Fragment/View的初始化一般都会消耗一些时间,例如:
-
在打开页面Activity时,一般的流程是这样的:
- 1、通知AMS进程去创建新的Activity
- 2、AMS检查Activity进程是否存在,不存在先创建进程,已经存在就通知该进程创建Activity实例
- 3、Activity创建完后加载布局View
- 4、然后去网络中或者数据库中异步请求数据
- 5、数据准备好后通知渲染到View上
-
上面的流程一般是串行的,即要等到Activity准备好后再去请求数据,而准备Activity的过程往往是耗时的过程(例如启动Activity涉及到跨进程、遍历创建View树都是耗时的过程),为什么不把这个过程改为并行的呢?甚至改为提前进行呢?
-
怎样优雅地把创建页面和请求数据并行进行,同时又不改变以前数据请求的调用方式呢?and-load-aot提供了一种思路:在需要加载数据的方法上添加注解标记,然后利用编译期注解生成页面与加载方法的映射关系,之后就可以在需要提前加载数据的时候调用该方法的路由去提前加载数据了
- 在项目的build.gradle中添加api依赖库以及编译时的注解处理器
annotationProcessor 'com.sw.aot.load:load-aot-compiler:1.0.1'
implementation 'com.sw.aot.load:load-aot-annotation:1.0.1'
implementation 'com.sw.aot.load:load-aot-api:1.0.1'
- 添加注解处理器的配置信息AOT_INDEX,会在编译器生成该类ExampleAotIndex.java,该类里面含有加载方法的路由信息
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AOT_INDEX: 'com.sw.aot.example.ExampleAotIndex']
}
}
}
- 在加载数据的方法上加上@AOTLoad注解,注解的参数router代表着该方法的路由,后面会通过这个路由来调用该方法。加载数据的方法的返回值需要统一为ResultData, T是具体的实体类型,通过调用ResultData.setData(xxx)填充实体数据,通过调用ResultData.flush()方法来通知数据已经加载完毕
@AOTLoad(router = "/Example/LoadMockData")
public ResultData<String> loadMockData(){
ResultData<String> result = new ResultData<String>();
//load data from server or db ...
//mock load data asyn
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
result.setCode(0);//加载成功
result.setData("MOCK: LOAD DATA SUCCESS");//设置数据
result.flush();//加载完毕
} catch (InterruptedException e) {
e.printStackTrace();
result.setCode(-1);//加载失败
result.flush();//加载完毕
}
}
}).start();
return result;
}
- 编译Build一下项目,就会生成含有上面注解信息的类ExampleAotIndex.java,例如:
/** This class is generated by AOTLoad, do not edit. */
public class ExampleAotIndex implements AotRouterInterface{
/* mock load async data */
public static String EXAMPLE_LOADMOCKDATA = "/Example/LoadMockData";
private final HashMap<String, String> routerMethodMap = new HashMap<String, String>();
private final HashMap<String, Class<?>> routerClassMap = new HashMap<String, Class<?>>();
public ExampleAotIndex() {
routerMethodMap.put(EXAMPLE_LOADMOCKDATA, "loadMockData");
routerClassMap.put(EXAMPLE_LOADMOCKDATA, com.sw.aot.example.ExampleActivity.class );
}
@Override
public HashMap<String, String> getMethodMap() {
return routerMethodMap;
}
@Override
public HashMap<String, Class<?>> getClassMap() {
return routerClassMap;
}
}
- 在应用启动后注入路由表ExampleAotIndex,一般在Application中:
AotLoader.enableLog(true);//是否开始日志
AotLoader.addRouter(new ExampleAotIndex());//注入加载方法的路由表
Debug模式下可以开启日志,开启了以后可以查看任务链的日志信息:
adb logcat -s AOT_LOG
- 现在就可以根据方法路由来提前执行加载任务的方法了,该框架将加载数据的方法抽象为生产和消费的task,是一个典型的生产者消费者模型。 当打开Activity前去生产加载数据的task,执行AotLoader.produce(methodRouter),会根据传人的方法路由名来定位到申明了该注解路由的方法,然后反射调用执行,AotLoader.produce (methodRouter)的返回值是该任务的ID,将ID以参数的形式传给Activity
public static void invoke(Context context){
Intent intent = new Intent(context, ExampleActivity.class);
intent.putExtra(START_AOT_LOAD_ID, AotLoader.produce(ExampleAotIndex.EXAMPLE_LOADMOCKDATA));
context.startActivity(intent);
}
- Activity初始化完成并且View准备好以后,就可以根据传递过来的任务的ID来消费提前加载的数据了:
aotTaskId = getIntent().getStringExtra(START_AOT_LOAD_ID);
if(AotLoader.isValidTask(aotTaskId)){
AotLoader.consume(aotTaskId, listener);
}
- 通过上面的回调就可以收到数据更新了,一旦调用了ResultData.flush()就表示加载完毕,此处就会收到通知
private ResultListener<String> listener = new ResultListener<String>() {
@Override
public void onDataChange(final ResultData<String> data) {
//收到数据,更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(data.getData());
}
});
}
};