声明:本文根据多篇博客整理,作为学习笔记,非常感谢他们贡献。

每个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线程。


hread+Handler的方式



1.接收

privateHandlermHandler=newHandler(){

publicvoidhandleMessage(Messagemsg){//此方法在ui线程运行

switch(msg.what){

break;

case1: //do something



break;

}

}

};



2.发送

Runnablerunnable=newRunnable()

{

@Override

publicvoidrun()

{

mHandler.obtainMessage(1,obj).sendToTarget();

}

}


使用AsyncTask异步更新UI界面



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类型即可。


实例说明如下:

1 publicclassAsyncTaskActivityextendsActivity{

2

3 privateImageViewmImageView;

4 privateButtonmButton;

5 privateProgressBarmProgressBar;

6

7 @Override

8 publicvoidonCreate(BundlesavedInstanceState){

9 super.onCreate(savedInstanceState);

10 setContentView(R.layout.main);

11

12 mImageView=(ImageView)findViewById(R.id.imageView);

13 mButton=(Button)findViewById(R.id.button);

14 mProgressBar=(ProgressBar)findViewById(R.id.progressBar);

15 mButton.setOnClickListener(newOnClickListener(){

16

17 @Override

18 publicvoidonClick(Viewv){

19 GetCSDNLogoTasktask=newGetCSDNLogoTask();

20 task.execute("http://b.hiphotos.baidu.com/image/pic/item/5ab5c9ea15ce36d334e0c0f238f33a87e850b108.jpg");

21 }

22 });

23 }

24

25 classGetCSDNLogoTaskextendsAsyncTask<String,Integer,Bitmap>{//继承AsyncTask

26

27 @Override

28 protectedBitmapdoInBackground(String...params){//处理后台执行的任务,在后台线程执行

29 publishProgress(0);//将会调用onProgressUpdate(Integer...progress)方法

30 HttpClienthc=newDefaultHttpClient();

31 publishProgress(30);

32 HttpGethg=newHttpGet(params[0]);//获取csdn的logo

33 finalBitmapbm;

34 try{

35 HttpResponsehr=hc.execute(hg);

36 bm=BitmapFactory.decodeStream(hr.getEntity().getContent());

37 }catch(Exceptione){

38

39 returnnull;

40 }

41 publishProgress(100);

42 //mImageView.setImageBitmap(result);不能在后台线程操作ui

43 returnbm;

44 }

45

46 protectedvoidonProgressUpdate(Integer...progress){//在调用publishProgress之后被调用,在ui线程执行

47 mProgressBar.setProgress(progress[0]);//更新进度条的进度

48 }

49

50 protectedvoidonPostExecute(Bitmapresult){//后台任务执行完之后被调用,在ui线程执行

51 if(result!=null){

52 Toast.makeText(AsyncTaskActivity.this,"成功获取图片",Toast.LENGTH_LONG).show();

53 mImageView.setImageBitmap(result);

54 }else{

55 Toast.makeText(AsyncTaskActivity.this,"获取图片失败",Toast.LENGTH_LONG).show();

56 }

57 }

58

59 protectedvoidonPreExecute(){//在doInBackground(Params...)之前被调用,在ui线程执行

60 mImageView.setImageBitmap(null);

61 mProgressBar.setProgress(0);//进度条复位

62 }

63

64 protectedvoidonCancelled(){//在ui线程执行

65 mProgressBar.setProgress(0);//进度条复位

66 }

67

68 }

69

70

71 }




Android线程间通讯 Handler+Looper+MessageQueue 实现


Android使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口。

1 publicclassLooperThreadActivityextendsActivity{

2 /**Calledwhentheactivityisfirstcreated.*/

3

4 privatefinalintMSG_HELLO=0;

5 privateHandlermHandler;

6

7 @Override

8 publicvoidonCreate(BundlesavedInstanceState){

9 super.onCreate(savedInstanceState);

10 setContentView(R.layout.main);

11 newCustomThread().start();//新建并启动CustomThread实例

12

13 findViewById(R.id.send_btn).setOnClickListener(newOnClickListener(){

14

15 @Override

16 publicvoidonClick(Viewv){//点击界面时发送消息

17 Stringstr="hello";

18 Log.d("Test","MainThreadisreadytosendmsg:"+str);

19 mHandler.obtainMessage(MSG_HELLO,str).sendToTarget();//发送消息到CustomThread实例

20

21 }

22 });

23

24 }

25

26

27

28

29

30 classCustomThreadextendsThread{

31 @Override

32 publicvoidrun(){

33 //建立消息循环的步骤

34 Looper.prepare();//1、初始化Looper

35 mHandler=newHandler(){//2、绑定handler到CustomThread实例的Looper对象

36 publicvoidhandleMessage(Messagemsg){//3、定义处理消息的方法

37 switch(msg.what){

38 caseMSG_HELLO:

39 Log.d("Test","CustomThreadreceivemsg:"+(String)msg.obj);

40 }

41 }

42 };

43 Looper.loop();//4、启动消息循环

44 }

45 }

46 }


我们看到,为一个线程建立消息循环有四个步骤:

1、 初始化Looper

2、 绑定handler到CustomThread实例的Looper对象

3、 定义处理消息的方法

4、 启动消息循环

下面我们以这个例子为线索,深入Android源代码,说明Android Framework是如何建立消息循环,并对消息进行分发的。

1、 初始化Looper : Looper.prepare()




1 privatestaticfinalThreadLocalsThreadLocal=newThreadLocal();

2 publicstaticfinalvoidprepare(){

3 if(sThreadLocal.get()!=null){

4 thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");

5 }

6 sThreadLocal.set(newLooper());

7 }

一个线程在调用Looper的静态方法prepare()时,这个线程会新建一个Looper对象,并放入到线程的局部变量中,而这个变量是不和其他线程共享的(关于ThreadLocal的介绍)。下面我们看看Looper()这个构造函数:



1 finalMessageQueuemQueue;

2 privateLooper(){

3 mQueue=newMessageQueue();

4 mRun=true;

5 mThread=Thread.currentThread();

6 }

可以看到在Looper的构造函数中,创建了一个消息队列对象mQueue,此时,调用Looper. prepare()的线程就建立起一个消息循环的对象(此时还没开始进行消息循环)。

2、 绑定handler到CustomThread实例的Looper对象 : mHandler= new Handler()



1 finalMessageQueuemQueue;

2 finalLoopermLooper;

3 publicHandler(){

4 if(FIND_POTENTIAL_LEAKS){

5 finalClass<?extendsHandler>klass=getClass();

6 if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&

7 (klass.getModifiers()&Modifier.STATIC)==0){

8 Log.w(TAG,"ThefollowingHandlerclassshouldbestaticorleaksmightoccur:"+

9 klass.getCanonicalName());

10 }

11 }

12

13 mLooper=Looper.myLooper();

14 if(mLooper==null){

15 thrownewRuntimeException(

16 "Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()");

17 }

18 mQueue=mLooper.mQueue;

19 mCallback=null;

20 }


Handler通过mLooper = Looper.myLooper();绑定到线程的局部变量Looper上去,同时Handler通过mQueue =mLooper.mQueue;获得线程的消息队列。此时,Handler就绑定到创建此Handler对象的线程的消息队列上了。

3、定义处理消息的方法:Override public void handleMessage (Message msg){}

子类需要覆盖这个方法,实现接受到消息后的处理方法。

4、启动消息循环 : Looper.loop()

所有准备工作都准备好了,是时候启动消息循环了!Looper的静态方法loop()实现了消息循环。



1 publicstaticfinalvoidloop(){

2 Looperme=myLooper();

3 MessageQueuequeue=me.mQueue;

4

5 //Makesuretheidentityofthisthreadisthatofthelocalprocess,

6 //andkeeptrackofwhatthatidentitytokenactuallyis.

7 Binder.clearCallingIdentity();

8 finallongident=Binder.clearCallingIdentity();

9

10 while(true){

11 Messagemsg=queue.next();//mightblock

12 //if(!me.mRun){

13 //break;

14 //}

15 if(msg!=null){

16 if(msg.target==null){

17 //Notargetisamagicidentifierforthequitmessage.

18 return;

19 }

20 if(me.mLogging!=null)me.mLogging.println(

21 ">>>>>Dispatchingto"+msg.target+""

22 +msg.callback+":"+msg.what

23 );

24 msg.target.dispatchMessage(msg);

25 if(me.mLogging!=null)me.mLogging.println(

26 "<<<<<Finishedto"+msg.target+""

27 +msg.callback);

28

29 //Makesurethatduringthecourseofdispatchingthe

30 //identityofthethreadwasn'tcorrupted.

31 finallongnewIdent=Binder.clearCallingIdentity();

32 if(ident!=newIdent){

33 Log.wtf("Looper","Threadidentitychangedfrom0x"

34 +Long.toHexString(ident)+"to0x"

35 +Long.toHexString(newIdent)+"whiledispatchingto"

36 +msg.target.getClass().getName()+""

37 +msg.callback+"what="+msg.what);

38 }

39

40 msg.recycle();

41 }

42 }

43 }


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)方法如下:



1 publicvoiddispatchMessage(Messagemsg){

2 if(msg.callback!=null){

3 handleCallback(msg);

4 }else{

5 if(mCallback!=null){

6 if(mCallback.handleMessage(msg)){

7 return;

8 }

9 }

10 handleMessage(msg);

11 }

12 }

可见,当msg.callback== null 并且mCallback == null时,这个例子是由handleMessage(msg);处理消息,上面我们说到子类覆盖这个方法可以实现消息的具体处理过程。


总结:从上面的分析过程可知,消息循环的核心是Looper,Looper持有消息队列MessageQueue对象,一个线程可以把Looper设为该线程的局部变量,这就相当于这个线程建立了一个对应的消息队列。Handler的作用就是封装发送消息和处理消息的过程,让其他线程只需要操作Handler就可以发消息给创建Handler的线程。


更多相关文章

  1. android的消息处理机制(图+源码分析)
  2. 教你如何在 Android 使用多线程下载文件
  3. 又一年对Android消息机制(Handler&Looper)的思考
  4. Android Service——在子线程中更新UI
  5. Android Handler 消息传递机制
  6. Android GLES多线程处理
  7. 学习Android Handler消息传递机制
  8. Android子线程在没有ViewRoot的情况下能刷新UI吗?

随机推荐

  1. unity 关闭弹出android 提示框
  2. andorid 自定义seekbar
  3. Service永不kill掉
  4. Android 三角标签(自定义Textview控件)
  5. android图片下载工具类
  6. Android 使用netty框架实现socket通信
  7. android接收和发送短信的xml配置
  8. Android Google地图那些事
  9. Android 滑动效果代码分享
  10. 一个android访问http资源的便捷工具类—