声明:本教程不收取任何费用,欢迎转载,尊重作者劳动成果,不得用于商业用途,侵权必究!!!
目录
一、前言
二、简介
三、在主线程中创建Handler
四、在子线程中创建Handler
五、总结说
1、Handler的创建
系列文章
Handler异步消息传递机制(一)Handler常用基本用法
Handler异步消息传递机制(二)在子线程中创建Handler
Handler异步消息传递机制(三)在主线程、子线程中创建Handler,源码(Android 9.0)解析
Handler异步消息传递机制(四)Handler发送消息流程,源码(Android 9.0)解析
上一篇文章我们讲到了Handler消息传递机制的最常见用法,我们在主线程创建了Handler对象,然后在新启动的线程创建一个Message对象,然后借助Handler对象发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后就可以进行UI更新操作了。想了解的可参考Handler异步消息传递机制(一)常用实现方式
依稀记得13年的某一天一个新来的Android同事,他说我运行程序出现bug了,报的错误信息为:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(),你知道怎么解决吗?
因为我有收集bug问题解决方式的习惯,于是我找了找自己的笔记给他发了一个链接。然后没过几秒,又问你知道为什么吗?当时有点懵,因为我完全没有印象。平时也不喜欢去记忆一些东西,除非是刚不久用到的东西才会有所印象吧,所以我的回答是:“ 很久了不是记得很清楚了”。
当时感觉还是挺纳闷的,不就是new Hanlder发送消息吗?于是找了一些资料复习了一下。确实不过即使只是简单new一下,还是有不少地方需要注意的!这也使我以后对技术点的探究逐渐的深入了。
我们先尝试在程序中创建两个Handler对象,一个在主线程中创建,一个在子线程中创建,看会有什么效果?
详见上篇文章 Handler异步消息传递机制(一)常用实现方式 ,它是典型的在主线程中创建Handler的实例
我们继续以文章 Handler异步消息传递机制(一)Handler常用实现方式 的demo为例,我们把主线程创建的Hanlder注释掉,然后把 Handler 的创建挪到新启动的子线程中,具体代码如下:
package com.luminal.handler_download;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView show_text;
private String strMsg;
private Handler handler;
//在主线程中获取、处理消息
// private Handler handler = new Handler() {
// @Override
// public void handleMessage(Message msg) {
// switch (msg.what) {
// case 1://下载成功
// strMsg = strMsg +"\n"+ "2、apk下载成功。。。开始自动安装下载好的apk!";
// show_text.setText(strMsg);
// break;
// case 2://下载失败
// strMsg = strMsg +"\n"+ "apk下载失败!";
// show_text.setText(strMsg);
// break;
//
// default:
// break;
// }
// }
// };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show_text = (TextView) findViewById(R.id.show_text);
downloadApkFile();
}
/**
* 下载apk文件
* 注:Android 6.0以上需要申请读写权限
*/
private void downloadApkFile() {
strMsg = "1、开始下载apk。。。";
show_text.setText(strMsg);
new Thread() {//在新启动的子线程,调用下载app的代码,并发送消息、反馈结果
public void run() {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1://下载成功
strMsg = strMsg +"\n"+ "2、apk下载成功。。。开始自动安装下载好的apk!";
show_text.setText(strMsg);
break;
case 2://下载失败
strMsg = strMsg +"\n"+ "apk下载失败!";
show_text.setText(strMsg);
break;
default:
break;
}
}
};
DownLoadAppFile downLoadFile = new DownLoadAppFile();
downLoadFile.download(null, handler, null);
};
}.start();
}
}
然后运行程序,你会发现,在子线程中创建的Handler是会导致程序崩溃的,提示的错误信息如下:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.(Handler.java:200)
at android.os.Handler.(Handler.java:114)
at com.luminal.handler_download.MainActivity$1$1.(MainActivity.java:60)
at com.luminal.handler_download.MainActivity$1.run(MainActivity.java:60)
报错信息为:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(),具体指向代码错误第60行,创建Handler的语句:handler = new Handler()
英文直译下,不能在没有调用 Looper.prepare() 的线程中创建Handler,那我们试试在线程中先调用一下Looper.prepare(),再创建Handler对象。
Looper.prepare();
handler = new Handler() {
。。。。。。
然后再运行一下程序,果然这样就不会崩溃了,你会发现bug就这么神奇的解决了,但是这时候UI不能更新了!
那么为什么子线程不调用 Looper.prepare() 就创建Handler会报错呢?子线程调用了Looper.prepare()、创建Handler以后,UI界面如何更新呢?欲知故事如何,请看下篇文章的整理分解吧!
(1)一般在主线程中创建Handler
(2)在子线程创建Handler会报错,错误信息为:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() ,意思是不能在没有调用 Looper的prepare方法的线程中创建Handler,所以我们先要调用Looper的prepare方法