使用Handler中遇到的问题分析
来源:互联网 发布:淘宝联盟鹊桥手机登录 编辑:程序博客网 时间:2024/06/05 17:00
- 引言
- Only one Looper may be created per thread
- Cant create handler inside thread that has not called Looperprepare
- 为何主线程中创建handler就没问题
- handler内存泄漏
引言
相信很多童鞋在使用handler的时候肯定遇到了不少的麻烦吧,比如:
1、Only one Looper may be created per thread;
2、Can’t create handler inside thread that has not called Looper.prepare();
3、handler导致的内存泄漏等。
那么今天就详细分析一下其中的原因,以记录又一次深深的掉入坑里无法自拔的囧态。
Only one Looper may be created per thread
这个问题还是比较好查找原因的,我们在使用的时候如果调用Looper.prepare()两次就会出现,通过查看源码来分析:
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));}
可以看到prepare方法中代码非常少,首先是调用sThreadLocal.get()方法去取Looper,如果是null说明没有创建并存储,然后会新建一个Looper对象,然后存入sThreadLocal对象中;如果不是null说明Looper对象已经创建过了,这时候就会抛出Only one Looper may be created per thread异常。可见prepare()方法不能调用两次,如果调用两次就会抛出异常。
Can’t create handler inside thread that has not called Looper.prepare()
意思是不能在没有调用Looper.prepare()方法的线程中创建handler,那么继续通过源码分析:
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;}
可见创建handler时,会去调用Looper.myLooper()方法,而Looper.myLooper()方法如下:
public static @Nullable Looper myLooper() { return sThreadLocal.get();}
上面我们分析过Looper.prepare()方法会去创建Looper对象,然后存入sThreadLocal中,而Looper.myLooper()方法就是去sThreadLocal中去取Looper对象。所以在没有调用Looper.prepare()方法的情况下,Looper肯定是没有创建的,而MessageQueue(消息队列)又是在Looper构造中去创建的;同时不断的去MessageQueue(消息队列)中取消息,调用handler的dispatchMessage()方法,都是Looper的loop()方法中去执行的。如果没有创建Looper对象,整个handler消息机制就没有办法正常工作,所以在没有调用Looper.prepare()方法(没有创建Looper对象)会抛出上面的异常。
为何主线程中创建handler就没问题?
大家应该会发现,平时使用handler的时候,没有遇到上面所说的问题啊,那到底是为啥呢?
继续分析,看代码:
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}
从ActivityThread的main方法中,我们可以看到里面调用了Looper 中的Looper.prepareMainLooper()方法,而这个方法中调用了prepare方法去创建Looper对象并存储起来了,并且main方法中还调用了Looper.loop()方法。所以我们平时在主线程中使用不会遇到上述问题,是因为主线程中已经把我们需要做的事情做了。
handler内存泄漏
在平时开发中,应该有多数人使用handler会造成内存泄漏,可能你还没有察觉到。一般我们在activity中使用handler的写法如下:
private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { //doSomething() } };
告诉我,你是不是就是这样写的呢?
当使用内部类(包括匿名类)来创建handler的时候,handler对象会隐式地持有这个activity的引用。而handler通常会伴随着一个耗时的后台线程(网络加载图片),这个任务执行完之后,通过消息机制通知handler,然后handler把图片更新到界面。但是,如果这时候任务没执行完用户关闭了activity,那么这个activity就有可能在GC检查时被回收掉,而该线程持有handler的引用,这个handler又持有activity的引用,导致该activity无法被回收掉(内存泄露)。
那么使用handler导致内存泄露的解决方法是什么呢?
使用handler的removeCallbacks()和removeMessages()方法将回调和消息移除,或者使用removeCallbacksAndMessages(null)将所有回调和消息都移除。
将handler声明为静态类。
将handler声明为静态类就不会跟随外部类创建对象而加载,所以不会持有外部类对象的引用,所以activity可以被回收。由于handler不再持有activity的引用,不能在handler中操作activity中的对象了。所以需要在handler中增加一个对activity的弱引用(WeakReference)。
static class MyHandler extends Handler { WeakReference<Activity> mWeakReference; public MyHandler(Activity activity) { mWeakReference=new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { Activity activity=mWeakReference.get(); if(activity!=null) { //doSomething() } }}
- 使用Handler中遇到的问题分析
- Handler使用中遇到的内存泄漏问题
- 编程中遇到的Handler问题
- GTK+中使用Glade3编程遇到Gtk-WARNING **: Could not find signal handler的问题
- 工作中遇到的问题之handler处理机制
- Handler使用过程中出现的问题
- 【Handler问题求指导】使用Handler遇到了问题
- 【Handler问题求指导】使用Handler遇到了问题
- 哪些年遇到过的Andriod问题(8)Handler使用 mars.barhandler中的错误(测试) 线程中停止线程?
- scrollview 中使用中遇到的问题
- Android中使用Handler造成内存泄露的分析总结
- Android中使用中Handler的内存泄露问题
- php使用中遇到的问题
- log4net使用中遇到的一些问题
- JSTL中EL使用遇到的问题
- petshop使用中遇到的两个问题
- eclipse使用中遇到的小问题
- 360浏览器使用中遇到的问题
- js调用扫描仪twain进行网页图像扫描
- linux0.11源码阅读笔记1-启动流程-bootsect.s
- C/C++中产生随机数函数(rand,srand)的用法
- Web 框架的要素
- 深入理解python中的浅拷贝和深拷贝
- 使用Handler中遇到的问题分析
- mysq 5.7.20 解压版安装遇到的坑
- Java解决N皇后问题
- Java程序设计(Java9版):第1章 Java开发环境配置 (Set up Java development environment)
- 再说python的强大 之大数据、AI、VR、图像处理
- 返回指针的函数特性,和malloc的初识,全局变量的提示
- AS设置日志的颜色
- Nexus学习笔记一
- magento开发--入门深入理解第一章