最近我開源了一個專注懶事件的事件匯流排 MailOtto: https://github.com/drakeet/MailOtto 並寫了一個用它來做預載入的實踐案例:第一個頁面預先為第四個頁面發起資料載入請求,等使用者進入第四個頁面,那載入好的資料才會分發給它,若在資料下來前進入第四個頁面,也會等完成的時候自動接收到。
這個資料需要 8 秒,如果進入到第四個頁面才開始載入,體驗就很不好,就算只要 1 秒,也會有一個文字從無到有閃動的過程。如果在第一個頁面停留超過 8 秒,它足夠完成全程預載入,進入第四個頁面裡面就能直接拿到資料,可謂完美預載入。
本文就是來介紹一下這個實踐案例。
先上這個實踐的原始碼,它位於 MailOtto 下的 sample 模組:sample, 演示 apk 下載
MainActivity 作為模擬一個使用者可能停留比較長時間的頁面,這樣我們就可以在這個頁面背後進行為後續的某個頁面內容做預載入。
1 2 3 4 |
public void onPreload(final View view) { ((TextView)view).append(": loading..."); mHandler.postDelayed(new InnerRunnable(view), 8 * 1000); } |
以上程式碼模擬了一個耗時 8 * 1000 毫秒的後臺任務,為了避免 Runnable 匿名內部類引用 MainActivity.this 造成記憶體洩漏,我使用了一個 InnerRunnable 靜態內部類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static class InnerRunnable implements Runnable { WeakReference textViewPreference; public InnerRunnable(View textView) { this.textViewPreference = new WeakReference((TextView) textView); } @Override public void run() { if (textViewPreference.get() != null) { textViewPreference.get().append(": done!"); } Mailbox.getInstance().post(new Mail("A mail from MainActivity", TargetActivity.class)); } } |
在這個靜態內部類中,run 方法會在 8s 後被呼叫,從而將載入下來的資料交給 Mailbox “郵遞員”。
之後路過兩個無謂的 NextActivity:
1 |
startActivity(NextActivity.getIntent(this, /*page = */2)); |
這兩個頁面基本什麼都沒幹,純粹是為了演示跨頁面懶通訊,為什麼要演示這個內容呢,因為這是為了體現 MailOtto 在跨頁面運輸方面天生比 Intent 方式容易得多,而且它支援懶傳送懶接收。
到進入第四個頁面,即 TargetActivity,我們只要告訴“郵遞員”:我在家,送信給我吧!“郵遞員”就會自動呼叫你的 @OnMailReceived:
1 |
Mailbox.getInstance().atHome(this); |
1 2 3 |
@OnMailReceived public void onPreloadDataReady(Mail mail) { mTextView.setText(mail.content.toString()); } |
這時候那載入好的資料才會分發給 TargetActivity,若在資料下來前進入 TargetActivity,也會等完成的時候自動接收到。所以我們不必再進行一堆判斷或重新請求。
另外,為了防止單例造成記憶體洩漏,需要在頁面結束的時候應該對 Mailbox 標示 “我已經離開了”:
1 |
Mailbox.getInstance().leave(this); |
到此為止,我的例子就結束了。當然這個庫絕不僅限於做預載入,只是今天突然發現它天生地適合做預載入,另外它也可以用於取代 startActivityForResult,同時擁有 Otto/EventBus 的功能。