在非UI线程里访问 Android UI toolkit—这个在一个worker线程修改了 View
。这会导致不可预期的结果,而且还难以调试。
为了修复这个问题,Android提供了几个方法从非UI线程访问Android UI toolkit 。详见下面的这个列表:
可以使用 View.post(Runnable)
方法来修改之前的代码:
public void onClick(View v){ new Thread(new Runnable(){ public void run(){ final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable(){ public void run(){ mImageView.setImageBitmap(bitmap); } }); } }).start();}
现在这个方案的线程安全的:这个网络操作在独立线程中完成后,UI线程便会对ImageView
进行操作。
使用 AsyncTask
AsyncTask
能让你在UI上进行异步操作。它在一个worker线程里进行一些阻塞操作然后把结果交给UI主线程,在这个过程中不需要你对线程或者handler进行处理。
使用它,你必须继承 AsyncTask
并实现 doInBackground()
回调方法,这个方法运行在一个后台线程池里面。如果你需要更新UI,那么你应该实现onPostExecute(),
这个方法从 doInBackground()
取出结果,然后在 UI 线程里面运行,所以你可以安全的更新你的UI。你可以通过在UI线程调用 execute()
方法来运行这个任务。
比如,你可以通过使用 AsyncTask
来实现之前的例子:
public void onClick(View v){ new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask<String,Void,Bitmap>{ /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(String... urls){ return loadImageFromNetwork(urls[0]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result){ mImageView.setImageBitmap(result); }}
现在UI是安全的了,代码也更加简单了,因为AsyncTask把worker线程里做的事和UI线程里要做的事分开了。
你应该阅读一下 AsyncTask
的参考文档以便更好的使用它。下面就是一个对 AsyncTask
如何作用的快速的总览:
- 你可以具体设置参数的类型,进度值,任务的终值,使用的范型
-
doInBackground()
方法自动在 worker 线程执行 onPreExecute()
,onPostExecute()
, 和onProgressUpdate()
方法都是在UI线程被调用-
doInBackground()
的返回值会被送往onPostExecute()方法
- 你可以随时在
doInBackground()
方法里面调用publishProgress()
方法来执行UI 线程里面的onProgressUpdate()
方法 - 你可以从任何线程取消这个任务
注意: 你在使用worker线程的时候可能会碰到的另一个问题就是因为runtime configuration change (比如用户改变了屏幕的方向)导致你的activity不可预期的重启,这可能会kill掉你的worker线程。为了解决这个问题你可以参考 Shelves 这个项目。
线程安全的方法
在某些情况下,你实现的方法可能会被多个线程所调用,因此你必须把它写出线程安全的。
转自:http://greenrobot.me/devpost/android-thread-and-process/