歡迎Follow我的GitHub, 關注我的CSDN.
本文的合集已經編著成書,進階Android開發強化實戰,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html
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(, 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(, 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.