Androidはローカルとサーバからデータ表示を引き出します

14048 ワード

需要分析
  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の使用をより便利にします.
  • はAsyncTaskの各バージョンの差異性と互換性があり、並列実行スレッドプール
  • を採用している.
  • メモリ漏洩問題、下層DataFetcherは上層Callbackに対してWeakReferenceパッケージを採用し、Activity回転recreate漏洩のような
  • を避ける.
  • スレッドセキュリティの問題、いくつかの変数はUIスレッドで
  • をできるだけシリアルで実行する.
    フレームワークで解決できることは自分で書いたほうがいいです.多く書いてこそ、ソフトウェア設計におけるモジュールと階層の思想を深めることができます.