Android之HandlerThread源码分析和简单使用(主线程和子线程通信、子线程和子线程通信)

1、先熟悉handler方式实现主线程和子线程互相通信方式,子线程和子线程的通信方式

      如果不熟悉或者忘记了,请参考我的这篇博客     Android之用Handler实现主线程和子线程互相通信以及子线程和子线程之间的通信     http://blog.csdn.net/u011068702/article/details/75577005

 

 

 

2、贴上简单HandlerThread简单使用(主线程和子线程通信、子线程和子线程通信)的例子

1、activity_main.xml文件

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.handler.MainActivity1" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ParentToChile" />
    <Button
        android:layout_below="@+id/button1"
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ChileToParent" />
    <Button
        android:layout_below="@+id/button2"
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ChileToChile" />

</RelativeLayout>

 

 

 

2、MainActivity.java文件

 

package com.example.handler;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {
	
	public static final String TAG = "HandlerTest";
	public HandlerThread mHandlerThread;
	public Handler mChileHandler;
	public Handler mHandlerCToP = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			int id =  (int) Thread.currentThread().getId();
			Log.d(TAG, "mHandlerCToP currentThread id is:" + id);
		}
	};
	public Button mButtonPtoC;
	public Button mButtonCtoP;
	public Button mButtonCtoC;
	
	public Handler mHandler = new Handler();
	public Handler mHandlerCtoC = null;
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		int id =  (int) Thread.currentThread().getId();
		Log.d(TAG, "onCreate currentThread id is:" + id);
		
		initUIAndThread();
	}
	
	public void initUIAndThread() {
		mHandlerThread = new HandlerThread("chenyu");
		mHandlerThread.start();
		mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
            	int id =  (int) Thread.currentThread().getId();
            	Log.d(TAG, "initThread() mHandler handleMessage currentThread id is:" + id);
    			switch(msg.what) {
					case 0:
						mHandlerCToP.post(new Runnable(){
							@Override
							public void run() {
								int id =  (int) Thread.currentThread().getId();
				            	Log.d(TAG, "initThread() mHandlerCToP post currentThread id is:" + id);
								mButtonPtoC.setText("chenyu");
							}
						});
						break;
					case 1:
						mHandlerCToP.post(new Runnable(){
							@Override
							public void run() {
								int id =  (int) Thread.currentThread().getId();
				            	Log.d(TAG, "initThread() mHandlerCToP post currentThread id is:" + id);
								mButtonCtoC.setText("chenyu");
							}
						});
					default:
						break;
    			}
            }
        };
        
		mButtonPtoC = (Button)findViewById(R.id.button1);
		mButtonCtoP = (Button)findViewById(R.id.button2);
		mButtonCtoC = (Button)findViewById(R.id.button3);
		
		mButtonPtoC.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				int id =  (int) Thread.currentThread().getId();
		        Log.d(TAG, "mButtonPtoC currentThread id is:" + id);
				Log.d(TAG, "mHandlerPToc msg.what is 0");
				mHandler.sendEmptyMessage(0);
			}
		});
		
		mButtonCtoP.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				int id =  (int) Thread.currentThread().getId();
            	Log.d(TAG, "mButtonCtoP currentThread id is:" + id);
				Log.d(TAG, "mHandlerPToc msg.what is 0");
				mHandlerCToP.sendEmptyMessage(0);
			}
		});
		
		mButtonCtoC.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				new Thread(new Runnable(){
					@Override
					public void run() {
						int id =  (int) Thread.currentThread().getId();
		            	Log.d(TAG, "mButtonCtoC currentThread id is:" + id);
						Log.d(TAG, "mHandlerCToc msg.what is 1");
						mHandler.sendEmptyMessage(1);
					}
				}).start();
			}
		});
	}
	
	 @Override
	 protected void onDestroy() {
		 super.onDestroy();
	     mHandlerThread.quit();
	 }    
}

 

 

 

 

 

 

 

 

3、简单分析例子

   1)、原始页面效果

        

    2)、控制台初始化打印的线程ID

       

       分析:在onCreate方法里面打印的主线程的Id为1,有3个按钮,分别是主线程向子线程发消息,子线程向主线程发消息,子线程和子线程发送消息,我们一开始就是对HandlerThread进程初始化,其实它就是一个线程,后面会分析,然后执行了start方法,我们把HandlerThread的looper对象传递给了Handler,之前的文章已分析,一个线程只能有一个looper对象,Handler拥有了HandlerThread的looper对象,就相当于这个Handler在HandlerThread线程同样的线程Id,可以理解为Handler 在子线程里面构建,为什么我这里还有其它的Handler构建,因为想搞清楚在哪里构建属于哪个线程以及子线程的handler是否可以更新UI,主线程构建的handler是否可以更新UI,

    3)、依次点击3个按钮后控制台打印的日志

       

     我们可以看到在onCreate方法里面始化handler的时候传递了一个HandlerThread的looper对象,点击第一个按钮后后没有开启线程,当前线程依然是主线程,handler发送了一个消息,然后初始化的handlMessage收到消息了,也就完成了主线程到子线程的通信,当收到消息的时候,我们发现线程的id是6314,所以这个时候虽然是在onCreate里面构建的handler里面的handlerMessage方法,但是线程Id是和HandlerThread线程Id是一样的,然后初始化handler去更新界面,我们代码是用mHandlerCtopP去更新的,因为它的初始化是在主线程构建的,所以可以post可以更新UI,但是这个时候用拥有HandlerThread的looper对象的handler更新界面就会出问题,和子线程里面的handler去更新界面异常一样,如下图

 

然后点击第二个按钮,是现实子线程向主线程通信,我们发现点击时间里面的线程Id,和handler收到消息的 handleMessage方法里面的线程id, 都是一样,和主线程Id一样,所以我们可以用这个handler直接post来更新UI,

点击第三个按钮,是实现子线程和子线程的通信,执行点击方法,我们开启了一个线程,自然线程Id会和主线程的不一样,为6318, handler收到消息的 handleMessage方法里面的线程id为6314,所以这里可以理解为为子线程线程里面构建了handler,然后用主线程构建的handler更新ui

 

   4)、依次点击3个按钮后手机效果

     

 

 

 

 

4、HandlerThread.java源码分析

    1、上源代码

     

package android.os;

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }


    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }


    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}


1、我们知道这个类在android.os目录下,然后继承了Thread,也就是一个线程

 

2、构造方法会传一个字符串,这里可以随便写,只作为一个标识而已,初始化的时候会调用start方法,然后会执行run()方法

 

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }


先执行了Looper,prepare(),然后给当前mLooper赋值,然后进行Looper.loop(),进行轮寻,这个代码和标准的子线程用handler的方式差不多,也可以理解为这种方式的封装,所以才能显示子线程和子线程和主线程之间的通信,原理都是一样。

 

3、getLooper()中有个wait(),这有什么用呢?因为的mLooper在一个线程中执行创建,而我们的handler是在UI线程中调用getLooper()初始化的,必须等到mLooper创建完成,才能正确的返回。getLooper();wait(),notify()就是为了解决这两个线程的同步问题。

 

4、这里有个退出的方法是

 

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }


我之前写代码的时候,以为停止线程就调用了,mHandlerThread.stop()方法,后面没发现什么问题,直到我们有个功能需要,从控制台下发消息让手机恢复默认出场,函数执行到了这里,导致进程崩溃了,然后就发现这里有问题,需要调用mHandlerThread.quit()方法正常退出。

 

 

 

 

5、总结

      有时还需要频繁更新UI,或则主线程向子线程通信,以及子线程和子线程经常通信的时候,我们可以使用HandlerThread,如果哪里没说清楚,或则讲得有问题,欢迎点评

 

 

 

 

 

 

         

     

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码莎拉蒂 .

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值