Android线程间通信机制
2013-02-21 10:18
337 查看
Android线程间通信机制
当android应用程序运行时,一个主线程被创建(也称作UI线程),此线程主要负责处理UI相关的事件,由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作,如果在非UI线程直接对UI进行了操作,则会报错,另外,对于运算量较大的操作和IO操作,我们需要新开线程来处理这些工作,以免阻塞UI线程,子线程与主线程之间是怎样进行通信的呢?此时就要采用消息循环机制(Looper)与Handler进行处理。一、基本概念
Looper:每一个线程都可以产生一个Looper,用来管理线程的Message,Looper对象会建立一个MessgaeQueue数据结构来存放message。
Handler:与Looper沟通的对象,可以push消息或者runnable对象到MessgaeQueue,也可以从MessageQueue得到消息。
查看其构造函数:
Handler()
Default constructor associates this handler with the queue for the current thread.//如不指定Looper参数则默认利用当前线程的Looper创建
Handler(Looper looper)
Use the provided queue instead of the default one.//使用指定的Looper对象创建Handler
线程A的Handler对象引用可以传递给别的线程,让别的线程B或C等能送消息来给线程A。
线程A的Message Queue里的消息,只有线程A所属的对象可以处理。
注意:Android里没有global的MessageQueue,不同进程(或APK之间)不能通过MessageQueue交换消息。
二、Handler通过Message通信的基本方式
使用Looper.myLooper可以取得当前线程的Looper对象。
使用mHandler = new Handler(Looper.myLooper()); 可产生用来处理当前线程的Handler对象。
使用mHandler = new Handler(Looper.getMainLooper());
可诞生用来处理main线程的Handler对象。
使用Handler传递消息对象时将消息封装到一个Message对象中,Message对象中主要字段如下:
public int | arg1 | 当需要传递的消息是整形时arg1 和 arg2 是一种低成本的可选方案,他使用 setData()/getData()访问或修改字段。 |
public int | arg2 | 同上 |
public Object | obj | 可传送的任意object类型. |
public int | what | Int类型用户自定义的消息类型码 |
Message()
Constructor (but the preferred way to get a Message is to call Message.obtain()).
Handler发出消息时,既可以指定消息被接受后马上处理,也可以指定经过一定时间间隔之后被处理,如sendMessageDelayed(Message msg,
long delayMillis),具体请参考API。
Handler消息被发送出去之后,将由handleMessage(Message msg)方法处理。
注意:在Android里,新诞生一个线程,并不会自动建立其Message Loop
可以通过调用Looper.prepare()为该线程建立一个MessageQueue,再调用Looper.loop()进行消息循环。
下面举例说明:
在main.xml中定义两个button及一个textview
<Button android:id="@+id/a" android:layout_width="80dp" android:layout_height="60dp" android:padding="6dp" android:layout_marginTop="10dp" android:text="Test looper" android:hint="Test looper" /> <Button android:id="@+id/b" android:layout_width="80dp" android:layout_height="60dp" android:padding="6dp" android:layout_marginTop="10dp" android:text="Exit" /> <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp"/>
Activity源代码如下:
1 public class HandlerTestActivity extends Activity implements Button.OnClickListener{ 2 3 public TextView tv; 4 5 private myThread myT; 6 7 Button bt1, bt2; 8 9 /** Called when the activity is first created. */ 10 11 @Override 12 13 public void onCreate(Bundle savedInstanceState) { 14 15 super.onCreate(savedInstanceState); 16 17 setContentView(R.layout.main); 18 19 bt1 = (Button)findViewById(R.id.a); 20 21 bt2 = (Button)findViewById(R.id.b); 22 23 tv = (TextView)findViewById(R.id.tv); 24 25 bt1.setId(1);//为两个button设置ID,此ID用于后面判断是哪个button被按下 26 27 bt2.setId(2); 28 29 bt1.setOnClickListener(this);//增加监听器 30 31 bt2.setOnClickListener(this); 32 33 } 34 35 36 37 @Override 38 39 public void onClick(View v) { 40 41 // TODO Auto-generated method stub 42 43 switch(v.getId()){//按键事件响应,如果是第一个按键将启动一个新线程 44 45 case 1: 46 47 myT = new myThread(); 48 49 myT.start(); 50 51 break; 52 53 case 2: 54 55 finish(); 56 57 break; 58 59 default: 60 61 break; 62 63 } 64 65 } 66 67 68 69 class myThread extends Thread 70 71 { 72 73 private EHandler mHandler; 74 75 public void run() 76 77 { 78 79 Looper myLooper, mainLooper; 80 81 myLooper = Looper.myLooper();//得到当前线程的Looper 82 83 mainLooper = Looper.getMainLooper();//得到UI线程的Looper 84 85 String obj; 86 87 if(myLooper == null)//判断当前线程是否有消息循环Looper 88 89 { 90 91 mHandler = new EHandler(mainLooper); 92 93 obj = "current thread has no looper!";//当前Looper为空,EHandler用mainLooper对象构造 94 95 } 96 97 else 98 99 { 100 101 mHandler = new EHandler(myLooper);//当前Looper不为空,EHandler用当前线程的Looper对象构造 102 103 obj = "This is from current thread."; 104 105 } 106 107 mHandler.removeMessages(0);//清空消息队列里的内容 108 109 Message m = mHandler.obtainMessage(1, 1, 1, obj); 110 111 mHandler.sendMessage(m);//发送消息 112 113 } 114 115 } 116 117 118 119 class EHandler extends Handler 120 121 { 122 123 public EHandler(Looper looper) 124 125 { 126 127 super(looper); 128 129 } 130 131 @Override 132 133 public void handleMessage(Message msg) //消息处理函数 134 135 { 136 137 tv.setText((String)msg.obj);//设置TextView内容 138 139 } 140 141 } 142 143 144 145 }
程序运行后点击TestLooper按键,TextView输出如下,说明新创建的线程里Looper为空,也就说明了新创建的线程并不会自己建立Message Looper。
修改myThread类:
1 class myThread extends Thread 2 3 { 4 5 private EHandler mHandler; 6 7 public void run() 8 9 { 10 11 Looper myLooper, mainLooper; 12 13 Looper.prepare(); 14 15 myLooper = Looper.myLooper(); 16 17 mainLooper = Looper.getMainLooper(); 18 19 ...... 20 21 ...... 22 23 mHandler.sendMessage(m); 24 25 Looper.loop(); 26 27 } 28 29 }
Looper.prepare为当前线程创建一个Message Looper,Looper.loop()开启消息循环。这样修改是OK呢?
答案是否定的!运行时Logcat将抛出CalledFromWrongThreadException异常错误,提示如下:
意思就是说“只有原始创建这个视图层次的线程才能修改它的视图”,本例中的TextView是在UI线程(主线程)中创建,因此,myThread线程不能修改其显示内容!
一般的做法是在子线程里获取主线程里的Handler对象,然后通过该对象向主线程的消息队列里发送消息,进行通信。
如果子线程想修改主线程的UI,可以通过发送Message给主线程的消息队列,主线程经行判断处理再对UI经行操作,具体可以参考之前的代码。
三、Handler通过runnable通信的基本方式
我们可以通过Handler的post方法实现线程间的通信,API中关于post方法说明如下
public
final boolean post (Runnable r)
Causes the Runnable r to be added to the message queue. The runnable
will be run on the thread to which this handler is attached.
Post方法将安排runnable对象在主线程的某个位置运行,但是并不会开启一个新的线程,验证代码如下:
1 public class HandlerTestActivity extends Activity { 2 3 4 5 private Handler handlerTest; 6 7 Runnable runnableTest = new Runnable() 8 9 { 10 11 public void run() 12 13 { 14 15 String runID = String.valueOf(Thread.currentThread().getId());//输出Runnable 线程的ID号 16 17 Log.v("Debug",runID); 18 19 } 20 21 }; 22 23 /** Called when the activity is first created. */ 24 25 @Override 26 27 public void onCreate(Bundle savedInstanceState) { 28 29 super.onCreate(savedInstanceState); 30 31 setContentView(R.layout.main); 32 33 handlerTest = new Handler(); 34 35 String mainID = String.valueOf(Thread.currentThread().getId()); 36 37 Log.v("Debug",mainID);//输出主线程的ID号 38 39 handlerTest.post(runnableTest); 40 41 } 42 43 }
Logcat里输出如下:
说明只是把runnable里的run方法放到UI线程里运行,并不会创建新线程
因此我们可以在子线程中将runnable加入到主线程的MessageQueue,然后主线程将调用runnable的方法,可以在此方法中更新主线程UI。
将之前的代码修改如下:
1 public class HandlerTestActivity extends Activity { 2 3 private Handler handlerTest; 4 5 private TextView tv; 6 7 /** Called when the activity is first created. */ 8 9 @Override 10 11 public void onCreate(Bundle savedInstanceState) { 12 13 super.onCreate(savedInstanceState); 14 15 setContentView(R.layout.main); 16 17 tv = (TextView)findViewById(R.id.tv);//TextView初始化为空 18 19 handlerTest = new Handler(); 20 21 myThread myT = new myThread(); 22 23 myT.start();//开启子线程 24 25 } 26 27 28 29 class myThread extends Thread{ 30 31 public void run(){ 32 33 handlerTest.post(runnableTest);//子线程将runnable加入消息队列 34 35 } 36 37 } 38 39 40 41 Runnable runnableTest = new Runnable() 42 43 { 44 45 public void run() 46 47 { 48 49 tv.setText("此信息由子线程输出!"); 50 51 } 52 53 }; 54 55 }
相关文章推荐
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- ANDROID线程间通信机制
- android中的线程使用与通信机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android线程间通信机制(Handler Looper )
- Android线程间通信机制
- 浅谈Android中的线程的通信及Handle机制
- 大话Android多线程(三) 线程间的通信机制之Handler
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android线程间通信机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- android线程间的通信机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android线程间通信的Message机制
- Android线程间异步通信机制源码分析