ndroid异步处理一:使用Thread+Handler实现非UI线程更新UI界面 (入门)
介绍如何使用Thread+Handler的方式从非UI线程发送界面更新消息到UI线程
Android异步处理二:使用AsyncTask异步更新UI界面 (入门)
介绍如何使用AsyncTask异步更新UI界面
Android异步处理三:Handler+Looper+MessageQueue深入详解(进阶)
追踪系统代码,介绍Thread+Handler的实现原理
Android异步处理四:AsyncTask的实现原理(进阶)
追踪系统代码,介绍系统底层AsyncTask的实现原理
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面
概述:每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,则会报错:
CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。
Android为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让Ui线程来进行ui的操作。
对于运算量较大的操作和IO操作,我们需要新开线程来处理这些繁重的工作,以免阻塞ui线程。
例子:下面我们以获取CSDN logo的例子,演示如何使用Thread+Handler的方式实现在非UI线程发送消息通知UI线程更新界面。
ThradHandlerActivity.java:
1 public class ThreadHandlerActivity extends Activity {
2 /** Called when the activity is first created. */
3
4 private static final int MSG_SUCCESS = 0;//获取图片成功的标识
5 private static final int MSG_FAILURE = 1;//获取图片失败的标识
6
7 private ImageView mImageView;
8 private Button mButton;
9
10 private Thread mThread;
11
12 private Handler mHandler = new Handler() {
13 public void handleMessage (Message msg) {//此方法在ui线程运行
14 switch(msg.what) {
15 case MSG_SUCCESS:
16 mImageView.setImageBitmap((Bitmap) msg.obj);//imageview显示从网络获取到的logo
17 Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show();
18 break;
19
20 case MSG_FAILURE:
21 Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show();
22 break;
23 }
24 }
25 };
26
27 @Override
28 public void onCreate(Bundle savedInstanceState) {
29 super.onCreate(savedInstanceState);
30 setContentView(R.layout.main);
31 mImageView= (ImageView) findViewById(R.id.imageView);//显示图片的ImageView
32 mButton = (Button) findViewById(R.id.button);
33 mButton.setOnClickListener(new OnClickListener() {
34
35 @Override
36 public void onClick(View v) {
37 if(mThread == null) {
38 mThread = new Thread(runnable);
39 mThread.start();//线程启动
40 }
41 else {
42 Toast.makeText(getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show();
43 }
44 }
45 });
46 }
47
48 Runnable runnable = new Runnable() {
49
50 @Override
51 public void run() {//run()在新的线程中运行
52 HttpClient hc = new DefaultHttpClient();
53 HttpGet hg = new HttpGet(http://www.linuxidc.com/pic/logo.gif);//获取Linux公社的logo
54 final Bitmap bm;
55 try {
56 HttpResponse hr = hc.execute(hg);
57 bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
58 } catch (Exception e) {
59 mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//获取图片失败
60 return;
61 }
62 mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象
63
64 // mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素
65
66 // mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
67 //
68 // @Override
69 // public void run() {//run()方法会在ui线程执行
70 // mImageView.setImageBitmap(bm);
71 // }
72 // });
73 }
74 };
75
76 }
main.xml布局文件:
77 <?xml version="1.0" encoding="utf-8"?>
78 <LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"
79 android:orientation="vertical" android:layout_width="fill_parent"
80 android:layout_height="fill_parent">
81 <Button android:id="@+id/button" android:text="@string/button_name" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
82 <ImageView android:id="@+id/imageView" android:layout_height="wrap_content"
83 android:layout_width="wrap_content" />
84 </LinearLayout>
strings.xml
85 <?xml version="1.0" encoding="utf-8"?>
86 <LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"
87 android:orientation="vertical" android:layout_width="fill_parent"
88 android:layout_height="fill_parent">
89 <Button android:id="@+id/button" android:text="@string/button_name" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
90 <ImageView android:id="@+id/imageView" android:layout_height="wrap_content"
91 android:layout_width="wrap_content" />
92 </LinearLayout>
Manifest.xml:
93 <?xml version="1.0" encoding="utf-8"?>
94 <manifest xmlns:Android="http://schemas.android.com/apk/res/android"
95 package="com.zhuozhuo"
96 android:versionCode="1"
97 android:versionName="1.0">
98 <uses-sdk android:minSdkVersion="9" />
99 <uses-permission android:name="android.permission.INTERNET"></uses-permission><!--不要忘记设置网络访问权限-->
100
101 <application android:icon="@drawable/icon" android:label="@string/app_name">
102 <activity android:name=".ThreadHandlerActivity"
103 android:label="@string/app_name">
104 <intent-filter>
105 <action android:name="android.intent.action.MAIN" />
106 <category android:name="android.intent.category.LAUNCHER" />
107 </intent-filter>
108 </activity>
109
110 </application>
111 </manifest>
运行结果:
为了不阻塞ui线程,我们使用mThread从网络获取了Linux公社的LOGO
,并用bitmap对象存储了这个Logo的像素信息。
此时,如果在这个线程的run()方法中调用
112 mImageView.setImageBitmap(bm)
会出现:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。原因是run()方法是在新开的线程中执行的,我们上面提到不能直接在非ui线程中操作ui元素。
12下一页 GO
非UI线程发送消息到UI线程分为两个步骤
一、发送消息到UI线程的消息队列
通过使用Handler的
1 Message obtainMessage(int what,Object object)
构造一个Message对象,这个对象存储了是否成功获取图片的标识what和bitmap对象,然后通过message.sendToTarget()方法把这条message放到消息队列中去。
二、处理发送到UI线程的消息
在ui线程中,我们覆盖了handler的
2 public void handleMessage (Message msg)
这个方法是处理分发给ui线程的消息,判断msg.what的值可以知道mThread是否成功获取图片,如果图片成功获取,那么可以通过msg.obj获取到这个对象。
最后,我们通过
3 mImageView.setImageBitmap((Bitmap) msg.obj);
设置ImageView的bitmap对象,完成UI的更新。
补充:
事实上,我们还可以调用
View的post方法来更新ui
4 mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
5
6 @Override
7 public void run() {//run()方法会在ui线程执行
8 mImageView.setImageBitmap(bm);
9 }
10 });
这种方法会把Runnable对象发送到消息队列,ui线程接收到消息后会执行这个runnable对象。
从例子中我们可以看到handler既有发送消息和处理消息的作用,会误以为handler实现了消息循环和消息分发,其实Android为了让我们的代码看起来更加简洁,与UI线程的交互只需要使用在UI线程创建的handler对象就可以了。如需深入学习,了解消息循环机制的具体实现,请关注《Android异步处理三:Handler+Looper+MessageQueue深入详解》
工程打包下载:
免费下载地址在 http://linux.linuxidc.com/
用户名与密码都是www.linuxidc.com
具体下载目录在 /pub/Android源码集锦/2011年/9月/Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面/
上一页12 GO
Android异步处理二:使用AsyncTask异步更新UI界面 (入门)
在《Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面》中,我们使用Thread+Handler的方式实现了异步更新UI界面,这一篇中,我们介绍一种更为简洁的实现方式:使用AsyncTask异步更新UI界面。
概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类。AsyncTask的内部实现是一个线程池,每个后台任务会提交到线程池中的线程执行,然后使用Thread+Handler的方式调用回调函数(如需深入了解原理请看《Android异步处理四:AsyncTask的实现原理》)。
AsyncTask抽象出后台线程运行的五个状态,分别是:1、准备运行,2、正在后台运行,3、进度更新,4、完成后台任务,5、取消任务,对于这五个阶段,AsyncTask提供了五个回调函数:
1、准备运行:onPreExecute(),该回调函数在任务被执行之后立即由UI线程调用。这个步骤通常用来建立任务,在用户接口(UI)上显示进度条。
2、正在后台运行:doInBackground(Params...),该回调函数由后台线程在onPreExecute()方法执行结束后立即调用。通常在这里执行耗时的后台计算。计算的结果必须由该函数返回,并被传递到onPostExecute()中。在该函数内也可以使用publishProgress(Progress...)来发布一个或多个进度单位(unitsof progress)。这些值将会在onProgressUpdate(Progress...)中被发布到UI线程。
3. 进度更新:onProgressUpdate(Progress...),该函数由UI线程在publishProgress(Progress...)方法调用完后被调用。一般用于动态地显示一个进度条。
4. 完成后台任务:onPostExecute(Result),当后台计算结束后调用。后台计算的结果会被作为参数传递给这一函数。
5、取消任务:onCancelled (),在调用AsyncTask的cancel()方法时调用
AsyncTask的构造函数有三个模板参数:
1.Params,传递给后台任务的参数类型。
2.Progress,后台计算执行过程中,进步单位(progress units)的类型。(就是后台程序已经执行了百分之几了。)
3.Result, 后台执行返回的结果的类型。
AsyncTask并不总是需要使用上面的全部3种类型。标识不使用的类型很简单,只需要使用Void类型即可。
例子:与《Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面》实现的例子相同,我们在后台下载CSDN的LOGO,下载完成后在UI界面上显示出来,并会模拟下载进度更新。
Android异步处理系列文章索引 http://www.linuxidc.com/Linux/2011-09/43146.htm
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面
Android异步处理二:使用AsyncTask异步更新UI界面
Android异步处理三:Handler+Looper+MessageQueue深入详解
Android异步处理四:AsyncTask的实现原理
例子工程文件下载
免费下载地址在 http://linux.linuxidc.com/
用户名与密码都是www.linuxidc.com
具体下载目录在 /pub/Android源码集锦/2011年/9月/Android异步处理二:使用AsyncTask异步更新UI界面/
AsyncTaskActivity.java
1 package com.zhuozhuo;
2
3
4 import org.apache.http.HttpResponse;
5 import org.apache.http.client.HttpClient;
6 import org.apache.http.client.methods.HttpGet;
7 import org.apache.http.impl.client.DefaultHttpClient;
8
9 import Android.app.Activity;
10 import android.graphics.Bitmap;
11 import android.graphics.BitmapFactory;
12 import android.os.AsyncTask;
13 import android.os.Bundle;
14 import android.view.View;
15 import android.view.View.OnClickListener;
16 import android.widget.Button;
17 import android.widget.ImageView;
18 import android.widget.ProgressBar;
19 import android.widget.Toast;
20
21 public class AsyncTaskActivity extends Activity {
22
23 private ImageView mImageView;
24 private Button mButton;
25 private ProgressBar mProgressBar;
26
27 @Override
28 public void onCreate(Bundle savedInstanceState) {
29 super.onCreate(savedInstanceState);
30 setContentView(R.layout.main);
31
32 mImageView= (ImageView) findViewById(R.id.imageView);
33 mButton = (Button) findViewById(R.id.button);
34 mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
35 mButton.setOnClickListener(new OnClickListener() {
36
37 @Override
38 public void onClick(View v) {
39 GetCSDNLogoTask task = new GetCSDNLogoTask();
40 task.execute(http://www.linuxidc.com/pic/logo.gif);
41 }
42 });
43 }
44
45 class GetCSDNLogoTask extends AsyncTask<String,Integer,Bitmap> {//继承AsyncTask
46
47 @Override
48 protected Bitmap doInBackground(String... params) {//处理后台执行的任务,在后台线程执行
49 publishProgress(0);//将会调用onProgressUpdate(Integer... progress)方法
50 HttpClient hc = new DefaultHttpClient();
51 publishProgress(30);
52 HttpGet hg = new HttpGet(params[0]);//获取csdn的logo
53 final Bitmap bm;
54 try {
55 HttpResponse hr = hc.execute(hg);
56 bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
57 } catch (Exception e) {
58
59 return null;
60 }
61 publishProgress(100);
62 //mImageView.setImageBitmap(result); 不能在后台线程操作ui
63 return bm;
64 }
65
66 protected void onProgressUpdate(Integer... progress) {//在调用publishProgress之后被调用,在ui线程执行
67 mProgressBar.setProgress(progress[0]);//更新进度条的进度
68 }
69
70 protected void onPostExecute(Bitmap result) {//后台任务执行完之后被调用,在ui线程执行
71 if(result != null) {
72 Toast.makeText(AsyncTaskActivity.this, "成功获取图片", Toast.LENGTH_LONG).show();
73 mImageView.setImageBitmap(result);
74 }else {
75 Toast.makeText(AsyncTaskActivity.this, "获取图片失败", Toast.LENGTH_LONG).show();
76 }
77 }
78
79 protected void onPreExecute () {//在 doInBackground(Params...)之前被调用,在ui线程执行
80 mImageView.setImageBitmap(null);
81 mProgressBar.setProgress(0);//进度条复位
82 }
83
84 protected void onCancelled () {//在ui线程执行
85 mProgressBar.setProgress(0);//进度条复位
86 }
87
88 }
89
90
91 }
12下一页 GO
main.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical" android:layout_width="fill_parent"
4 android:layout_height="fill_parent">
5 <ProgressBar android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal"></ProgressBar>
6 <Button android:id="@+id/button" android:text="下载图片" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
7 <ImageView android:id="@+id/imageView" android:layout_height="wrap_content"
8 android:layout_width="wrap_content" />
9 </LinearLayout>
manifest.xml
10 <?xml version="1.0" encoding="utf-8"?>
11 <manifest xmlns:Android="http://schemas.android.com/apk/res/android"
12 package="com.zhuozhuo"
13 android:versionCode="1"
14 android:versionName="1.0">
15 <uses-sdk android:minSdkVersion="10" />
16 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
17 <application android:icon="@drawable/icon" android:label="@string/app_name">
18 <activity android:name=".AsyncTaskActivity"
19 android:label="@string/app_name">
20 <intent-filter>
21 <action android:name="android.intent.action.MAIN" />
22 <category android:name="android.intent.category.LAUNCHER" />
23 </intent-filter>
24 </activity>
25
26 </application>
27 </manifest>
流程说明:
1、 当点击按钮时,创建一个task,并且传入Linux公社的LOGO地址(String类型参数,因此AsyncTask的第一个模板参数为String类型)
2、 UI线程执行onPreExecute(),把ImageView的图片清空,progrssbar的进度清零。
3、 后台线程执行doInBackground(),不可以在doInBackground()操作ui,调用publishProgress(0)更新进度,此时会调用onProgressUpdate(Integer...progress)更新进度条(进度用整形表示,因此AsyncTask的第二个模板参数是Integer)。函数最后返回result(例子中是返回Bitmap类型,因此AsyncTask的第二个模板参数是Bitmap)。
4、 当后台任务执行完成后,调用onPostExecute(Result),传入的参数是doInBackground()中返回的对象。
总结:
AsyncTask为我们抽象出一个后台任务的五种状态,对应了五个回调接口,我们只需要根据不同的需求实现这五个接口(doInBackground是必须要实现的),就能完成一些简单的后台任务。使用AsyncTask的方式使编写后台进程和UI进程交互的代码变得更为简洁,使用起来更加方便,但是,AsyncTask也有一些缺憾,我们留到以后再讲。
上一页12 GO
Android异步处理三:Handler+Looper+MessageQueue深入详解
在《Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面》中,我们讲到使用Thread+Handler的方式来实现界面的更新,其实是在非UI线程发送消息到UI线程,通知UI线程进行界面更新,这一篇我们将深入学习Android线程间通讯的实现原理。
概述:Android使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口。
例子:在介绍原理之前,我们先介绍Android线程通讯的一个例子,这个例子实现点击按钮之后从主线程发送消息"hello"到另外一个名为” CustomThread”的线程。
Android异步处理系列文章索引 http://www.linuxidc.com/Linux/2011-09/43146.htm
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面
Android异步处理二:使用AsyncTask异步更新UI界面
Android异步处理三:Handler+Looper+MessageQueue深入详解
Android异步处理四:AsyncTask的实现原理
本文代码下载
免费下载地址在 http://linux.linuxidc.com/
用户名与密码都是www.linuxidc.com
具体下载目录在 /pub/Android源码集锦/2011年/9月/Android异步处理三:Handler+Looper+MessageQueue深入详解/
LooperThreadActivity.java
1 package com.zhuozhuo;
2
3 import Android.app.Activity;
4 import android.os.Bundle;
5 import android.os.Handler;
6 import android.os.Looper;
7 import android.os.Message;
8 import android.util.Log;
9 import android.view.View;
10 import android.view.View.OnClickListener;
11
12 public class LooperThreadActivity extends Activity{
13 /** Called when the activity is first created. */
14
15 private final int MSG_HELLO = 0;
16 private Handler mHandler;
17
18 @Override
19 public void onCreate(Bundle savedInstanceState) {
20 super.onCreate(savedInstanceState);
21 setContentView(R.layout.main);
22 new CustomThread().start();//新建并启动CustomThread实例
23
24 findViewById(R.id.send_btn).setOnClickListener(new OnClickListener() {
25
26 @Override
27 public void onClick(View v) {//点击界面时发送消息
28 String str = "hello";
29 Log.d("Test", "MainThread is ready to send msg:" + str);
30 mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//发送消息到CustomThread实例
31
32 }
33 });
34
35 }
36
37
38
39
40
41 class CustomThread extends Thread {
42 @Override
43 public void run() {
44 //建立消息循环的步骤
45 Looper.prepare();//1、初始化Looper
46 mHandler = new Handler(){//2、绑定handler到CustomThread实例的Looper对象
47 public void handleMessage (Message msg) {//3、定义处理消息的方法
48 switch(msg.what) {
49 case MSG_HELLO:
50 Log.d("Test", "CustomThread receive msg:" + (String) msg.obj);
51 }
52 }
53 };
54 Looper.loop();//4、启动消息循环
55 }
56 }
57 }
main.xml
58 <?xml version="1.0" encoding="utf-8"?>
59 <LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"
60 android:orientation="vertical"
61 android:layout_width="fill_parent"
62 android:layout_height="fill_parent"
63 >
64 <TextView
65 android:layout_width="fill_parent"
66 android:layout_height="wrap_content"
67 android:text="@string/hello"
68 />
69 <Button android:text="发送消息" android:id="@+id/send_btn" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
70 </LinearLayout>
12下一页 GO
Log打印结果:
原理:
我们看到,为一个线程建立消息循环有四个步骤:
1、 初始化Looper
2、 绑定handler到CustomThread实例的Looper对象
3、 定义处理消息的方法
4、 启动消息循环
下面我们以这个例子为线索,深入Android源代码,说明Android Framework是如何建立消息循环,并对消息进行分发的。
1、 初始化Looper : Looper.prepare()
Looper.java
1 private static final ThreadLocal sThreadLocal = new ThreadLocal();
2 public static final void prepare() {
3 if (sThreadLocal.get() != null) {
4 throw new RuntimeException("Only one Looper may be created per thread");
5 }
6 sThreadLocal.set(new Looper());
7 }
一个线程在调用Looper的静态方法prepare()时,这个线程会新建一个Looper对象,并放入到线程的局部变量中,而这个变量是不和其他线程共享的(关于ThreadLocal的介绍)。下面我们看看Looper()这个构造函数:
Looper.java
8 final MessageQueue mQueue;
9 private Looper() {
10 mQueue = new MessageQueue();
11 mRun = true;
12 mThread = Thread.currentThread();
13 }
可以看到在Looper的构造函数中,创建了一个消息队列对象mQueue,此时,调用Looper. prepare()的线程就建立起一个消息循环的对象(此时还没开始进行消息循环)。
2、 绑定handler到CustomThread实例的Looper对象 : mHandler= new Handler()
Handler.java
14 final MessageQueue mQueue;
15 final Looper mLooper;
16 public Handler() {
17 if (FIND_POTENTIAL_LEAKS) {
18 final Class<? extends Handler> klass = getClass();
19 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
20 (klass.getModifiers() & Modifier.STATIC) == 0) {
21 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
22 klass.getCanonicalName());
23 }
24 }
25
26 mLooper = Looper.myLooper();
27 if (mLooper == null) {
28 throw new RuntimeException(
29 "Can't create handler inside thread that has not called Looper.prepare()");
30 }
31 mQueue = mLooper.mQueue;
32 mCallback = null;
33 }
Handler通过mLooper = Looper.myLooper();绑定到线程的局部变量Looper上去,同时Handler通过mQueue =mLooper.mQueue;获得线程的消息队列。此时,Handler就绑定到创建此Handler对象的线程的消息队列上了。
3、定义处理消息的方法:Override public void handleMessage (Message msg){}
子类需要覆盖这个方法,实现接受到消息后的处理方法。
4、启动消息循环 : Looper.loop()
所有准备工作都准备好了,是时候启动消息循环了!Looper的静态方法loop()实现了消息循环。
Looper.java
34 public static final void loop() {
35 Looper me = myLooper();
36 MessageQueue queue = me.mQueue;
37
38 // Make sure the identity of this thread is that of the local process,
39 // and keep track of what that identity token actually is.
40 Binder.clearCallingIdentity();
41 final long ident = Binder.clearCallingIdentity();
42
43 while (true) {
44 Message msg = queue.next(); // might block
45 //if (!me.mRun) {
46 // break;
47 //}
48 if (msg != null) {
49 if (msg.target == null) {
50 // No target is a magic identifier for the quit message.
51 return;
52 }
53 if (me.mLogging!= null) me.mLogging.println(
54 ">>>>> Dispatching to " + msg.target + " "
55 + msg.callback + ": " + msg.what
56 );
57 msg.target.dispatchMessage(msg);
58 if (me.mLogging!= null) me.mLogging.println(
59 "<<<<< Finished to " + msg.target + " "
60 + msg.callback);
61
62 // Make sure that during the course of dispatching the
63 // identity of the thread wasn't corrupted.
64 final long newIdent = Binder.clearCallingIdentity();
65 if (ident != newIdent) {
66 Log.wtf("Looper", "Thread identity changed from 0x"
67 + Long.toHexString(ident) + " to 0x"
68 + Long.toHexString(newIdent) + " while dispatching to "
69 + msg.target.getClass().getName() + " "
70 + msg.callback + " what=" + msg.what);
71 }
72
73 msg.recycle();
74 }
75 }
76 }
while(true)体现了消息循环中的“循环“,Looper会在循环体中调用queue.next()获取消息队列中需要处理的下一条消息。当msg != null且msg.target != null时,调用msg.target.dispatchMessage(msg);分发消息,当分发完成后,调用msg.recycle();回收消息。
msg.target是一个handler对象,表示需要处理这个消息的handler对象。Handler的void dispatchMessage(Message msg)方法如下:
Handler.java
77 public void dispatchMessage(Message msg) {
78 if (msg.callback != null) {
79 handleCallback(msg);
80 } else {
81 if (mCallback != null) {
82 if (mCallback.handleMessage(msg)) {
83 return;
84 }
85 }
86 handleMessage(msg);
87 }
88 }
可见,当msg.callback== null 并且mCallback == null时,这个例子是由handleMessage(msg);处理消息,上面我们说到子类覆盖这个方法可以实现消息的具体处理过程。
总结:从上面的分析过程可知,消息循环的核心是Looper,Looper持有消息队列MessageQueue对象,一个线程可以把Looper设为该线程的局部变量,这就相当于这个线程建立了一个对应的消息队列。Handler的作用就是封装发送消息和处理消息的过程,让其他线程只需要操作Handler就可以发消息给创建Handler的线程。由此可以知道,在上一篇《Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面》中,UI线程在创建的时候就建立了消息循环(在ActivityThread的public static final void main(String[] args)方法中实现),因此我们可以在其他线程给UI线程的handler发送消息,达到更新UI的目的。
上一页12 GO
Android异步处理四:AsyncTask的实现原理
在《Android异步处理二:使用AsyncTask异步更新UI界面》一文中,我们介绍了如何使用AsyncTask实现异步下载图片,并且更新图片到UI界面的方法。本篇我们将学习Framework层AsyncTask的实现原理。
概述:AsyncTask的本质是一个线程池,所有提交的异步任务都会在这个线程池中的工作线程内执行,当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler(原理见:《Android异步处理三:Handler+Looper+MessageQueue深入详解
》)传递消息的方式,调用相关的回调函数,从而实现UI界面的更新。
Android异步处理系列文章索引 http://www.linuxidc.com/Linux/2011-09/43146.htm
Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面
Android异步处理二:使用AsyncTask异步更新UI界面
Android异步处理三:Handler+Looper+MessageQueue深入详解
Android异步处理四:AsyncTask的实现原理
例子:
本章还是以《Android异步处理二:使用AsyncTask异步更新UI界面》中的例子说明AsyncTask的实现原理。
这个例子是在后台下载Linux公社的LOGO,下载完成后在UI界面上显示出来。
AsyncTask.java
1 package com.zhuozhuo;
2
3
4 import org.apache.http.HttpResponse;
5 import org.apache.http.client.HttpClient;
6 import org.apache.http.client.methods.HttpGet;
7 import org.apache.http.impl.client.DefaultHttpClient;
8
9 import Android.app.Activity;
10 import android.graphics.Bitmap;
11 import android.graphics.BitmapFactory;
12 import android.os.AsyncTask;
13 import android.os.Bundle;
14 import android.view.View;
15 import android.view.View.OnClickListener;
16 import android.widget.Button;
17 import android.widget.ImageView;
18 import android.widget.ProgressBar;
19 import android.widget.Toast;
20
21 public class AsyncTaskActivity extends Activity {
22
23 private ImageView mImageView;
24 private Button mButton;
25 private ProgressBar mProgressBar;
26
27 @Override
28 public void onCreate(Bundle savedInstanceState) {
29 super.onCreate(savedInstanceState);
30 setContentView(R.layout.main);
31
32 mImageView= (ImageView) findViewById(R.id.imageView);
33 mButton = (Button) findViewById(R.id.button);
34 mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
35 mButton.setOnClickListener(new OnClickListener() {
36
37 @Override
38 public void onClick(View v) {
39 GetCSDNLogoTask task = new GetCSDNLogoTask();
40 task.execute("http://www.linuxidc.com/pic/logo.gif");
41 }
42 });
43 }
44
45 class GetCSDNLogoTask extends AsyncTask<String,Integer,Bitmap> {//继承AsyncTask
46
47 @Override
48 protected Bitmap doInBackground(String... params) {//处理后台执行的任务,在后台线程执行
49 publishProgress(0);//将会调用onProgressUpdate(Integer... progress)方法
50 HttpClient hc = new DefaultHttpClient();
51 publishProgress(30);
52 HttpGet hg = new HttpGet(params[0]);//获取csdn的logo
53 final Bitmap bm;
54 try {
55 HttpResponse hr = hc.execute(hg);
56 bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
57 } catch (Exception e) {
58
59 return null;
60 }
61 publishProgress(100);
62 //mImageView.setImageBitmap(result); 不能在后台线程操作ui
63 return bm;
64 }
65
66 protected void onProgressUpdate(Integer... progress) {//在调用publishProgress之后被调用,在ui线程执行
67 mProgressBar.setProgress(progress[0]);//更新进度条的进度
68 }
69
70 protected void onPostExecute(Bitmap result) {//后台任务执行完之后被调用,在ui线程执行
71 if(result != null) {
72 Toast.makeText(AsyncTaskActivity.this, "成功获取图片", Toast.LENGTH_LONG).show();
73 mImageView.setImageBitmap(result);
74 }else {
75 Toast.makeText(AsyncTaskActivity.this, "获取图片失败", Toast.LENGTH_LONG).show();
76 }
77 }
78
79 protected void onPreExecute () {//在 doInBackground(Params...)之前被调用,在ui线程执行
80 mImageView.setImageBitmap(null);
81 mProgressBar.setProgress(0);//进度条复位
82 }
83
84 protected void onCancelled () {//在ui线程执行
85 mProgressBar.setProgress(0);//进度条复位
86 }
87
88 }
89
90
91 }
main.xml
92 <?xml version="1.0" encoding="utf-8"?>
93 <LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"
94 android:orientation="vertical" android:layout_width="fill_parent"
95 android:layout_height="fill_parent">
96 <ProgressBar android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal"></ProgressBar>
97 <Button android:id="@+id/button" android:text="下载图片" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
98 <ImageView android:id="@+id/imageView" android:layout_height="wrap_content"
99 android:layout_width="wrap_content" />
100 </LinearLayout>
mainifest.xml
101 <?xml version="1.0" encoding="utf-8"?>
102 <manifest xmlns:Android="http://schemas.android.com/apk/res/android"
103 package="com.zhuozhuo"
104 android:versionCode="1"
105 android:versionName="1.0">
106 <uses-sdk android:minSdkVersion="10" />
107 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
108 <application android:icon="@drawable/icon" android:label="@string/app_name">
109 <activity android:name=".AsyncTaskActivity"
110 android:label="@string/app_name">
111 <intent-filter>
112 <action android:name="android.intent.action.MAIN" />
113 <category android:name="android.intent.category.LAUNCHER" />
114 </intent-filter>
115 </activity>
116
117 </application>
118 </manifest>
12下一页 GO
在分析实现流程之前,我们先了解一下AsyncTask有哪些成员变量。
1 private static final int CORE_POOL_SIZE =5;//5个核心工作线程
2 private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程
3 private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒
4
5 private static final BlockingQueue<Runnable> sWorkQueue =
6 new LinkedBlockingQueue<Runnable>(10);//等待队列
7
8 private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
9 MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。
回到例子中,点击按钮之后会新建一个GetCSDNLogoTask对象:
10 GetCSDNLogoTask task = new GetCSDNLogoTask();
此时会调用父类AsyncTask的构造函数:
AsyncTask.java
11 public AsyncTask() {
12 mWorker = new WorkerRunnable<Params, Result>() {
13 public Result call() throws Exception {
14 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
15 return doInBackground(mParams);
16 }
17 };
18
19 mFuture = new FutureTask<Result>(mWorker) {
20 @Override
21 protected void done() {
22 Message message;
23 Result result = null;
24
25 try {
26 result = get();
27 } catch (InterruptedException e) {
28 Android.util.Log.w(LOG_TAG, e);
29 } catch (ExecutionException e) {
30 throw new RuntimeException("An error occured while executing doInBackground()",
31 e.getCause());
32 } catch (CancellationException e) {
33 message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
34 new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
35 message.sendToTarget();//取消任务,发送MESSAGE_POST_CANCEL消息
36 return;
37 } catch (Throwable t) {
38 throw new RuntimeException("An error occured while executing "
39 + "doInBackground()", t);
40 }
41
42 message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
43 new AsyncTaskResult<Result>(AsyncTask.this, result));//完成任务,发送MESSAGE_POST_RESULT消息并传递result对象
44 message.sendToTarget();
45 }
46 };
47 }
WorkerRunnable类实现了callable接口的call()方法,该函数会调用我们在AsyncTask子类中实现的doInBackground(mParams)方法,由此可见,WorkerRunnable封装了我们要执行的异步任务。FutureTask中的protected void done() {}方法实现了异步任务状态改变后的操作。当异步任务被取消,会向UI线程传递MESSAGE_POST_CANCEL消息,当任务成功执行,会向UI线程传递MESSAGE_POST_RESULT消息,并把执行结果传递到UI线程。
由此可知,AsyncTask在构造的时候已经定义好要异步执行的方法doInBackground(mParams)和任务状态变化后的操作(包括失败和成功)。
当创建完GetCSDNLogoTask对象后,执行
48 task.execute("http://www.linuxidc.com/pic/logo.gif");
此时会调用AsyncTask的execute(Params...params)方法
AsyncTask.java
49 public final AsyncTask<Params,Progress, Result> execute(Params... params) {
50 if (mStatus != Status.PENDING) {
51 switch (mStatus) {
52 case RUNNING:
53 throw newIllegalStateException("Cannot execute task:"
54 + " the taskis already running.");
55 case FINISHED:
56 throw newIllegalStateException("Cannot execute task:"
57 + " the taskhas already been executed "
58 + "(a task canbe executed only once)");
59 }
60 }
61
62 mStatus = Status.RUNNING;
63
64 onPreExecute();//运行在ui线程,在提交任务到线程池之前执行
65
66 mWorker.mParams = params;
67 sExecutor.execute(mFuture);//提交任务到线程池
68
69 return this;
70 }
当任务正在执行或者已经完成,会抛出IllegalStateException,由此可知我们不能够重复调用execute(Params...params)方法。在提交任务到线程池之前,调用了onPreExecute()方法。然后才执行sExecutor.execute(mFuture)是任务提交到线程池。
前面我们说到,当任务的状态发生改变时(1、执行成功2、取消执行3、进度更新),工作线程会向UI线程的Handler传递消息。在《Android异步处理三:Handler+Looper+MessageQueue深入详解》一文中我们提到,Handler要处理其他线程传递过来的消息。在AsyncTask中,InternalHandler是在UI线程上创建的,它接收来自工作线程的消息,实现代码如下:
AsyncTask.java
71 private static class InternalHandler extends Handler {
72 @SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
73 @Override
74 public voidhandleMessage(Message msg) {
75 AsyncTaskResult result =(AsyncTaskResult) msg.obj;
76 switch (msg.what) {
77 caseMESSAGE_POST_RESULT:
78 // There is onlyone result
79 result.mTask.finish(result.mData[0]);//执行任务成功
80 break;
81 caseMESSAGE_POST_PROGRESS:
82 result.mTask.onProgressUpdate(result.mData);//进度更新
83 break;
84 caseMESSAGE_POST_CANCEL:
85 result.mTask.onCancelled();//取消任务
86 break;
87 }
88 }
89 }
当接收到消息之后,AsyncTask会调用自身相应的回调方法。
总结:
1、 AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。
2、线程池中的工作线程执行doInBackground(mParams)方法执行异步任务
3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数
上一页12 GO