Android主线程与子线程之沟通
原文地址:http://www.android1.net/Topic.aspx?BoardID=11&TopicID=651
介绍过Android主线程与子线程之沟通。所谓主线程通常是UI线程。Android的UI是单线程(Single-threaded)的。为了避免拖住GUI,一些较费时的对象应该交给独立的线程去执行。如果幕后的线程来执行UI对象,Android就会发出错误讯息
CalledFromWrongThreadException
例如下述范例:
//----- Looper_05范例 -----package com.misoo.kx04;import android.app.Activity;import android.content.Context;import android.graphics.Color;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.LinearLayout;import android.widget.TextView; public class ac01 extends Activityimplements OnClickListener { private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; private final int FP = LinearLayout.LayoutParams.FILL_PARENT; public TextView tv; private myThread t; private Button btn, btn2; private Handler h; private Context ctx; public void onCreate(Bundle icicle) { super.onCreate(icicle); ctx =this; LinearLayout layout =new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); btn =new Button(this); btn.setId(101); btn.setBackgroundResource(R.drawable.heart); btn.setText("test looper"); btn.setOnClickListener(this); LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(100,50); param.topMargin = 10; layout.addView(btn, param); btn2 =new Button(this); btn2.setId(102); btn2.setBackgroundResource(R.drawable.ok_blue); btn2.setText("exit"); btn2.setOnClickListener(this); layout.addView(btn2, param); tv =new TextView(this); tv.setTextColor(Color.WHITE); tv.setText(""); LinearLayout.LayoutParams param2 = new LinearLayout.LayoutParams(FP, WC); param2.topMargin = 10; layout.addView(tv, param2); setContentView(layout); //------------------------ t =new myThread(); t.start(); } public void onClick(View v) { switch(v.getId()){ case 101: String obj = "mainThread"; Message m = h.obtainMessage(1, 1, 1, obj); h.sendMessage(m); break; case 102: h.getLooper().quit(); finish(); break; } }//------------------------------------------------ public class EventHandler extends Handler { public EventHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { tv.setText((String)msg.obj); } }//------------------------------------------------ class myThread extends Thread{ final boolean TEST_FLAG =true; public void run() { Looper.prepare(); h = new Handler(){ public void handleMessage(Message msg) { if( TEST_FLAG ) tv.setText("myThread is running"); else { EventHandler ha =new EventHandler(Looper.getMainLooper()); String obj = (String)msg.obj + ", myThread"; Message m = ha.obtainMessage(1, 1, 1, obj); ha.sendMessage(m); } } }; Looper.loop(); } }}
由于指令:
final boolean TEST_FLAG = true;
所以子线程会执行到指令:
tv.setText("myThread is running")
因为tv对象是主线程诞生的UI对象,如果子线程也去插手的话,Android程序就停止了,如下图:
解决方法之一是将指令改为:
final boolean TEST_FLAG = false;
子线程就「不会」执行到指令:
tv.setText("myThread is running")
而是执行到:
EventHandler ha = new EventHandler(Looper.getMainLooper());
String obj = (String)msg.obj + ", myThread";
Message m = ha.obtainMessage(1, 1, 1, obj);
ha.sendMessage(m);
就触发主线程去执行:
public class EventHandlerextends Handler {
…………
public void handleMessage(Message msg) {
tv.setText((String)msg.obj);
}
}
本来就是主线程所诞生tv对象,现在由主线程来执行这个:
tv.setText((String)msg.obj);
指令,Android程序就顺利进行了,输出画面如下:
基本上,Android希望UI thread能够给予User做快速的反应。如果UI thread花费太多时间做幕后的事情,超过5秒钟,Android就会给user如下的提示:
例如,将上述程序的onClick()函数修改如下:
public void onClick(View v) {
switch(v.getId()){
case 101:
try {
Thread.sleep(8000);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
String obj = "mainThread";
Message m = h.obtainMessage(1, 1, 1, obj);
h.sendMessage(m);
break;
case 102:
h.getLooper().quit();
finish();
break;
}
}
UI thread执行到指令:Thread.sleep(8000);
就停下来。Android发现停太久了,就发给user一个提示:
UI thread所执行的每一个函数,所花费的时间都应该越短越好。只要是较费时的工作,都应该交由子线程去执行。那么,UI thread 如何等待子线程做完其工作呢?常用方法是:诞生一个主线程的Handler对象,当做Listener去让子线程能将讯息push到主线程的Message Queue里,以便触发主线程的handleMessage()函数,让主线程知道子线程的状态。
与UI 类别很类似的是IntentReceiver类别,要求其函数工作时间必须很短,而且每一个函数的执行时间是间断的,所以其对象是stateless。基于这样的要求,对于IntentReceiver而言,如果任务(Task)里含有费时的工作,不宜将诞生子线程来单任此费时之工作;而较常用的作法是:由IntentReceiver启动一个Service来担任之。
IntentReceiver最好不要启动一个Activity,以免此Activity被push到Activity 推迭(Stack)上,覆盖掉正在执行的Activity。此时适合用Notification Manager,来作为IntentReceiver与User沟通的管道。
现在,来看看ProgressDialog与线程之运用,如下的典型ProgressDialog范例:
//----- Looper_06范例 -----package com.misoo.pkcc;import android.app.Activity;import android.app.ProgressDialog;import android.graphics.Color;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.LinearLayout;import android.widget.TextView; public class ac01 extends Activityimplements OnClickListener{ private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; private final int FP = LinearLayout.LayoutParams.FILL_PARENT; private ProgressDialog progressDialog =null; public TextView tv; private Button btn, btn2; public void onCreate(Bundle icicle) { super.onCreate(icicle); LinearLayout layout =new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); btn =new Button(this); btn.setId(101); btn.setBackgroundResource(R.drawable.heart); btn.setText("test looper"); btn.setOnClickListener(this); LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(100,50); param.topMargin = 10; layout.addView(btn, param); btn2 =new Button(this); btn2.setId(102); btn2.setBackgroundResource(R.drawable.ok_blue); btn2.setText("exit"); btn2.setOnClickListener(this); layout.addView(btn2, param); tv =new TextView(this); tv.setTextColor(Color.WHITE); tv.setText(""); LinearLayout.LayoutParams param2 = new LinearLayout.LayoutParams(FP, WC); param2.topMargin = 10; layout.addView(tv, param2); setContentView(layout); //------------------------ } public void onClick(View v) { switch(v.getId()){ case 101: progressDialog = ProgressDialog.show(this, "please wait…","Loading",true); new Thread(){ public void run() { try{ sleep(6000); //故意延迟 } catch(Exception e){ e.printStackTrace(); } progressDialog.dismiss(); } }.start(); setTitle("mainThread..."); break; case 102: finish(); break; } }}
指令:
new Thread(){
public void run() {
try{
sleep(6000); //故意延迟
}
catch(Exception e){
e.printStackTrace();
}
progressDialog.dismiss();
}
}.start();
就诞生一个子线程,等到子线程做完事情之后,就执行:progressDialog.dismiss();
而关闭ProgressDialog。其画面为:
此范例是由子线程来执行progressDialog.dismiss();指令。其相当于:
//----- Looper_06aa范例 -----
public class ac01 extends Activityimplements OnClickListener{
//………………(省略)
public void onClick(View v) {
switch(v.getId()){
case 101:
progressDialog = ProgressDialog.show(this,
"please wait…","Loading",true);
th1 =new myThread();
th1.start();
setTitle("mainThread....");
break;
case 102:
finish();
break;
}
}
// ---------------------------------------
class myThread extends Thread {
@Override
public void run() {
try{
sleep(6000); //故意延迟
}
catch(Exception e)
{
e.printStackTrace();
}
progressDialog.dismiss();
}
}
}
上述程序又可写为:
//----- Looper_06bb范例 -----
public class ac01 extends Activity
implements OnClickListener, Runnable{
//………………(省略)
public void onClick(View v) {
switch(v.getId()){
case 101:
progressDialog = ProgressDialog.show(this,
"please wait…","Loading",true);
th1 =new Thread(this);
th1.start();
setTitle("mainThread....");
break;
case 102:
finish();
break;
}
}
public void run() {
try{
Thread.sleep(6000); //故意延迟
}
catch(Exception e)
{
e.printStackTrace();
}
progressDialog.dismiss();
}
}
如果想由主线程来执行progressDialog.dismiss();可藉由Message Queue来传递讯息,如下范例:
//----- Looper_06cc范例 -----package com.misoo.pkcc;import android.app.Activity;import android.app.ProgressDialog;import android.graphics.Color;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.LinearLayout;import android.widget.TextView; public class ac01 extends Activityimplements OnClickListener{ private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; private final int FP = LinearLayout.LayoutParams.FILL_PARENT; private ProgressDialog progressDialog =null; private myThread th1; public TextView tv; private Button btn, btn2; private EventHandler h; public void onCreate(Bundle icicle) { super.onCreate(icicle); LinearLayout layout =new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); btn =new Button(this); btn.setId(101); btn.setBackgroundResource(R.drawable.heart); btn.setText("test looper"); btn.setOnClickListener(this); LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(100,50); param.topMargin = 10; layout.addView(btn, param); btn2 =new Button(this); btn2.setId(102); btn2.setBackgroundResource(R.drawable.ok_blue); btn2.setText("exit"); btn2.setOnClickListener(this); layout.addView(btn2, param); tv =new TextView(this); tv.setTextColor(Color.WHITE); tv.setText(""); LinearLayout.LayoutParams param2 = new LinearLayout.LayoutParams(FP, WC); param2.topMargin = 10; layout.addView(tv, param2); setContentView(layout); //------------------------ } public void onClick(View v) { switch(v.getId()){ case 101: progressDialog = ProgressDialog.show(this, "please wait…","Loading",true); h =new EventHandler(Looper.myLooper()); th1 =new myThread(); th1.start(); break; case 102: finish(); break; } } //------------------------------------------------ public class EventHandler extends Handler { public EventHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { progressDialog.dismiss(); setTitle((String)msg.obj); } } // --------------------------------------- class myThread extends Thread { @Override public void run() { try{ sleep(6000); //故意延迟 } catch(Exception e) { e.printStackTrace(); } String obj = "mainThread...."; Message m = h.obtainMessage(1, 1, 1, obj); h.sendMessage(m); } }}
一开始,画面如下:
接着画面变为:
以上介绍了UI线程及其子线程。除了这些主/子线程之沟通之外,不同的主线程之间也有沟通的机制,例如Activity类别的UI主线程与remote Service主线程之间有如何沟通呢? 还有,Java类别与C++的*.so里各由不同的线程执行,它们又如何沟通呢?
更多相关文章
- Android(安卓)UI主线程与子线程
- Android中对Log日志文件的分析
- adb设备连接以及文件拷贝
- Android中的线程机制
- 比較具体的handle机制
- Android中的消息机制:Handler消息传递机制
- Android(安卓)Handler机制剖析
- Android应用程序启动Binder线程源码分析
- NDK编译Android字符界面的可执行程序