Android在子线创建Handler出现异常的原因及解决办法
2018-04-11 20:58
309 查看
在日常的代码编写中,Handler主要是用来进行线程间通信的一种手段,或者说一种工具来使用,一般我们都会将handler写在主线程中,然后开启一个Thread,在里面进行post或者sendMessage,将Message从MessageQueue中送给Handler,然后我们获取数据进行UI更新。
但是这是为什么呢?其实每一个Handler都会有一个MessageQueue,而MessageQueue是被封装在Looper中的,而Looper又会关联一个线程,最后就相当于一个MessageQueue关联了线程,一个线程只有一个MessageQueue,一个Looper,之后在通过Message来打到线程间通信。
默认情况下,只有一个MessageQueue就是主线程会通过Looper.java中的prepareMainLooper()方法来创建出来,也就是Looper的生成时,会自动产生一个MessageQueue。
但是有一点要注意子线程默认情况下是没有Looper的更不会有MessageQueue;
那么Looper是怎么创建的呢,Looper在主线程中,是通过prepareMainLooper()这个方法中的prepare()创建的源码如下:/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
下面我们看一段部分源码 这段源码的主要功能就是死循环不断传递MessageQueue中的Message给Handler public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); //MessageQueue是一个队列结构 不断地调用.next传递Message
if (msg == null) { //这里可以看出Message是可以为空的。
// No message indicates that the message queue is quitting.
return;
}
通常我们会认为子线程是不能创建handler的,但是这是为什么呢,我们先开启一个子线程,在里面创建一个handler看看会发生什么吧。public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
Handler handler = null;
@Override
public void run() {
handler = new Handler();
}
}.start();
}
}
我们的程序崩溃了,同时抛出了Can't create handler inside thread that has not called Looper.prepare()的异常,这个异常的意思为,我们不能在子线程创建hadnler,因为无法调用Looper.prepare()方法,
所以我们无法使用Handler的原因就是Looper.prepare()是用来创建Looper的,因为子线程默认不会产生Looper,所以也没办法产生MessageQueue,也不能发送Message来进行通信了,
解决办法很简单,只要我们手动执行Looper的prepare方法创建一个Looper,在执行prepare的时候,Looper会自动创建一个MessageQueue,并且绑定到Looper所在的线程,
然后通过调用loop方法,开启无限循环就可以正常的在子线程执行Handler了代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
Handler handler = null;
@Override
public void run() {
Looper.prepare(); //创建一个Looper,同时创建了MessageQueue,并且绑定了线程
handler = new Handler();
Looper.loop();//开启Looper的循环
}
}.start();
}
}
总结:
1.子线程是可以创建Handler的,不能创建的原因是因为子线程默认不会创建Looper,当Looper为空就会抛出上述异常,导致程序崩溃掉。
2.Looper.prepare()方法会创建一个Looper对象,并且创建的同时通过sThreadLocal.set(new Looper(quitAllowed)); 这行代码来为创建的Looper绑定线程,同时Looper创建的同时会产生一个MessageQueue。
3.Looper.loop()方法会开启死循环,不断地遍历MessageQueue。
4.解决办法子线程不能创建Handler的方法 手动调用Looper.prepare()方法创建一个Looper并且调用Looper.loop()开启循环就可以了。
但是这是为什么呢?其实每一个Handler都会有一个MessageQueue,而MessageQueue是被封装在Looper中的,而Looper又会关联一个线程,最后就相当于一个MessageQueue关联了线程,一个线程只有一个MessageQueue,一个Looper,之后在通过Message来打到线程间通信。
默认情况下,只有一个MessageQueue就是主线程会通过Looper.java中的prepareMainLooper()方法来创建出来,也就是Looper的生成时,会自动产生一个MessageQueue。
但是有一点要注意子线程默认情况下是没有Looper的更不会有MessageQueue;
那么Looper是怎么创建的呢,Looper在主线程中,是通过prepareMainLooper()这个方法中的prepare()创建的源码如下:/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //如果looper的线程不唯一,抛出异常,looper已经在该线程被创建了 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); //否则,创建一个looper 并且绑定到创建Looper的线程 } /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */创建了MessageQueue之后,Looper就要绑定到创建Looper的线程,同时进行死循环,不断传递MessageQueue中的Message给Handler,让handler处理。
下面我们看一段部分源码 这段源码的主要功能就是死循环不断传递MessageQueue中的Message给Handler public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); //MessageQueue是一个队列结构 不断地调用.next传递Message
if (msg == null) { //这里可以看出Message是可以为空的。
// No message indicates that the message queue is quitting.
return;
}
通常我们会认为子线程是不能创建handler的,但是这是为什么呢,我们先开启一个子线程,在里面创建一个handler看看会发生什么吧。public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
Handler handler = null;
@Override
public void run() {
handler = new Handler();
}
}.start();
}
}
我们的程序崩溃了,同时抛出了Can't create handler inside thread that has not called Looper.prepare()的异常,这个异常的意思为,我们不能在子线程创建hadnler,因为无法调用Looper.prepare()方法,
所以我们无法使用Handler的原因就是Looper.prepare()是用来创建Looper的,因为子线程默认不会产生Looper,所以也没办法产生MessageQueue,也不能发送Message来进行通信了,
解决办法很简单,只要我们手动执行Looper的prepare方法创建一个Looper,在执行prepare的时候,Looper会自动创建一个MessageQueue,并且绑定到Looper所在的线程,
然后通过调用loop方法,开启无限循环就可以正常的在子线程执行Handler了代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
Handler handler = null;
@Override
public void run() {
Looper.prepare(); //创建一个Looper,同时创建了MessageQueue,并且绑定了线程
handler = new Handler();
Looper.loop();//开启Looper的循环
}
}.start();
}
}
总结:
1.子线程是可以创建Handler的,不能创建的原因是因为子线程默认不会创建Looper,当Looper为空就会抛出上述异常,导致程序崩溃掉。
2.Looper.prepare()方法会创建一个Looper对象,并且创建的同时通过sThreadLocal.set(new Looper(quitAllowed)); 这行代码来为创建的Looper绑定线程,同时Looper创建的同时会产生一个MessageQueue。
3.Looper.loop()方法会开启死循环,不断地遍历MessageQueue。
4.解决办法子线程不能创建Handler的方法 手动调用Looper.prepare()方法创建一个Looper并且调用Looper.loop()开启循环就可以了。
相关文章推荐
- Android 启动模拟器是出现“Failed to allocate memory: 8”错误提示的原因及解决办法
- Android 启动模拟器是出现“Failed to allocate memory: 8”错误提示的原因及解决办法
- Android手机出现"已安装了存在签名冲突的同名数据包"的原因及解决办法
- Android项目更换开发环境时出现的 java.lang.VerifyError 异常解决办法
- 举例说明android中出现java.lang.IllegalStateException: database not open这种错误的原因及解决办法
- Android 启动模拟器是出现“Failed to allocate memory: 8”错误提示的原因及解决办法
- 使用Android SDK Manager下载sdk时总是出现中断异常的解决办法。
- 2014-10-22遇到的问题----Android创建第一个项目出现appcompat_v7工程的解决办法
- 【异常】android开发adb shell下出现Sqlite3 not found解决办法
- 从 IClassFactory 为 CLSID 为 {00024500-0000-0000-C000-000000000046} 的 COM 组件创建实例失败,原因是出现以下错误: 8001010a解决办法
- Android 启动模拟器是出现“Failed to allocate memory: 8”错误提示的原因及解决办法
- android项目中切换界面出现Unable to pause activity异常的解决办法
- android访问静态页面,出现405异常解决办法
- 停止预览时调用Camera.release(), 出现Method called after release()异常问题原因及解决办法
- 关于一些异常出现原因及简单的解决办法
- Android手机出现"已安装了存在签名冲突的同名数据包"的原因及解决办法
- 停止预览时调用Camera.release(), 出现Method called after release()异常问题原因及解决办法
- Android项目更换开发环境时出现的 java.lang.VerifyError 异常解决办法
- 举例android项目中的string.xml出现这个The character reference must end with the ';' delimiter.错误提示的原因及解决办法
- android.database.sqlite.SQLiteException: no such table错误出现的原因及解决办法