分享

记住了不要再backgroundthread里面去做ui的操作,因为GUI是主线程应该完成的内容

 dmw_zgl 2014-11-17

 认识UI Thread(1/2)

 

    介绍过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 Activity implements 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 EventHandler extends 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 Activity implements 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 Activity implements 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 Activity implements 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里各由不同的线程执行,它们又如何沟通呢?

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多