使用Rx緩存網絡數據
RxJava是響應式編程, 在異步處理網絡數據時, 使用廣泛.
我們也可以使用一些Rx的特性, 優雅地緩存網絡數據.
緩存模式: 讀取數據庫, 顯示, 請求數據, 存儲到數據庫, 再更新頁面.
使用Dagger2+Retrofit+Rx的標準組合, 我來講解一下如何使用.
GitHub下載地址
1. 框架
常規項目, 包含跳轉緩存和非緩存頁面, 為了模擬慢速環境, 延遲3秒加載數據.
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 跳轉無緩存 public void gotoNoCache(View view) { startActivity(new Intent(this, NocacheActivity.class)); } // 跳轉有緩存 public void gotoCache(View view) { startActivity(new Intent(this, CacheActivity.class)); } }
2. 無緩存
依賴注入三個關鍵部分, Application/Component/Module.
public class RcApplication extends Application { private ApiComponent mApiComponent; @Override public void onCreate() { super.onCreate(); mApiComponent = DaggerApiComponent.builder() .apiModule(new ApiModule(this)).build(); } public ApiComponent getApiComponent() { return mApiComponent; } }
@Singleton @Component(modules = ApiModule.class) public interface ApiComponent { void inject(NocacheActivity activity); void inject(CacheActivity activity); }
@Module public class ApiModule { private Application mApplication; public ApiModule(Application application) { mApplication = application; } @Provides @Singleton public Application provideApplication() { return mApplication; } @Provides @Singleton GitHubClient provideGitHubClient() { return new GitHubClient(); } @Provides ObservableRepoDb provideObservableRepoDb() { return new ObservableRepoDb(mApplication); } }
模塊提供應用信息, GitHub的網絡請求, 數據庫.
@Singleton表示單例模式, 全部注入擁有一個實例.
頁面, 使用RecyclerView顯示列表信息, 在加載時顯示ProgressBar.
/** * 無緩存Activity * <p> * Created by wangchenlong on 16/1/18. */ public class NocacheActivity extends Activity { @Bind(R.id.nocache_rv_list) RecyclerView mRvList; @Bind(R.id.nocache_pb_progress) ProgressBar mPbProgress; @Inject Application mApplication; @Inject GitHubClient mGitHubClient; private ListAdapter mListAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_nocache); ButterKnife.bind(this); ((RcApplication) getApplication()).getApiComponent().inject(this); LinearLayoutManager layoutManager = new LinearLayoutManager(mApplication); mRvList.setLayoutManager(layoutManager); mListAdapter = new ListAdapter(); mRvList.setAdapter(mListAdapter); } @Override protected void onResume() { super.onResume(); // 延遲3秒, 模擬效果 mGitHubClient.getRepos("SpikeKing") .delay(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::onSuccess, this::onError); mPbProgress.setVisibility(View.VISIBLE); } private void onSuccess(ArrayList<Repo> repos) { mListAdapter.setRepos(repos); mPbProgress.setVisibility(View.INVISIBLE); } private void onError(Throwable throwable) { mPbProgress.setVisibility(View.INVISIBLE); } }
通過觀察可以發現, 長時間顯示白屏會降低用戶體驗. 我來看看緩存模式.
3. 緩存
緩存模式: 讀取數據庫, 顯示, 請求數據, 存儲到數據庫, 再更新頁面.
推薦使用腳本生成數據庫處理類, 使用方式參考, 自動生成DbHelper的腳本.
主頁邏輯.
public class CacheActivity extends Activity { @Bind(R.id.cache_rv_list) RecyclerView mRvList; // 列表 @Bind(R.id.cache_srl_swipe) SwipeRefreshLayout mSrlSwipe; // 刷新 @Inject Application mApplication; @Inject ObservableRepoDb mRepoDb; @Inject GitHubClient mGitHubClient; private ListAdapter mListAdapter; // RecyclerView適配器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cache); ButterKnife.bind(this); // 注入類 ((RcApplication) getApplication()).getApiComponent().inject(this); LinearLayoutManager layoutManager = new LinearLayoutManager(mApplication); mRvList.setLayoutManager(layoutManager); mListAdapter = new ListAdapter(); mRvList.setAdapter(mListAdapter); mSrlSwipe.setOnRefreshListener(this::fetchUpdates); } @Override protected void onResume() { super.onResume(); mRepoDb.getObservable() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::setData); fetchUpdates(); Toast.makeText(mApplication, "正在更新", Toast.LENGTH_SHORT).show(); } // 設置數據, 更新完成會調用 private void setData(ArrayList<Repo> repos) { mListAdapter.setRepos(repos); Toast.makeText(mApplication, "更新完成", Toast.LENGTH_SHORT).show(); } private void fetchUpdates() { // 延遲3秒, 模擬效果 mGitHubClient.getRepos("SpikeKing") .delay(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(mRepoDb::insertRepoList, this::fetchError, this::fetchComplete); } private void fetchError(Throwable throwable) { mSrlSwipe.setRefreshing(false); } private void fetchComplete() { mSrlSwipe.setRefreshing(false); } }
數據庫的觀察者
/** * Redo的觀察者 * <p> * Created by wangchenlong on 16/1/18. */ public class ObservableRepoDb { private PublishSubject<ArrayList<Repo>> mPublishSubject; // 發表主題 private RepoDbHelper mDbHelper; // 數據庫 public ObservableRepoDb(Context context) { mDbHelper = new RepoDbHelper(context); mPublishSubject = PublishSubject.create(); } // 返回觀察者 public Observable<ArrayList<Repo>> getObservable() { Observable<ArrayList<Repo>> firstObservable = Observable.fromCallable(this::getRepoList); return firstObservable.concatWith(mPublishSubject); // 連接發表主題 } // 從數據庫獲得數據 private ArrayList<Repo> getRepoList() { mDbHelper.openForRead(); ArrayList<Repo> repos = new ArrayList<>(); Cursor c = mDbHelper.getAllRepo(); if (!c.moveToFirst()) { return repos; // 返回空 } do { // 添加數據 repos.add(new Repo( c.getString(RepoDbHelper.REPO_ID_COLUMN_POSITION), c.getString(RepoDbHelper.REPO_NAME_COLUMN_POSITION), c.getString(RepoDbHelper.REPO_DESCRIPTION_COLUMN_POSITION), new Repo.Owner(c.getString(RepoDbHelper.REPO_OWNER_COLUMN_POSITION), "", "", ""))); } while (c.moveToNext()); c.close(); mDbHelper.close(); return repos; } // 插入Repo列表 public void insertRepoList(ArrayList<Repo> repos) { mDbHelper.open(); mDbHelper.removeAllRepo(); for (Repo repo : repos) { mDbHelper.addRepo( repo.getId(), repo.getName(), repo.getDescription(), repo.getOwner().getLogin() ); } mDbHelper.close(); mPublishSubject.onNext(repos); // 會調用更新數據 } }
這一部分是關鍵, 實現網絡請求同步插入數據庫和更新頁面.
關聯PublishSubject, 在插入數據完成后, 調用綁定觀察者, 更新頁面.
即.concatWith(mPublishSubject)和mPublishSubject.onNext(repos).
Rx在處理網絡請求方面, 確實非常優雅, 值得喜歡完美的人使用.
OK, that’s all! Enjoy it.
來自: http://blog.csdn.net/caroline_wendy/article/details/50540272
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!