and-load-aot

Introduction: Load data ahead of time for Android
More: Author   ReportBugs   
Tags:
  • 在打开页面之前,提前去调用加载数据的方法(可能是网络数据或者数据库),这样页面打开以后数据可能已经准备好了,就可以马上展示给用户。页面的初始化一般比较耗时,这样可以节省掉初始化的时间,并行的去初始化页面和加载数据,缩短页面展示给用户的总时间。
  • 解决思路举一个例子:例如在打开 A 页面 Activity 时,在调用 startActivity 方法的时候,就去调用 A 页面的加载数据方法 loadData(),此时开始加载网络数据,同时 Acitivity 开始初始化加载布局等,等 Activity 准备好 UI 需要数据的时候去检查 loadData()是否加载完毕,如果完毕了就直接显示数据,如果没有完成就弹出 loading 开始等待它执行完成。

About

Activity/Fragment/View 的初始化一般都会消耗一些时间,例如:

  • 在打开页面 Activity 时,一般的流程是这样的:

    • 1、通知 AMS 进程去创建新的 Activity
    • 2、AMS 检查 Activity 进程是否存在,不存在先创建进程,已经存在就通知该进程创建 Activity 实例
    • 3、Activity 创建完后加载布局 View
    • 4、然后去网络中或者数据库中异步请求数据
    • 5、数据准备好后通知渲染到 View 上
  • 上面的流程一般是串行的,即要等到 Activity 准备好后再去请求数据,而准备 Activity 的过程往往是耗时的过程(例如启动 Activity 涉及到跨进程、遍历创建 View 树都是耗时的过程),为什么不把这个过程改为并行的呢?甚至改为提前进行呢?

  • 怎样优雅地把创建页面和请求数据并行进行,同时又不改变以前数据请求的调用方式呢?and-load-aot 提供了一种思路:在需要加载数据的方法上添加注解标记,然后利用编译期注解生成页面与加载方法的映射关系,之后就可以在需要提前加载数据的时候调用该方法的路由去提前加载数据了

Usage

详细见ExampleActivity

  • 在项目的 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,例如: ```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 routerMethodMap = new HashMap(); private final HashMap> routerClassMap = new HashMap>();

    public ExampleAotIndex() {

       routerMethodMap.put(EXAMPLE_LOADMOCKDATA, "loadMockData");
       routerClassMap.put(EXAMPLE_LOADMOCKDATA, com.sw.aot.example.ExampleActivity.class );
    

    }

    @Override public HashMap getMethodMap() {

      return routerMethodMap;
    

    }

    @Override public HashMap> getClassMap() {

      return routerClassMap;
    

    }

}



- 在应用启动后注入路由表 ExampleAotIndex,一般在 Application 中:
```java
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());
                  }
              });
    
          }
      };
    
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools