在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的,并且这些操作必须在UI线程中执行。在单线程模型中始终要记住两条法则:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后,是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员 。
android提供了几种在其他线程中访问UI线程的方法。
Activity.runOnUiThread( Runnable )
View.post( Runnable )
View.postDelayed( Runnable, long )
Hanlder
如果仅仅是为了更改UI,这些类或方法会使你的代码很复杂很难理解。然而当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕(当然这些技术在不同场合都有它的优点)。
为了解决这个问题,Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要自行另开线程和Handler即可实现。
另外,官方强调了一点:
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.
AsyncTask对线程间通讯进行了包装,提供了简易的编程方式来使后台线程和UI线程进行通讯:后台线程执行异步任务,并把操作结果通知UI线程。
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask是一个泛抽象型类,三个参数可以是任意类型,该类必须实现方法 doInBackground(Params...)。
第一个参数:传入doInBackground()方法的参数类型
第二个参数:传入onProgressUpdate()方法的参数类型,也即是调用publishProgress()方法所放入的参数类型
第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。
以下是执行AsyncTask的四个常用方法,每个方法都对应一个回调方法,开发者不应该手动调用这些方法,开发者需要做的就是实现这些方法。
onPreExecute(),该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
doInBackground(Params...),将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。在此方法中可以调用 publishProgress()方法来更新实时的任务进度。doInBackground(Params...)是抽象方法,子类必须实现,也是该类中唯一一个必须实现的方法。
onProgressUpdate(Progress...),在publishProgress()方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。注意,是用过调用publishProgress()来间接启动该方法。
onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
以下例子的功能为将一个值传入,每隔一秒钟+0.1,并更新至TextView,总共循环10次。
更新前,TextView显示"ready",循环更新完毕后,显示"finish"。
public class MainActivity extends Activity { TextView tv1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv1 = (TextView)findViewById(R.id.tv1); /* * 将需要更改UI的控件传入 * 本例是TextView */ ImageLoader il = new ImageLoader(tv1); /* * 你可以在此放入多个参数 * 这些参数都应该是给doInBackground()处理的 * 本例子中,虽然传入了多个参数 * 但是只处理了第一个参数 */ il.execute(1,22,555,777,88888,45353553); } private class ImageLoader extends AsyncTask<Integer, Double, String> { TextView tv; /* * 重写构造函数 * 接收TextView */ public ImageLoader(TextView tv) { this.tv = tv; } @Override protected void onPreExecute() { tv.setText("ready"); } @Override protected void onProgressUpdate(Double... values) { tv.setText(values[0].toString()); } /* * 这里的Integer参数对应AsyncTask中的第一个参数 * 这里的String返回值对应AsyncTask的第三个参数 * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改 * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作 */ @Override protected String doInBackground(Integer... params) { double num = (double)params[0]; for(int index = 0; index<10; index++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } num = num + (double)0.1; publishProgress( num ); } return "finish"; } /* * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值) * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置 */ @Override protected void onPostExecute(String result) { tv.setText("finish"); } } }
以上部分内容转载或参考来源如下:
http://my.eoe.cn/niunaixiaoshu/archive/1282.html
http://developer.android.com/reference/android/os/AsyncTask.html
在此表示感谢。
转载请注明来源,版权归原作者所有,未经同意严禁用于任何商业用途。
微博:http://weibo.com/theworldsong
邮箱:[email protected]