Androidはローカルとサーバからデータ表示を引き出します
需要分析
概要設計
スレッドコンテキスト切替の作業はAsyncTask内部のHandler処理に任せ,これで基本的なデータ構造が完成する.
コーディング実装
最下位のDataFetcherモジュール:
上位レベルのテストActivity:
まとめ
ここでは、DataFetcherを1つ追加することで、UIの使用をより便利にします.はAsyncTaskの各バージョンの差異性と互換性があり、並列実行スレッドプール を採用している.メモリ漏洩問題、下層DataFetcherは上層Callbackに対してWeakReferenceパッケージを採用し、Activity回転recreate漏洩のような を避ける.スレッドセキュリティの問題、いくつかの変数はUIスレッドで をできるだけシリアルで実行する.
フレームワークで解決できることは自分で書いたほうがいいです.多く書いてこそ、ソフトウェア設計におけるモジュールと階層の思想を深めることができます.
GankIO App , : , , , , , 。
概要設計
RxJava merge 。 RxJava , ? ?
、 , , , 。
class DataFetcher{
LocalDataFetcher;
RemoteDataFetcher;
}
スレッドコンテキスト切替の作業はAsyncTask内部のHandler処理に任せ,これで基本的なデータ構造が完成する.
コーディング実装
最下位のDataFetcherモジュール:
package com.liguang.datafetcher;
import android.os.AsyncTask;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
*/
public class DataFetcher {
private static final String TAG = "DataFetcher";
/**
* ,
*/
private WeakReference mRef;
private Worker mLocalFetcher;
private Worker mRemoteFetcher;
private String mUrl;
public DataFetcher(String url, Callback callback) {
mRef = new WeakReference<>(callback);
mUrl = url;
//
mLocalFetcher = new Worker(500, new String[]{"D", "E", "F"});
mRemoteFetcher = new Worker(2500, new String[]{"A", "B", "C", "D", "E", "F"});
}
/**
*
*/
public void cancel() {
Log.d(TAG, "cancel: ");
mRef.clear();
mLocalFetcher.cancel(false);
mRemoteFetcher.cancel(false);
}
public void execute() {
Log.d(TAG, "execute: ");
//To avoid AsyncTask's version problems, we schedule our job to parallel executor
mLocalFetcher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mUrl);
mRemoteFetcher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mUrl);
}
private class Worker extends AsyncTask> {
private static final String TAG = "Worker";
private boolean mCompleted;
private long mDelay;
private String[] mMockData;
public Worker(long delay, String[] mockData) {
mDelay = delay;
mMockData = mockData;
}
@Override
protected void onPreExecute() {
mCompleted = false;
}
@Override
protected List doInBackground(String... strings) {
Log.d(TAG, "doInBackground: enter worker " + System.identityHashCode(this));
try {
//
Thread.sleep(mDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "doInBackground: exit worker " + System.identityHashCode(this));
return new ArrayList<>(Arrays.asList(mMockData));
}
@Override
protected void onPostExecute(List data) {
mCompleted = true;
Callback callback = mRef.get();
if (callback == null) {
Log.d(TAG, "onPostExecute: worker complete but high level callback is null");
return;
}
callback.onNext(data);
checkState();
}
@Override
protected void onCancelled() {
Log.d(TAG, "onCancelled: " + System.identityHashCode(this));
}
public boolean isCompleted() {
return mCompleted;
}
}
private void checkState() {
if (mLocalFetcher.isCompleted() && mRemoteFetcher.isCompleted()) {
// Callback
// GC Callback , (onPostExecute)
mRef.get().onComplete();
}
}
interface Callback {
void onNext(List data);
void onComplete();
}
}
上位レベルのテストActivity:
package com.liguang.datafetcher;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity implements DataFetcher.Callback {
private DataFetcher mDataFetcher;
private List mData;
private String mUrl = "https://github.com/passionli";
@BindView(R.id.progressBar)
ProgressBar mProgressBar;
@BindView(R.id.recyclerView)
RecyclerView mRecyclerView;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new MyAdapter();
mRecyclerView.setAdapter(mAdapter);
mProgressBar.setVisibility(View.VISIBLE);
mDataFetcher = new DataFetcher(mUrl, this);
mDataFetcher.execute();
}
@Override
public void onNext(List data) {
if (mData == null) {
mData = new ArrayList<>();
}
//merge
for (String element : data) {
if (!mData.contains(element)) {
mData.add(element);
}
}
//sort
Collections.sort(mData);
mAdapter.notifyDataSetChanged();
}
@Override
public void onComplete() {
mProgressBar.setVisibility(View.GONE);
}
@Override
protected void onDestroy() {
super.onDestroy();
//unregister to low level
mDataFetcher.cancel();
}
class MyAdapter extends RecyclerView.Adapter {
LayoutInflater mInflater;
public MyAdapter() {
mInflater = LayoutInflater.from(MainActivity.this);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(mInflater.inflate(R.layout.list_item, parent, false));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyViewHolder) holder).tv.setText(mData.get(position));
}
@Override
public int getItemCount() {
if (mData == null) {
return 0;
} else {
return mData.size();
}
}
}
class MyViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.tv)
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
まとめ
ここでは、DataFetcherを1つ追加することで、UIの使用をより便利にします.
フレームワークで解決できることは自分で書いたほうがいいです.多く書いてこそ、ソフトウェア設計におけるモジュールと階層の思想を深めることができます.