原文地址: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里各由不同的线程执行,它们又如何沟通呢?

更多相关文章

  1. Android UI主线程与子线程
  2. Android中的线程机制
  3. Android应用程序启动Binder线程源码分析
  4. 狂刷Android范例之1:ReadAsset
  5. Android之进程与线程
  6. Android Handler机制 - handleMessage究竟在哪个线程执行
  7. 【Android】进程与线程基本知识

随机推荐

  1. Android 监听U盘插入和拔出并获取U盘文件
  2. Android中FTP上传、下载的功能实现(含进度
  3. Android -- SpannableString
  4. 【Android】MTK Android 修改默认日期时
  5. 极光推送监听点击通知栏
  6. Android(安卓)四大组件之Activity(Activi
  7. android 获取蓝牙已连接设备
  8. Android视频录制小例子
  9. 尝试 Android(安卓)Scripting Environmen
  10. Android第一次app总结