AsyncTask 在android中是一个轻量级的异步线程,我们使用也比较多,今天就从源码的角度来解析一下AsyncTask。
首先我们看一下怎么使用:
我们从网络上下载一张图片,显示下载的进度:
先把代码贴出来
/** * Created by xxnan on 2016/6/3. */ public class TestAsyncTaskActivity extends AppCompatActivity { private Button begin; private ProgressDialog progressDialog; private MyAsyncTask myAsyncTask; String url="http://f.hiphotos.baidu.com/image/h%3D300/sign=b4b02e92ce177f3e0f34fa0d40cf3bb9/4afbfbedab64034f1d887e22a8c379310a551d48.jpg"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.async); begin=(Button)findViewById(R.id.begindown); begin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myAsyncTask = new MyAsyncTask(TestAsyncTaskActivity.this, progressDialog); myAsyncTask.execute(url); } }); } }
MyAsyncTask.java类:
/** * Created by xxnan on 2016/6/3. */ public class MyAsyncTask extends AsyncTask<String,Integer,Boolean> { private ProgressDialog progressDialog; private Context mContext; public MyAsyncTask(Context context,ProgressDialog progressDialog) { this.progressDialog=progressDialog; mContext=context; } @Override protected void onPreExecute() { super.onPreExecute(); progressDialog=new ProgressDialog(mContext); progressDialog.setTitle("下载进度"); progressDialog.setProgress(0); progressDialog.setMax(100); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.show(); } @Override protected Boolean doInBackground(String... params) { HttpURLConnection httpURLConnection=null; try{ URL url=new URL(params[0]); httpURLConnection=(HttpURLConnection)url.openConnection(); httpURLConnection.setConnectTimeout(3000); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setReadTimeout(3000); int filelegth=0; if(httpURLConnection.getResponseCode()==200) { filelegth=httpURLConnection.getContentLength(); File file=new File("/mnt/sdcard/async/"); if(!file.exists()) file.mkdir(); File f=new File(file.getAbsolutePath(),"qiaoen.jpg"); InputStream inputStream=null; FileOutputStream fileOutputStream=new FileOutputStream(f); inputStream=httpURLConnection.getInputStream(); int len=0; long totalsize=0; byte[] bytes=new byte[1024]; while((len=inputStream.read(bytes))!=-1) { fileOutputStream.write(bytes,0,len); totalsize+=len*100; int progress=(int)(totalsize/filelegth); publishProgress(progress); } } } catch (Exception e) { e.printStackTrace(); return false; }finally { httpURLConnection=null; } return true; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); progressDialog.setProgress(values[0]); } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); progressDialog.dismiss(); if (aBoolean) { ToastUtil.getInstance().showToast(mContext, "下载成功!"); }else ToastUtil.getInstance().showToast(mContext, "下载失败!"); } }
我们看怎么启动一个AsyncTask:
myAsyncTask = new MyAsyncTask(TestAsyncTaskActivity.this, progressDialog);
myAsyncTask.execute(url);
记住启动 AsyncTask必须在主线程,这一点我们也可以从 AsyncTask.java的注解可以看出
我们new了一个MyAsyncTask 继承与AsyncTask并传入了一个字符串,我们来看一下AsyncTask的构造方法:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@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);
}
}
};
}
AsyncTask构造方法总初始化了两个类,一个
WorkerRunnable一个FutureTask,WorkerRunnable简单实现了Callable接口
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); } }; mFuture = new FutureTask<Result>(mWorker) { @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); } } }; }FutureTask初始化的时候把初始化的WorkerRunnable传进去了构造方法如下:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
new完MyAsyncTask然后就执行myAsyncTask.execute(url);,执行的是execute()方法,我们在AsyncTask中找到execute()
如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
继续跟踪executeOnExecutor方法:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { if (mStatus != 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 = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; }
先判断在运行或者已经结束的状态就抛出异常,看到onPreExecute()方法,这事我们熟悉的方法, onPreExecute()在AsyncTask是个空方法,等我们重写。这也是我们熟知的AsyncTask先执行 onPreExecute()方法,此方法还是跑在主线程。
把execute(url)的参数赋给WorkerRunnable实例的Params[]成员变量,再执行exec.execute(mFuture)
public void run() { if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
初始化mFuture的时候将mWorker传入赋值给 mFuture的callable,然后执行callable的call()方法,call()的实现在mworker的初始化中
public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); }
call()方法里面我们又可看到了我们熟悉的doInBackground()方法,此方法是一个抽象方法在子类中我们实现,再执行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 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; } }
跟着finish方法走,执行
如下:
<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'宋体';font-size:12.0pt;"><span style="color:#cc7832;">private void </span><span style="color:#ffc66d;">finish</span>(<span style="color:#507874;">Result </span>result) {
<span style="color:#cc7832;">if </span>(isCancelled()) {
onCancelled(result)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>} <span style="color:#cc7832;">else </span>{
onPostExecute(result)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
<span style="color:#9876aa;">mStatus </span>= Status.<span style="color:#9876aa;"><em>FINISHED</em></span><span style="color:#cc7832;">;
</span>}
如果当前的task已经取消了,那么就不返回结果,否则则执行我们熟悉的onPostExecute方法,此方法也是一个空方法,需要我们在子类中重写。
这里还有两个方法没有讲到那就是publishProgress和onProgressUpdate方法,
我们看一下这两个方法
<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'宋体';font-size:12.0pt;"><span style="color:#cc7832;">protected final void </span><span style="color:#ffc66d;">publishProgress</span>(<span style="color:#507874;">Progress</span>... values) {
<span style="color:#cc7832;">if </span>(!isCancelled()) {
<span style="font-style:italic;">getHandler</span>().obtainMessage(<span style="color:#9876aa;"><em>MESSAGE_POST_PROGRESS</em></span><span style="color:#cc7832;">,
</span><span style="color:#cc7832;"> new </span>AsyncTaskResult<<span style="color:#507874;">Progress</span>>(<span style="color:#cc7832;">this, </span>values)).sendToTarget()<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
}
publishProgress方法可以在子线程中调用发送进度条的值给主线程去更新UI,因而我们可以在 doInBackground()中调用 publishProgress()去更新UI。
case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break;
最终调用到
onProgressUpdate()方法,(一个空的方法,我们在子类中去重写)也是我们熟悉的方法去更新进度条。
到这里就把整个Asynctask类的思路理清楚了,当然里面还有很多东西,有兴趣的可以跟着我这个思路去理一理,有不正确的地方请多多指出,谢谢!