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对象中主要字段如下:

Message对象可以通过Message类的构造函数获得,但Google推荐使用Message.obtain()方法获得,该方法会从全局的对象池里返回一个可复用的Messgae实例,API中解释如下:

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

public class HandlerTestActivity extends Activity implements OnClickListener{

    public TextView tv;
    private myThread myT;
    Button bt1, bt2;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bt1 = (Button)findViewById(R.id.a);
        bt2 = (Button)findViewById(R.id.b);
        tv = (TextView)findViewById(R.id.tv);
        bt1.setId(1);//为两个button设置ID,此ID用于后面判断是哪个button被按下
        bt2.setId(2);
        bt1.setOnClickListener(this);//增加监听器
        bt2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
       switch(v.getId()){//按键事件响应,如果是第一个按键将启动一个新线程
       case 1:
            myT = new myThread();
            myT.start();
            break;
       case 2:
            finish();
            break;
       default:
            break;
       }
    }  

    class myThread extends Thread {
        private EHandler mHandler;
        public void run() {
             Looper myLooper, mainLooper;
             myLooper = Looper.myLooper();//得到当前线程的Looper
             mainLooper = Looper.getMainLooper();//得到UI线程的Looper
             String obj;
             if(myLooper == null) { //判断当前线程是否有消息循环Looper
                  mHandler = new EHandler(mainLooper);
                  obj = "current thread has no looper!";//当前Looper为空,EHandler用mainLooper对象构造
             } else {
              mHandler = new EHandler(myLooper);//当前Looper不为空,EHandler用当前线程的Looper对象构造
              obj = "This is from current thread.";
             }

             mHandler.removeMessages(0);//清空消息队列里的内容
             Message m = mHandler.obtainMessage(1, 1, 1, obj);
             mHandler.sendMessage(m);//发送消息
           }
    }
    class EHandler extends Handler {

        public EHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) { //消息处理函数
           tv.setText((String)msg.obj);//设置TextView内容
        }
    }
}

程序运行后点击TestLooper按键,TextView输出如下,说明新创建的线程里Looper为空,也就说明了新创建的线程并不会自己建立Message Looper。

修改myThread类:

class myThread extends Thread {

    private EHandler mHandler;

    public void run() {
        Looper myLooper, mainLooper;
        Looper.prepare();
        myLooper = Looper.myLooper();
        mainLooper = Looper.getMainLooper();
        ......
        ......
        mHandler.sendMessage(m);
        Looper.loop();
    }

}

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对象在主线程的某个位置运行,但是并不会开启一个新的线程,验证代码如下:

public class HandlerTestActivity extends Activity {

    private Handler handlerTest;

    Runnable runnableTest = new Runnable() {

       public void run() {
           String runID = String.valueOf(Thread.currentThread().getId());//输出Runnable 线程的ID号
           Log.v("Debug",runID);
       }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        handlerTest = new Handler();
        String mainID = String.valueOf(Thread.currentThread().getId());
        Log.v("Debug",mainID);//输出主线程的ID号
        handlerTest.post(runnableTest);
    }
}

Logcat里输出如下:

说明只是把runnable里的run方法放到UI线程里运行,并不会创建新线程

因此我们可以在子线程中将runnable加入到主线程的MessageQueue,然后主线程将调用runnable的方法,可以在此方法中更新主线程UI。

将之前的代码修改如下:

public class HandlerTestActivity extends Activity {

    private Handler handlerTest;
    private TextView tv;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        tv = (TextView)findViewById(R.id.tv);//TextView初始化为空
        handlerTest = new Handler();
        myThread myT = new myThread();
        myT.start();//开启子线程
    }

    class myThread extends Thread{
        public void run(){
            handlerTest.post(runnableTest);//子线程将runnable加入消息队列
        }
    }

    Runnable runnableTest = new Runnable() {

       public void run() {
           tv.setText("此信息由子线程输出!");
       }
    };

}

相当于在主线程中调用了runnalbe的run方法,更改了TextView的UI!

时间: 2024-07-31 03:12:10

Android线程间通信机制的相关文章

线程间通信机制posix匿名信号量

信号量分为两种 一种是简单的信号量,另一种是用于进程间通信的信号量集. 简单的信号量 属于POSIX标准的信号量; 从信号量的命名来看,信号量又可分为命名信号量和匿名(未命名)信号量; 从信号量的值来看,信号量可分为二进制信号量和计数信号量; 1.匿名信号量和命名信号量: 匿名信号量是在内存中分配内存.进行初始化并由系统API进行管理的,它可以在多个线程之间进行资源同步,也可以在多个进程之间进行资源同步,这主要是看在初始化的时候给pshared传递的参数值,为0,则在线程之间同步,非0,则在进程

Android 线程间通信

进程与线程的区别? 在Android中,线程是跑在进程之中的,当手机打开一个APP就相当于打开了一个进程,比如:UI界面的更新,就是在主线程中完成的,我也可以自定义一些子线程来完成所需要的任务. 如何创建线程?创建线程的几种方式? 1.创建一个类继承Thread类 2.创建一个类实现Runnable接口 什么是多线程? 线程是程序中一个单一的顺序控制流程,在程序中同是运行多个线程完成不同的工作,称为多线程 ANR的基础知识及产生? ANR:application not responding :

Android线程间通信更新UI的方法(重点分析EventBus)

Android的UI更新只能在UI线程中,即主线程.子线程中如果要进行UI更新,都是要通知主线程来进行. 几种实现方式总结如下,欢迎补充. 1.runOnUiThread() 子线程中持有当前Activity引用(假如为Activity mActivity;),即可以调用mActivity的runOnUiThread(Runnable r)方法. 2.post()和postDelay() 子线程如果持有某个View的引用,要对该View进行更新,则可调用该View对象的post(Runnable

android 线程间通信原理

要理解原理, read the fucking source 1,从HandlerThread入手. HandlerThread是android系统提供的类,继承Thread,是一个线程.请看run方法. public void run() { mTid = Process.myTid(); Looper.prepare();// #1 synchronized (this) { mLooper = Looper.myLooper();// #2 notifyAll(); } Process.s

android线程间通信的四种实现方式

通过Handler机制. private void one() { handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 123: tv.setText(""+msg.obj); break; } } }; new Thread(){ @Override public void run() {

android线程间通讯

近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下载完成后会发送消息给主线程,这个消息是通过绑定在主线程的Handler来传递的. 在Android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新 概念.我们的主线程(UI线程)就是一个消息循环的线程.针对这种消息循

Android中线程间通信原理分析:Looper,MessageQueue,Handler

自问自答的两个问题 在我们去讨论Handler,Looper,MessageQueue的关系之前,我们需要先问两个问题: 1.这一套东西搞出来是为了解决什么问题呢? 2.如果让我们来解决这个问题该怎么做? 以上者两个问题,是我最近总结出来的,在我们学习了解一个新的技术之前,最好是先能回答这两个问题,这样你才能对你正在学习的东西有更深刻的认识. 第一个问题:google的程序员们搞出这一套东西是为了解决什么问题的?这个问题很显而易见,为了解决线程间通信的问题.我们都知道,Android的UI/Vi

线程间通信与协作方式之——wait-notify机制

大家好,上篇文章为大家介绍了线程间通信和协作的一些基本方式,那这篇文章就来介绍一下经典的wait-notify机制吧. 什么是wait-notify机制? 想象一下有两个线程A.B,如果业务场景中需要这两个线程交替执行任务(比如A执行完一次任务后换B执行,B执行完后再换A执行这样重复交替),之前的基本通信方式只能让线程暂停一段指定时间,Join方法也无法做到这种交替执行的要求,那怎么办呢? 别急,针对这种场景java同样为我们提供了一种经典的线程通信方式--wait-notify机制,这里涉及到

【线程间通信:等待唤醒机制】

在线程安全解决之后,还是一样存在着如下的问题: A:如果消费者先抢到CPU的执行权,就会去消费数据,但是现在的数据是默认值,没有意义,应该等着数据有意义,再消费. B:如果生产者先抢到CPU的执行权,就会去生产数据,但是呢,它生产完数据后,还继续拥有执行权,它又继续产生数据.这是有问题的,它应该等着消费者把数据消费掉,然后再生产. 正常的思路: A:生产者:先看是否有数据,有就等待,没有就生产,生产完后通知消费者来消费数据. B:消费者:先是是否有数据,有就消费,没有就等待,通知生产者生产数据.