说明
线程请求数据,ui线程显示数据的实现有很多种途径.这里介绍的是如何修改AsyncTask让其实例能够重复使用.
由于AsyncTask每次执行的时候只能通过new一个实例来调用execute.在项目中使用的时候个人感觉有点难受.
比如如果设计了如下的fragment基类,用于后台请求和ui线程处理的话,如果使用普通AsyncTask就不得不断的new AsyncTask.
public abstract class FragmentBase<AsyncTaskParams, AsyncTaskProgress, AsyncTaskResult> extends Fragment{
protected Context mContext;
private final MyAsync myAsync = new MyAsync();
protected void showLoadingDialog() {
LoadingDialog.showLoading(mContext, getResources().getString(R.string.loading_update_data_hint),true);
}
protected void stopLoadingDialog() {
LoadingDialog.stopLoading();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mContext = context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStop() {
myAsync.cancelAll(true);
super.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@SafeVarargs
protected final void startAsyncTask(AsyncTaskParams... params) {
myAsync.execute(params);
}
protected abstract void onPreExecuteImp();
protected abstract AsyncTaskResult doInBackgroundImp(AsyncTaskParams... params);
protected abstract void onPostExecuteImp(AsyncTaskResult result);
@SuppressLint("StaticFieldLeak")
private class MyAsync extends ReuseAsyncTask<AsyncTaskParams, AsyncTaskProgress, AsyncTaskResult> {
@Override
protected void onPreExecute() {
LogUtils.d("MyAsync--onPreExecute");
showLoadingDialog();
FragmentBase.this.onPreExecuteImp();
}
@SafeVarargs
@Override
protected final AsyncTaskResult doInBackground(AsyncTaskParams... params) {
LogUtils.d("MyAsync--doInBackground thread.name="+Thread.currentThread().getName());
AsyncTaskResult asyncTaskResult = FragmentBase.this.doInBackgroundImp(params);
LogUtils.d("MyAsync--doInBackground after get result");
return asyncTaskResult;
}
@Override
protected void onCancelled() {
stopLoadingDialog();
}
@Override
protected void onPostExecute(AsyncTaskResult result) {
stopLoadingDialog();
if (result == null) {
return;
}
Lifecycle.State currentState = getLifecycle().getCurrentState();
LogUtils.d("FragmentBase--onPostExecute currentState="+currentState);
if (!currentState.isAtLeast(Lifecycle.State.CREATED)) {
return;
}
FragmentBase.this.onPostExecuteImp(result);
}
}
protected void hideKeyBoard(View v) {
InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager.isActive()) {
inputMethodManager.hideSoftInputFromWindow(v.getApplicationWindowToken(), 0);
}
}
}
修改
研究了AsyncTask的源码后你会发现,AsyncTask本身是具有复用潜能的,它没有使用系统级别或hide级别的系统api,所以修改的时候只需要完整复制它的代码即可进行进一步修改.线程池,队列等内在逻辑天生具有复用的可能性.AsyncTask源码大家自己研究下,我这里仅仅贴出我修改后的代码.注意AsyncTask中的有些方法我不需要用到就没做处理.大家可以根据自己的需求进行修改.如对某一次的Future进行关闭操作我没用到就没处理.
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.util.ArrayDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class ReuseAsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "RepeatUseAsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "RepeatUseAsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
private static final String TAG = "ReuseAsyncTask";
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final SerialExecutor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final SerialExecutor sDefaultExecutor = SERIAL_EXECUTOR;
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private /*final*/ FutureTask<Result> mFuture;
private volatile ReuseAsyncTask.Status mStatus = ReuseAsyncTask.Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private final Handler mHandler;
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
/* mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});*/
mTasks.offer(r);
if (mActive == null) {
scheduleNext();
}
}
public synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@linkRepeatUseAsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
private static Handler getMainHandler() {
synchronized (ReuseAsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
private Handler getHandler() {
return mHandler;
}
/* *//** @hide *//*
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}*/
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public ReuseAsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public ReuseAsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public ReuseAsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
/* mFuture = new FutureTask<Result>(mWorker) {
private int mID;
public void setID(int id) {
mID = id;
}
@Override
public void run() {
super.run();
((SerialExecutor)sDefaultExecutor).scheduleNext();
}
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};*/
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
public final ReuseAsyncTask.Status getStatus() {
return mStatus;
}
@WorkerThread
protected abstract Result doInBackground(Params... params);
@MainThread
protected void onPreExecute() {
}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
onCancelled();
}
@MainThread
protected void onCancelled() {
}
public final boolean isCancelled() {
return mCancelled.get();
}
public final boolean cancelAll(boolean mayInterruptIfRunning) {
mCancelled.set(true);
boolean result = true;
for (Runnable runnable : sDefaultExecutor.mTasks) {
MyFutureTask<Result> runnable1 = (MyFutureTask<Result>) runnable;
boolean cancel = runnable1.cancel(mayInterruptIfRunning);
Log.d(TAG, "cancelAll: cancel="+cancel);
result&=cancel;
}
Log.d(TAG, "cancelAll: result="+result);
return result;
}
/* public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}*/
/* public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return mFuture.get(timeout, unit);
}*/
@MainThread
public final ReuseAsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final ReuseAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
/* if (mStatus !=RepeatUseAsyncTask.Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}*/
mStatus = ReuseAsyncTask.Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
// exec.execute(mFuture);
MyFutureTask<Result> futureTask = new MyFutureTask<>(mWorker);
exec.execute(futureTask);
return this;
}
private class MyFutureTask<V extends Result> extends FutureTask<V> {
private int mID;
private boolean mStopNotify = false;
public MyFutureTask(Callable<V> callable) {
super(callable);
}
public MyFutureTask(Runnable runnable, V result) {
super(runnable, result);
}
public void setID(int id) {
mID = id;
}
@Override
public void run() {
super.run();
sDefaultExecutor.scheduleNext();
}
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
public boolean cancel(boolean mayInterruptIfRunning,boolean stopNotify) {
mStopNotify = stopNotify;
return super.cancel(mayInterruptIfRunning);
}
}
@MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = ReuseAsyncTask.Status.FINISHED;
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final ReuseAsyncTask mTask;
final Data[] mData;
AsyncTaskResult(ReuseAsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
}