handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息
1、在主线程创建Handler对象。
2、重写 handlerMessage() 方法。
3、当子线程需要UI操作时,就创建一个Message对象,并通过Handler将这条消息发出去。
4、这条消息被添加到MessageQueue中等待处理,最后分发回handlerMessage中。
5、由于Handler是在主线程创建的,所以此时handlerMessage()方法也在主线程中运行,于是就可以进行UI操作了。
利用Handler修改子线程UI,这里简单演示修改TextView的文字。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView show;
public static final int UPDATE_TEXT = 1;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//接收,判断,处理消息
switch (msg.what) {
case UPDATE_TEXT:
show.setText("更改UI");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//一个Textview,修改文字来达到模拟更改界面UI的效果
show = (TextView) findViewById(R.id.tv_show);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_show:
//开启线程(这里是模拟在线程里要修改UI的情况)
new Thread(new Runnable() {
@Override
public void run() {
//定义一个Message,可以发送给Handler
Message myMessage = new Message();
//用户定义的消息代码,以便接收者能够识别该消息的内容。
myMessage.what = UPDATE_TEXT;
//把myMessage放到消息队列的末尾,它将在handleMessage(Message)中接收
handler.sendMessage(myMessage);
}
});
default:
break;
}
}
}
如果你需要发送数据,那么可以往message里放一个Bundle类型的数据,如下
//设置数据
Bundle bundle = new Bundle();
bundle.putInt("data1", 1);
bundle.putString("data2", "数据二");
message.setData(bundle);
在handleMessage中将数据取出
//取出数据
int data1 = msg.getData().getInt("data1");
String data2 = msg.getData().getString("data2");
如果你不需要设置数据,其实不用像上面例子那样麻烦,使用sendEmptyMessage()
方法可以一行解决,相当于直接发个msg.what给handler了,可以做个对比。
//方法1用三行代码
Message myMessage = new Message();
myMessage.what = UPDATE_TEXT;
handler.sendMessage(myMessage);
//方法2只用一行代码
handler.sendEmptyMessage(UPDATE_TEXT)
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e(TAG, "收到信息");
}
super.handleMessage(msg);
}
};
Timer timer=new Timer();
TimerTask task=new TimerTask(){
@Override
public void run() {
Message message = new Message();
message.what=1;
handler.sendMessage(message);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//延迟5秒发送消息
timer.schedule(task,5000);
}
Handler handler = new Handler();
Runnable mRunnable = new Runnable() {
@Override
public void run() {
Log.e(TAG, "开始工作");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//执行任务
handler.post(mRunnable);
}
每隔一秒执行一次work()
函数
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
Handler handler = new Handler();
Runnable mRunnable = new Runnable() {
@Override
public void run() {
work();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//第一次调用work函数
work();
}
//定义work函数
public void work() {
handler.postDelayed(mRunnable, 1000);
Log.e(TAG, "收到信息");
}
}
HandlerThread
关于HandlerThread的写了另外一篇,感兴趣的可以看下,把耗时的操作放到子线程中去,比如下载文件。而主线程通过收到子线程的各种动态,比如子线程开始下载、子线程下载完成来修改主页面的UI,可以弹个框提示,或者修改TextView文字等等…传送~传送
感谢
https://www.cnblogs.com/wlming/p/5553207.html
==============================================================================================
public class Handler.A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
public class Thread. extends Object implements Runnable.A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.
public class HandlerThread. extends Thread. 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.
①Handler:在android中负责发送和处理消息,通过它可以实现其他支线线程与主线程之间的消息通讯。
②Thread:Java进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。
③HandlerThread:一个继承自Thread的类HandlerThread,Android中没有对Java中的Thread进行任何封装,而是提供了一个继承自Thread的类HandlerThread类,这个类对Java的Thread做了很多便利的封装。
其实这个问题,最主要的关注点还是落在了HandlerThread类上,那么这个类到底有什么作用,所谓的便利封装又体现在哪里?
观察HandlerThread的官方文档的两句:①Thread. Handy class for starting a new thread that has a looper.②The looper can then be used to create handler classes.
释义:HandlerThread对象start后可以获得其Looper对象,并且使用这个Looper对象实例Handler,之后Handler就可以运行在其他线程中了。
那么Handler和Looper到底是什么关系,为什么HandlerThread要做这样的处理?观看下图:
Andriod提供了 Handler 和 Looper 来满足线程间的通信。 Handler 先进先出原则。 Looper 类用来管理特定线程内对象之间的消息交换 (MessageExchange) 。
1)Looper: 一个线程可以产生一个 Looper 对象,由它来管理此线程里的 MessageQueue( 消息队列 ) 和对消息进行循环。
2)Handler: 你可以构造 Handler 对象来与 Looper 沟通,以便 push 新消息到 MessageQueue 里 ; 或者接收 Looper 从 Message Queue 取出 所送来的消息。
3) Message Queue( 消息队列 ): 用来存放线程放入的消息。
4) Message:是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想传递的消息。
看到这里就明白了为什么:如果一个线程要处理消息,那么它必须拥有自己的Looper,并不是Handler在哪里创建,就可以在哪里处理消息。
注:对应关系Thread(1):Looper(1):MessageQueen(1):Handler(n).
正如前面所说,线程间通信的时候,比如Android中常见的更新UI,涉及到的是子线程和主线程之间的通信,实现方式就是Handler+Looper,但是要自己手动操作Looper,不推荐,所以谷歌封装了HandlerThread类(类似于AsyncTask类)。
上代码,具体实现:
public class MainActivity extends AppCompatActivity {
Handler mainHandler,workHandler;
HandlerThread mHandlerThread;
TextView text;
Button button1,button2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text1);
// 创建与主线程关联的Handler
mainHandler = new Handler();
/**
* 步骤①:创建HandlerThread实例对象
* 传入参数 = 线程名字,作用 = 标记该线程
*/
mHandlerThread = new HandlerThread("handlerThread");
/**
* 步骤②:启动线程
*/
mHandlerThread.start();
/**
* 步骤③:创建工作线程Handler & 复写handleMessage()
* 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
* 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
*/
workHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg)
{
//设置了两种消息处理操作,通过msg来进行识别
switch(msg.what){
case 1:
try {
//延时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通过主线程Handler.post方法进行在主线程的UI更新操作
mainHandler.post(new Runnable() {
@Override
public void run () {
text.setText("第一次执行");
}
});
break;
case 2:
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainHandler.post(new Runnable() {
@Override
public void run () {
text.setText("第二次执行");
}
});
break;
default:
break;
}
}
};
/**
* 步骤④:使用工作线程Handler向工作线程的消息队列发送消息
* 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
*/
button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = Message.obtain();
msg.what = 1; //消息的标识
msg.obj = "A"; // 消息的存放
// 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
}
});
button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = Message.obtain();
msg.what = 2;
msg.obj = "B";
workHandler.sendMessage(msg);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit(); // 退出消息循环
workHandler.removeCallbacks(null); // 防止Handler内存泄露 清空消息队列
}
}
从上面代码可以看出,HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,然后在内部直接实现了Looper的实现,这是Handler消息机制必不可少的。有了自己的looper,可以让我们在自己的线程中分发和处理消息。如果不用HandlerThread的话,需要手动去调用Looper.prepare()和Looper.loop()这些方法。
// 子线程中创建新的Handler 没有使用HandlerThread
new Thread () {
@Override
public void run() {
Looper.prepare();
Hnadler handler = new Handler();
Looper.loop();
}
}
提供一些其他Android消息机制分析,帮助理解读者理解:
①Handler是Android消息机制的上层接口,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行,该线程既可以是主线程,也可以是子线程,要看构造Handler时使用的构造方法中传入的Looper位于哪里;
②Handler的运行需要底层的MessageQueue和Looper的支撑,Handler创建的时候会采用线程的Looper来构造消息循环系统,而线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper;
③上述代码中的第一个Handler-mainHandler,实例化的时候,直接在onCreate()方法中new出了实例,其实是其已经在主线程中了,主线程-ActivityThread,ActivityThread被创建时就会初始化Looper,这就是主线程中默认可以直接使用Handler的原因;
④上述代码中的第二个Handler-workHandler,它在实例化的时候,参数传入了 mHandlerThread.getLooper() ,注意,这个Handler使用的就不是主线程的Looper了,而是子线程的Looper,HandlerThread在调用start()方法之后,就可以获取到子线程的Looper,然后将其传入workHandler的构造方法中,那么此时的workHandler就会运行在子线程中,用于处理耗时操作。
⑤Handler的工作原理:Handler创建时会采用线程的Looper来构建内部消息循环系统,如果当前线程没有Looper,那么就会报错“Can`t create handler inside thread that has not called Looper.prepare()”解决方法有两个:为当前线程创建Looper即可,像上述代码中workHandler,或者在一个有Looper的线程中创建Handler也行,就像上述代码中的mainHandler一样;
⑥调用Handler的post方法会将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法来发送一个消息,这个消息同样会在Looper中去处理。其实post方法最终也是通过send方法来完成的。每当Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable的run方法或者Handler的handleMessage方法就会被调用。注意Looper是运行在创建Handler所在的线程中的,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中去执行了;
⑦Looper的工作原理:Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。注意关注一些重要的Looper的方法:
Looper.prepare()-为当前线程创建一个Looper;
Looper.loop()-开启消息循环,只有调用该方法,消息循环系统才会开始循环;
Looper.prepareMainLooper()-为主线程也就是ActivityThread创建Looper使用;
Looper.getMainLooper()-通过该方法可以在任意地方获取到主线程的Looper;
Looper.quit() Looper.quitSafely()-退出Looper,自主创建的Looper建议在不使用的时候退出
⑧ActivityThread主线程通过ApplicationThread和AMS进行进程间通信
=================================================================================================
Handler在android线程编程中非常常见。
每个线程只有一个Looper来管理消息队列,handler在使用的时候需要绑定到对应的Looper上。Handler给自己绑定的Looper不断的发送消息,Thread对Looper来做死循环来不断读取MessageQueue队列中的消息,发送给handler来进行处理。
Android的UI是运行在主线程中,主线程是用MainLooper来管理,循环读取MessageQueue队列中消息的,如果创建Handler对象new Handler( )时构造的时候参数没有指定绑定的Looper,默认是和主线程的Looper绑定在一起。Handler发送和处理消息是默认在主线程中进行的。
应用中和UI是在主线程中进行绘制的,为了保证用户和UI交互的流畅,软件中常常耗时的动作,如网络的操作、IO的读取、数据的处理等单独的放在子线程中去处理。比如通过异步的获取数据,获取完成后通过使用主线程的handler来发送msg给主线程的MainLooper队列来通知主线程再进行UI刷新。
Android原生的HandlerThread为我们提供了一种思路。
HandlerThread实质是Android封装的一个Thread。
1、new一个HandlerThread对象,实质是创建线程,然后必须通过start()方法把此线程运行起来;
2、new 一个Handler对象,在构造Handler对象的时候,把此thread的Looper作为参数传递给我们构造的Handler对象;这样我们创建的Handler对象就和此thread线程的Looper绑定起来;
3、此时我们创建的Handler对象就可以给thread的MessageQueue队列发送消息和处理消息了,处理消息是运行在子线程中,可以做耗时的操作,不会阻塞UI线程。等在子线程做完耗时的动作获取完数据后就可以通过主线程的handler发消息给主线程来更新当前UI界面;
4、用完后,需要来停止此thread的Looper循环,防止内存泄露。参考实例如下:
1、创建需要的变量
private BtSwitchHandler btSwitchHandler;
private HandlerThread checkBtSwitchThread;
private long checkBtSwitchDelayTime = 45 * 1000;
private static final int MSG_CHECK_BT_SWITCH = 0x20;
2、开始构造HandlerThread对象和构造Handler对象,并发送Message消息
private void checkBtSwitchIsOpenThread()
{
L.d(TAG, "checkBtSwitchIsOpenThread start");
if (checkBtSwitchThread == null)
checkBtSwitchThread = new HandlerThread("checkBtSwitch");
checkBtSwitchThread.start();
if (btSwitchHandler == null)
btSwitchHandler = new BtSwitchHandler(checkBtSwitchThread.getLooper());
btSwitchHandler.removeCallbacksAndMessages(null);
btSwitchHandler.sendEmptyMessageDelayed(MSG_CHECK_BT_SWITCH, checkBtSwitchDelayTime);
}
3、创建内部类Handler类处理消息,运行在子线程
private class BtSwitchHandler extends Handler
{
public BtSwitchHandler(Looper looper)
{
super(looper);
}
@Override
public void handleMessage(Message msg)
{
if (msg.what == MSG_CHECK_BT_SWITCH)
{
if (mBluetooth.isEnabled())
{
L.d(TAG, "bt open success");
} else
{
L.d(TAG, "bt switch open 45s timeout, show failer view");
onekeystate = ONEKEYSTATE.ERROR;
Message errMsg = mHandler.obtainMessage(MSG_ERROR);
mHandler.sendMessage(errMsg);
}
stopBtSwitchThread();
}
super.handleMessage(msg);
}
}
4、注销HandlerThread,销毁loop循环
private void stopBtSwitchThread()
{
if (checkBtSwitchThread != null)
{
Looper btSwitchLooper = checkBtSwitchThread.getLooper();
if (btSwitchLooper != null)
{
btSwitchLooper.quit();
}
}
checkBtSwitchThread = null;
btSwitchHandler = null;
}
===============================================================================================
官方解释
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.
方便我们创建一个拥有looper的线程
HandlerThread继承自Thread,因此HandlerThread其实就是一个线程。
线程开启时也就是run方法运行起来后,线程同时创建一个含有消息队列的looper,并对外提供自己这个对象的get方法,这就是和普通的Thread不一样的地方。可用于多个耗时任务要串行执行。
如果对handler不熟的话,可以先看这个Handler介绍
使用流程
实例对象,参数为线程名字
HandlerThread handlerThread = new HandlerThread("handlerThread");
启动线程
handlerThread.start();
实例主线程的Handler,参数为HandlerThread内部的一个looper.
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
看个简单的例子
里面打了挺多的注释,Log日志也用序号标明了顺序。这里模拟的情况是我们在子线程下载东西,然后和主线程之间进行通信。主线程知道了下载开始和下载结束的时间,也就能及时改变界面UI。
首先是DownloadThread类,继承于HandlerThread,用于下载。
public class DownloadThread extends HandlerThread{
private static final String TAG = "DownloadThread";
public static final int TYPE_START = 2;//通知主线程任务开始
public static final int TYPE_FINISHED = 3;//通知主线程任务结束
private Handler mUIHandler;//主线程的Handler
public DownloadThread(String name) {
super(name);
}
/*
* 执行初始化任务
* */
@Override
protected void onLooperPrepared() {
Log.e(TAG, "onLooperPrepared: 1.Download线程开始准备");
super.onLooperPrepared();
}
//注入主线程Handler
public void setUIHandler(Handler UIhandler) {
mUIHandler = UIhandler;
Log.e(TAG, "setUIHandler: 2.主线程的handler传入到Download线程");
}
//Download线程开始下载
public void startDownload() {
Log.e(TAG, "startDownload: 3.通知主线程,此时Download线程开始下载");
mUIHandler.sendEmptyMessage(TYPE_START);
//模拟下载
Log.e(TAG, "startDownload: 5.Download线程下载中...");
SystemClock.sleep(2000);
Log.e(TAG, "startDownload: 6.通知主线程,此时Download线程下载完成");
mUIHandler.sendEmptyMessage(TYPE_FINISHED);
}
}
然后是MainActivity部分。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private DownloadThread mHandlerThread;//子线程
private Handler mUIhandler;//主线程的Handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化,参数为线程的名字
mHandlerThread = new DownloadThread("mHandlerThread");
//调用start方法启动线程
mHandlerThread.start();
//初始化Handler,传递mHandlerThread内部的一个looper
mUIhandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//判断mHandlerThread里传来的msg,根据msg进行主页面的UI更改
switch (msg.what) {
case DownloadThread.TYPE_START:
//不是在这里更改UI哦,只是说在这个时间,你可以去做更改UI这件事情,改UI还是得在主线程。
Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");
break;
case DownloadThread.TYPE_FINISHED:
Log.e(TAG, "7.主线程知道Download线程下载完成了...这时候可以更改主界面UI,收工");
break;
default:
break;
}
super.handleMessage(msg);
}
};
//子线程注入主线程的mUIhandler,可以在子线程执行任务的时候,随时发送消息回来主线程
mHandlerThread.setUIHandler(mUIhandler);
//子线程开始下载
mHandlerThread.startDownload();
}
@Override
protected void onDestroy() {
//有2种退出方式
mHandlerThread.quit();
//mHandlerThread.quitSafely(); 需要API>=18
super.onDestroy();
}
}
运行的Log日志如下
例子:
HandlerThread handlerThread = new HandlerThread("handlerThread"){
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
Log.d("执行","准备工作");
}
};
handlerThread.start();
//必须在调用handlerThread.start()之后才能创建Handler
final Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("消息","在 "+Thread.currentThread().getName()+" 线程,收到 "+msg.obj);
}
};
handler.sendMessage(Message.obtain(handler,1,"test1"));
使用相对比较简单,总结如下:
创建HandlerThread对象,可以重写onLooperPrepared()方法做一些初始化操作;
调用handlerThread.start()方法;
创建Handler,在构造中传入handlerThread.getLooper()方法创建的Looper对象,重写handleMessage(Message msg)方法,在子线程处理消息;
使用Handler对象在相关场景发送处理消息;
适时退出异步操作
原理
本身HandlerThread的源码比较少,直接贴出:
//本身继承了Thread类
public class HandlerThread extends Thread {
int mPriority;//线程优先级
int mTid = -1;//线程id
Looper mLooper;//Looper对象
private @Nullable Handler mHandler;//无法调用
//name为线程的名字
public HandlerThread(String name) {
super(name);
//线程的优先级,这是默认级别,其他的级别在Process中有存储
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//可以传入优先级的构造,优先级来自android.os.Process类中
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
//可以重写此方法做准备工作
protected void onLooperPrepared() {
}
//在调用HandlerThread.start()方法后,run方法会运行
//这里主要是进行了Looper对象的创建和初始化
@Override
public void run() {
mTid = Process.myTid();//获取线程id
Looper.prepare();//准备当前线程的Looper,使用了ThreadLocal存储
synchronized (this) {//进入同步代码块
mLooper = Looper.myLooper();//获取当前线程Looper
notifyAll();//通知线程
}
Process.setThreadPriority(mPriority);//设置线程优先级
onLooperPrepared();//调用准备方法
Looper.loop();//开启消息循环
mTid = -1;//重置线程ID
}
//主要用于获取当前线程关联的Looper对象,用于创建Handler使用
public Looper getLooper() {
//如果线程没有存活,直接返回Null
if (!isAlive()) {
return null;
}
//进入同步代码
synchronized (this) {
//如果线程存活,但是Looper对象还没初始化成功的时候,wait,等待Looper初始化完毕后唤醒
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
//此方法标记为@hide,外部无法直接调用
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
//调用该方法,Looper对象中的MessageQueue存储的Messag,将全部被情况
//无论是不是延迟消息都将被移除,如果想要保障非延迟消息执行的话,那么使用quitSafely()方法
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//和quit()方法的区别就是只有延迟方法才会被移除,当前的消息会被处理完成 API18支持
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
//获取线程Id
public int getThreadId() {
return mTid;
}
}
其实整个HandlerThread做的工作就是在Thread中封装了Looper对象,以此在创建的Handler中能够进行异步的消息处理。
如果有耗时操作可以考虑使用HandlerThread机制。
创建HandlerThread后必须首先调用start方法,以此来准备子线程需要的Looper对象,之后再创建Handler对象时,才能在构造方法中调用new Handler(handlerThread.getLooper())来使用Looper对象,注意创建的先后顺序。
参考文章:
Android中Looper的quit方法和quitSafely方法
=========================================================================================
为子线程创建Handler
Handler和线程通过Looper挂钩;所有在UI线程中创建的Handler不需要指定Looper,默认设置了MainLooper;所有在子线程中创建的Handler需要通过Loop.prepare()和Loop.loop()来设置子线程的Looper;一个线程只有一个Looper。
public class MainActivity extends AppCompatActivity {
private String TAG = "testHandler";
private Button testBtn;
class MyThread extends Thread {
public Handler myThreadHandler;
@Override
public void run() {
super.run();
Looper.prepare();
myThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e(TAG, "msg.what:" + msg.what);
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testBtn = (Button) findViewById(R.id.sample_text);
//创建子线程并启动
final MyThread myThread = new MyThread();
myThread.start();
testBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
for (int i = 0; i < 60; i++) {
//使用子线程中的Handler发送消息
myThread.myThreadHandler.sendEmptyMessageDelayed(i, i * 1000);
}
}
});
}
}
先看如下代码:
public class MainActivity extends AppCompatActivity {
private String TAG = "testHandler";
private Handler myHandler;
class MyThread extends Thread {
public Looper myThreadLooper;
@Override
public void run() {
super.run();
Looper.prepare();
myThreadLooper = Looper.myLooper();
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建子线程并启动
final MyThread myThread = new MyThread();
myThread.start();
//将子线程的Looper对象赋给myHandler
myHandler = new Handler(myThread.myThreadLooper) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e(TAG, "msg what:" + msg.what);
}
};
myHandler.sendEmptyMessage(2);
}
}
在上述代码中涉及到两个线程:UI线程和一个子线程;在UI线程中试图将子线程的Looper对象赋给Handler对象,在子线程运行的时候获取自己的Looper对象。
上述代码运行的时候会报如下错误:
Caused by: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference
at android.os.Handler.(Handler.java:236)
at android.os.Handler.(Handler.java:140)
at com.cloudecg.jgc.test.androidrestudy.MainActivity$1.(MainActivity.java:46)
at com.cloudecg.jgc.test.androidrestudy.MainActivity.onCreate(MainActivity.java:46)
at android.app.Activity.performCreate(Activity.java:6910)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
即Looper对象为空。
产生上述错误的原因在于多线程并发,当UI线程执行到赋值操作的时候,子线程中的Looper对象还没有创建成功。
使用HandlerThread可以很方便的避免上述多线程并发带来的问题。
将上述代码改为HandlerThread的方式,如下:
public class MainActivity extends AppCompatActivity {
private String TAG = "testHandler";
private Handler myHandler;
private HandlerThread myThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建子线程并启动
myThread = new HandlerThread("MyThreadHandler");
myThread.start();
//将子线程的Looper对象赋给myHandler
myHandler = new Handler(myThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e(TAG, "msg what:" + msg.what);
}
};
myHandler.sendEmptyMessage(2);
}
}
将Thread改为HandlerThread之后就不会产生之前的多线程并发问题了。
原因是在HandlerThread的getLooper()方法中已经做了相关处理,查看源代码如下:
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;
}
在HandlerThread中如果Looper对象没有创建成功,则当前线程一直处于等待状态。
HandlerThread的唤醒源代码如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在HandlerThread的run方法中如果Looper对象创建成功,则唤醒所有等待线程。
==============================================================================================
当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程),主线程为管理界面中的UI控件,进行事件分发,比如说:点击一个Button,Android会分发事件到Button上,来响应你的操作。如果此时需要一个耗时的操作。例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示"强制关闭"。这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。这个时候,Handler就出现了,来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接收子线程传过来的(子线程用sendMessage()方法传递)Message对象(里面包含数据),把这些消息放入主线程队列中,配合主线程进行UI更新。
这个例子介绍最简单的Handler使用,是将Handler绑定到它所建立的线程中。本次实例完成的功能是:单击“开始”按钮,程序会开始启动线程,并且线程程序完成后延时1s后继续启动该线程,每次线程的run函数中完成对界面输出UpdateThread...文字,不停的运行下去,当单击“结束”按钮时,该线程就会停止,如果继续单击“开始”,则文字又开始输出了。
源代码:
package com.example.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView text_view = null;
private Button start = null;
private Button end = null;
//使用handler时首先要创建一个handler
Handler handler = new Handler();
//要用handler来处理多线程可以使用runnable接口,这里先定义该接口
//线程中运行该接口的run函数
Runnable update_thread = new Runnable()
{
public void run()
{
//线程每次执行时输出"UpdateThread..."文字,\n为自动换行
text_view.append("\nUpdateThread...");
//延时1s后又将线程加入到线程队列中
handler.postDelayed(update_thread, 1000);
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text_view = (TextView)findViewById(R.id.text_view);
start = (Button)findViewById(R.id.start);
start.setOnClickListener(new StartClickListener());
end = (Button)findViewById(R.id.end);
end.setOnClickListener(new EndClickListener());
}
private class StartClickListener implements OnClickListener
{
public void onClick(View v) {
// TODO Auto-generated method stub
//将线程接口立刻送到线程队列中
handler.post(update_thread);
}
}
private class EndClickListener implements OnClickListener
{
public void onClick(View v) {
// TODO Auto-generated method stub
//将接口从线程队列中移除
handler.removeCallbacks(update_thread);
}
}
}
布局文件:
这个例子比刚才那个例子稍微复杂些。因为这个例子中用到了Handler的消息队列机制,即通过Handler中一个线程向消息队列中用sendMessage方法发送消息,发送的消息当然可以用来传递参数。在Handler中用handleMessage来处理消息,处理方法是获得消息队列中的消息参数,用这些参数来完成另外一些功能。
本实例实现的是当“Start”按钮按下时,会启动一个线程,并绑定到Handler中,该线程发送带有参数的message到handler的消息队列中,消息队列的另一端获取该消息,并且用该消息的参数来更新进度条。
源代码:
package com.example.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends Activity {
private ProgressBar progress_bar = null;
private Button start = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progress_bar = (ProgressBar) findViewById(R.id.progress_bar);
start = (Button) findViewById(R.id.start);
start.setOnClickListener(new StartOnClickListener());
}
private class StartOnClickListener implements OnClickListener {
public void onClick(View v) {
// 让进度条显示出来
progress_bar.setVisibility(View.VISIBLE);
// 将线程加入到handler的线程队列中
update_progress_bar.post(update_thread);
}
}
// 创建一个handler,内部完成处理消息方法
Handler update_progress_bar = new Handler() {
@Override
public void handleMessage(Message msg) {
// super.handleMessage(msg);
// 显示进度条
progress_bar.setProgress(msg.arg1);
// 重新把进程加入到进程队列中
update_progress_bar.post(update_thread);
}
};// 不加这个分号则不能自动添加代码
Runnable update_thread = new Runnable() {
int i = 0;
public void run() {
// TODO Auto-generated method stub
i += 10;
// 首先获得一个消息结构
Message msg = update_progress_bar.obtainMessage();
// 给消息结构的arg1参数赋值
msg.arg1 = i;
// 延时1s,java中的try+catch用来排错处理
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
// 把消息发送到消息队列中
update_progress_bar.sendMessage(msg);
if (i == 100) {// 把线程从线程队列中移除
update_progress_bar.removeCallbacks(update_thread);
}
}
};
}
布局文件:
上面2个例子表面上看Handler使用了post方法启动了runnbale,其实启动的线程和activity主线程是同一个线程,因为它只是运行了线程的run方法,而不是start方法。实例3的目的是为了验证仅使用Handler的post方法是否处于同一个线程。
该实例在主activtiy的onCreate函数中打印了2条关于本线程的信息,然后创建一个Handler并为它绑定一个线程,在线程的run方法中也打印了线程的信息,观察2者的信息是否一样。
结果如下:
说明这2个线程确实是同一线程,并且可以看出主界面中的文字大概过了5s才显示出来,因为语句setContentView(R.layout.activity_main);放在了handler的post启动语句后面,而handler绑定的线程中又延时了5s,所以同时也证明了只有是同一个线程才会出现这种情况。而且Logcat中打印出如下日志:
09-06 17:10:19.353: I/Choreographer(19937): Skipped 62 frames! The application may be doing too much work on its main thread.
源代码:
package com.example.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
public class MainActivity extends Activity {
//新建一个handler
private Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//将runnable加载到handler的线程队列中去
handler.post(r);
//Thread t = new Thread(r);
//t.start();
setContentView(R.layout.activity_main);
//打印activtiy线程信息
System.out.println("activity_id---->"+Thread.currentThread().getId());
System.out.println("activity_name---->"+Thread.currentThread().getName());
}
Runnable r = new Runnable()
{
public void run() {
//打印新建线程信息
System.out.println("handler_id---->"+Thread.currentThread().getId());
System.out.println("handler_name---->"+Thread.currentThread().getName());
//延时10s,为了观察主界面中内容出现的时间
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
};
}
如果把语句:
handler.post(r);
换成:
Thread t = new Thread(r);
t.start();
其它的不变,则程序运行时主界面内容立刻就显示出来了,且系统输出如下:
这2者都说明这样绑定的线程与它所在的activity线程就不是同一个线程了。
这个例子将学会怎样不使用runnable来启动一个线程,而是用HandlerThread的looper来构造一个handler,然后该handler自己获得消息,并传递数据,然后又自己处理消息,当然这是在另一个线程中完成的。
消息结构中传递简单的整型可以采用它的参数arg1和arg2,或者传递一些小的其它数据,可以用它的Object,该Object可以是任意的对象。当需要传送比较大的数据是,可以使用消息的setData方法,该方法需要传递一个Bundle的参数。Bundle中存放的是键值对的map,只是它的键值类型和数据类型比较固定而已。
源代码:
package com.example.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("activity_ID---->"+Thread.currentThread().getId());
//新建一个HanderThread对象,该对象实现了用Looper来处理消息队列的功能
HandlerThread handler_thread = new HandlerThread("handler_thread");
handler_thread.start();
//MyHandler类是自己继承的一个类,这里采用hand_thread的Looper来初始化它
MyHandler my_handler = new MyHandler(handler_thread.getLooper());
//获得一个消息msg
Message msg = my_handler.obtainMessage();
//采用Bundle保存数据,Bundle中存放的是键值对的map,只是它的键值类型和数据类型比较固定而已
Bundle b = new Bundle();
b.putString("whether", "晴天");
b.putInt("temperature", 34);
msg.setData(b);
//将msg发送到自己的handler中,这里指的是my_handler,调用该handler的HandleMessage方法来处理该mug
msg.sendToTarget();
}
class MyHandler extends Handler
{
//空的构造函数
public MyHandler()
{}
//以Looper类型参数传递的函数,Looper为消息泵,不断循环的从消息队列中得到消息并处理,因此
//每个消息队列都有一个Looper,因为Looper是已经封装好了的消息队列和消息循环的类
public MyHandler(Looper looper)
{
//调用父类的构造函数
super(looper);
}
@Override
public void handleMessage(Message msg) {
//此处能看到,消息处理不是在主线程中执行的
System.out.println("Handler_ID---->"+Thread.currentThread().getId());
System.out.println("Handler_Name---->"+Thread.currentThread().getId());
//将消息中的bundle数据取出来
Bundle b = msg.getData();
String whether = b.getString("whether");
int temperature = b.getInt("temperature");
System.out.println("whether= "+whether+" ,temperature= "+temperature);
}
}
}
Android中的handler可以用来完成异步的消息出来,即发送消息和接收消息相互独立,可以同时运行。在例1和例2中,实际上handler中使用的线程是与它所在的activity处于同一个主线程,因为handler中调用的runnable接口是直接运行该接口的run函数的,而不是start函数。例3专门比较了这2中情况。例4学会使用怎样在新线程中处理消息的方法。
===============================================================================
Android HandlerThread使用,自带Looper消息循环的快捷类。
代码下载:http://www.demodashi.com/demo/10628.html
原文地址:
Android HandlerThread详解 CSDN
Android HandlerThread详解 简书
开发环境:
jdk1.8
Eclipse Luna Service Release 1 (4.4.1)
运行环境:
华为荣耀6(Android4.4)、华为p9(Android7.0)
实现功能:
Android HandlerThread的使用
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.
HandlerThread是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例。注意:start()仍然必须被调用。
如下是HandlerThread使用的demo。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
|
上述代码比较简单,功能也比较简单,可以在此基础上进行扩展。
在Actvitiy创建的时候调用initHandlerThraed()函数:
创建HandlerThread线程
运行线程
获取HandlerThread线程中的Looper实例
通过Looper实例创建Handler实例,从而使mSubThreadHandler与该线程连接到一起。
多次点击按钮,打印信息如下所示:
1 2 3 4 5 6 |
|
点击按钮,向mSubThreadHandler发送消息,mSubThreadHandler中接收到消息进行处理,由打印可知mSubThreadHandler的handleMessage方法运行在子线程中。
模拟耗时操作,生成随机数,然后向主线程中(mUiHandler)发送消息(Message)。
mUiHandler的handleMessage方法运行在主线程,可以用来更新Ui界面。
Activity销毁的时候,调用mHandlerThread.quit(),退出HandlerThread的Looper循环。
【运行方式:右键项目:Run as -》Android Application (备注:Eclipse需要配置Android开发环境)】
源码路径路径:frameworks/base/core/Java/android/os/HandlerThread.java
先看下HandlerThread的构造方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
HandlerThread是Thread(线程)的子类。创建一个HandlerThread实例,也就是创建了一个特殊的线程实例。
HandlerThread提供了两个构造方法:
HandlerThread(String name) 参数为线程名称,线程优先级为Process.THREAD_PRIORITY_DEFAULT。
HandlerThread(String name, int priority),name为线程名称,priority为设置的线程优先级。
我们知道线程需要通过start()方法来运行线程,HandlerThread也是这样的。接着看下线程运行的run()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
由run方法可知HandlerThrea线程运行创建了Looper实例,并开启了Looper循环,循环从消息队列中获取消息并给Handler进行处理。对于Looper不太明白的可以参考这篇深入理解Handler、Looper、Messagequeue
onLooperPrepared()在Looper循环之前调用,如果需要在Looper循环之前执行一些设置,可以显式覆盖此方法。
接着看获取Looper实例
1 2 |
|
对应源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
mHandlerThread.getLooper()获取与该线程绑定的Looper实例。mLooper是在HandlerThread的run()方法中赋值的(也就是在子线程中),getLooper是我们在主线程中调用,该方法会阻塞直到mLooper赋值。
然后demo中通过该looper实例创建Handler.
1 2 |
|
你可能会好奇为什么要这样长久Handler而不是“new Handler()“这样呢?因为我们要创建的Handler要与子线程绑定到一起,要处理子线程中的消息,所以要通过子线程中的looper(有线程对应的消息队列)实例创建Handler。这样通过mSubThreadHandler发送的消息会添加到子线程中的消息队列中,然后Looper实例消息进行分发,交给mSubThreadHandler进行处理。
HandlerThread提供的线程退出方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
quit和quitSafely都是退出HandlerThread的消息循环。其分别调用Looper的quit和quitSafely方法。
quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。
quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息
HandlerThread适合处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。而网络操作相对比较耗时,容易阻塞后面的请求,因此在这个HandlerThread中不适合加入网络操作。
至此HandlerThread就说完了。有什么问题欢迎大家指正、交流。
四、其他补充
参考文章:
Android Handler的基本使用、
深入理解Handler、Looper、Messagequeue