当前位置: 首页 > 科技观察

Android消息机制Handler,有必要再说一遍

时间:2023-03-15 09:44:35 科技观察

我们在日常开发中难免会用到Handler。熟悉它非常重要!通过本文,你可以轻松得到以下问题的答案:Handler、Looper、Message、MessageQueue的原理是什么,它们之间的关系是什么?MessageQueue的存储结构是怎样的?子线程是DoyouhavetocallLooper.prepare()andLooper.loop()?Handler的简单使用,相信没有人不会用到Handler吧?假设在Activity中处理了一个耗时的任务,需要更新UI,那么简单看看我们平时是如何处理的。overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContentView(R.layout.activity_main3)//请求网络subThread.start()}overridefunonDestroy(){subThread.interrupt()super.onDestroy()}privatevalhandlerbylazy(LazyThreadSafetyMode.NONE){MyHandler()}privatevalsubThreadbylazy(LazyThreadSafetyMode.NONE){SubThread(handler)}privateclassMyHandler:Handler(){overridefunhandleMessage(msg:Message){super.handleMessage(msg)//主线程处理逻辑,这里一般用Weak引用持有Activity实例,避免内存泄露}}privateclassSubThread(valhandler:Handler):Thread(){overridefunrun(){super.run()//网络请求等耗时操作//网络请求完成后,我们只好通知UI已经刷新了,直接考虑Handler处理,其他方案暂时不考虑。//第一种方法,一般这个数据就是请求结果解析的内容handler.obtainMessage(1,数据a).sendToTarget()//第二种方法valmessage=Message.obj()//尝试使用Message.obtain()来初始化message.what=1message.obj=data//一般这个data就是message的内容请求结果分析handler.sendMessage(message)//前三个方法handler.post(object:Thread(){overridefunrun(){super.run()//处理更新操作}})}}上面的代码很简单,因为网络请求是一个耗时的任务,所以我们新开一个创建线程,网络请求结束后通过Handl解析er通知主线程更新UI。使用了三种简单的方法。细心的朋友可能会发现,第一种方法和第二种方法一样,都是使用Handler发送一个带内容的Message对象。有一点要提一下:我们应该尽可能使用Message.obtain()而不是newMessage()来初始化Message,主要是因为Message.obtain()可以减少内存的申请。根据您在上一篇文章中的建议,我们将尽可能少地发布源代码。你可以很容易地发现,上面所有的方法最终都会调用这个方法:.w("Looper",e.getMessage(),e);returnfalse;}returnenqueueMessage(queue,msg,uptimeMillis);}privatebooleanqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){msg.target=this;if(mAsynchronous){msg.setAsynchronous(true);}returnqueue.enqueueMessage(msg,uptimeMillis);}上面的代码有一个MessageQueue,最后调用MessageQueue#enqueueMessage方法对消息进行排队。不得不简单说一下MessageQueue的基本情况。MessageQueueMessageQueue顾名思义就是一个消息队列,也就是一个存放多条消息的容器。它使用单向链表数据结构而不是队列。它的next()指向链表中的下一个Message元素。booleanenqueueMessage(Messagemsg,longwhen){//...省略一些校验码synchronized(this){//...省略一些校验码msg.markInUse();msg.when=when;Messagep=mMessages;booleanneedWake;if(p==null||when==0||whenlocal=newThreadLocal<>();//设置初始值为true.local.set(true);Booleanbool=local.get();Logger.i("MainThread读取的值:"+bool);newThread(){@Overridepublicvoidrun(){Booleanbool=local.get();Logger.i("SubThread读取的值就是:"+bool);//设置值为false.local.set(false);}}.start()://主线程休眠1秒,确保上层子线程先执行执行以下代码。Thread.sleep(1000);BooleannewBool??=local.get();Logger.i("MainThread读取的新值:"+newBool??);代码没什么好说的,打印的log,你会看到这个的:MainThread读到的值:trueSubThread读到的值:nullMainThread读到的值:true第一个Log是毋庸置疑的,因为值设置为true,因为打印结果没什么好说的。对于第二个Log,根据上面的介绍,一个线程使用ThreadLocal存储的数据只能被这个线程读取,所以第二个Log的结果是:null。然后在子线程中设置ThreadLocal的值为false,然后就会打印第三个Log。原理同上。在子线程中设置ThreadLocal的值不影响主线程的数据,所以打印为真。实验结果证实:即使是同一个ThreadLocal对象,任何线程对其set()和get()方法的操作都是相互独立的,互不影响。Looper.myLooper()让我们回到Looper.myLooper():staticfinalThreadLocalsThreadLocal=newThreadLocal();让我们看看我们在sThreadLocal上操作的地方。publicstaticvoidprepare(){prepare(true);}privatestaticvoidprepare(booleanquitAllowed){if(sThreadLocal.get()!=null){thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");}sThreadLocal.set(newLooper(quitAllowed));}所以你知道,这就是为什么在子线程中使用Handler之前必须调用Looper.prepare()的原因。你可能想知道,当我在主线程中使用它时,我没有请求Looper.prepare()。原来在ActivityThread中,我们要调用Looper.prepareMainLooper():publicstaticvoidmain(String[]args){//...Looper.prepareMainLooper();//...if(sMainThreadHandler==null){sMainThreadHandler=thread.getHandler();}//...Looper.loop();//...}我们看Looper.prepareMainLooper():publicstaticvoidprepareMainLooper(){prepare(false);synchronized(Looper.class){if(sMainLooper!=null){thrownewIllegalStateException("ThemainLooper已经准备好了。");}sMainLooper=myLooper();}}

最新推荐
猜你喜欢