Android頁面啟動速度優化工具專案:and-load-aot

heimashi發表於2018-05-10

and-load-aot

專案地址: and-load-aot

  • 在開啟頁面之前,提前去呼叫載入資料的方法(可能是網路資料或者資料庫),這樣頁面開啟以後資料可能已經準備好了,就可以馬上展示給使用者。頁面的初始化一般比較耗時,這樣可以節省掉初始化的時間,並行的去初始化頁面和載入資料,縮短頁面展示給使用者的總時間。
  • 解決思路舉一個例子:例如在開啟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代表著該方法的路由,後面會通過這個路由來呼叫該方法。
    @AOTLoad(router = "/Example/LoadMockData")
    public ResultData<String> loadMockData(){
        final ResultData<String> result = new ResultData<String>();
        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());//注入載入方法的路由表
複製程式碼
  • 現在就可以根據方法路由來提前執行載入任務的方法了,該框架將載入資料的方法抽象為生產和消費的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);
    }
複製程式碼

相關文章