零蚀 都知道知识栈积累非一日之功,但是枯燥的生活中必须有点乐趣,乐趣是啥,学点有意思的东西(虽然祖国不一定需要你,但是你可以为人类做贡献嘛,哈哈哈,当然祖国一定是需要你的,起码房地产需要你)。 面试很容易遇到提问handler,那我就从handler开始加深技术栈,让我不菜的那么彻底,来吧! 文章的主要内容: 其实很多人会说handler防止内存泄漏,其实,handler的内存泄漏对手机影响真的很小,小到可以忽略掉它。对它的执着应该是面试官逼的吧!(借鉴别人的话) handler是什么:它是一个消息机制,什么是消息机制,它是处理消息为目的的一种功能结构和运行方式,简单说就是一种消息处理工具。 handler的用处: handler 可以用于线程之间的消息交互(当然这里不包括主线程之间的交互,因为主线程不共享的特性导致) handler 可以应用做定时 实现一个发送消息的功能。 再比如所我想发送一个消息再5秒以后。以此来通知更新UI,源码里说这里的第二个参数是需要被准确设置的,用SystemClock,也就是说这个时间要用时间戳来设置发送消息的时间。 或者我们也可以这么用 当然也是可以这么用的,通过post来引入一个runnable。post的方式和send方式大同小异,自己对照看就知道了。 当然这种消息发送是支持在thread里面进行的。 最后我们还能控制message发送的顺序(消息队列),前提是都在消息队列里面。 导致这个原因看源码 这里抛的异常,这里有一个参数表示队列里的这个msg是in-use状态,信息在用, 这个函数是用来释放信息的,而且这个消息是不能被释放的(当处于use的状态),顺着flag找,哪里有释放,然后我们可以看到Message源码里有这个,通过这个方法,我们可以看到这里使用 所以这个问题这么改就好了,当然新建一个也是没问题的,,当然你重新new 一个Message也是没问题的,不过这样更好。它内部可以复用已经用过的message。 这种方式应该是为了防止好耗时操作,程序员(代码狗)会一不小心,将message的内容改动,导致队列信息内容发生改动,所以索性,不暴露给你们了,我自己处理好了。 在我们使用以下带码时候迅速退出应用,是会发生内存泄漏的。 我们需要在退出的时候移除runnable内容才行。 首先什么是引用,先不说gc回收机制,可以看看NO.2 java gc回收机制 我们先了解一下什么叫做有效的引用,且如何获取的引用,能被gc机制认可,类走向引用巅峰的心路历程。(反正这里handler 被activity创建,有了activity的强引用) 引用等级图解(后面细说) : 按照上述描述我们可以用弱引用,最符合 这里是将activity处理为弱应用对象,当activty回收所处的主线程回收后,它的所有内容一并回收。 当我们在自线程里打toast的时候就会发现抛出异常了,这是为什么,为什么会提示没有loop。我们也知道添加 报错的源码(这里要注意的是,如果查看源码,一定让run的手机/模拟器和自己的targetSdkVersion对应上,因为手机系统版本报错位置对应它对应版本源码版本的位置,我这里targetSdkVersion 28,所以我用9.0的手机) 也就是说在使用handler之前,他会检查一下loop,但是发现loop=null,然后就崩了。为什么要使用handler。看到下面却是toast.show是通过handler获取一个message然后由Message发送消息,给它持有的handler(和handler的sendmessage一样)。所以我们通过show()来告诉handler这里toast是SHOW的状态。然后在new Handler的callback中处理逻辑。 这就是toast with handler的全过程 那 所以更细腻的写法可以这样,定义写一个toast的function。 首先loop在创建的时候会拿到当前线程对象,然后里面有很多关于控制MessageQueue的方法,比如 当然最重要的方法是下面这个loop这个方法,它如何结束的呢?看源码,loop就是不断的处理messageQueue的message单元。 我们可以看到的是Activity的thread也调用了looper,且这个Mainlooper是人为无法调用quitUQueue。好了到这looper的真相就基本到此为止了,他就是个管理者。 然后重点看sendMessage,在发送消息中,然后message持有了这个hadler的target然后就进入消息队列了,由此可见,handler就相当于最上层,给用户(程序员控制的句柄),负责发送消息到消息队列,然后接受消息队列送来的指定消息 由这里可以看出来handler和Looper这两个类在handler消息机制中,是发号命令的,而干事的是message和MessageQueue,而message像程序员,Queue像开发部,Looper像产品经理,Handler像boss,细品。 消息队列拿到消息:先看看这个消息能不能用,检测过后,将这个消息可用的先同步锁一下,然后将之前消息队列的最后一个消息的尾指针指向这个Message,这个消息就加入对列了。(指针是一种概念,不要说*,那太狭隘了,这只是他的存在方式之一,指针的目的都是指向某个地址/对象) 内容大致是不断的循环这个消息连表,然后找到最后一个消息prev,他的prev.next==null,我们就将得到的msg赋值给这个pre.next,这样就在消息链表的末尾添加了一个消息单元。 然后看一看消息发送内容。 其实上面已经说了是looper在for(;;)死循环里,拿到了queue里面的next消息,从而进行了分发。 然后不谈Message的内容,因为我觉得没有什么很有吸引力的内容了,就这样,最后总结一下。 首先看图来总结今天学习的内容。handler作为源码入门因该是最简单的的了吧,难怪面试官喜欢问,因为这是入门标准,源码简单,哈哈哈。 handler大体内容就是如下,具体你要把它分为几个部分,都行,网上有说4个,有说5个,看你怎么定义(按我说,就分两个,干事的msg和queue,负责调用的handler和looper,估计这么说面试官会wf,但是我就是这么皮,完) 这里有一处画的不严谨的是:looper虽然调用了dispatch分发功能,但是具体的分发功能实现是Message实现的。所以大家心里明白就好。
前言
handler的使用
handler 介绍
handler 应用
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Message msg=new Message(); msg.what=1; msg.obj="first"; // handler.sendEmptyMessage(1); 发一个空消息,只需要传入what就行 handler.sendMessage(msg); } Handler handler=new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if(msg.what==1){ ((TextView)findViewById(R.id.text)).setText((String)msg.obj); } return false; } }); }
handler.sendMessageAtTime(msg, SystemClock.uptimeMillis()+5000);
handler.sendMessageDelayed(msg,5000);
public class MainActivity extends AppCompatActivity implements Runnable { Handler handler =new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler.postDelayed(this,5000); } @Override public void run() { ((TextView)findViewById(R.id.text)).setText("这是一个文本"); } }
new Thread(){ @Override public void run() { super.run(); while(num<5){ // 这种sleep不抛异常 SystemClock.sleep(1000); ++num; Log.e("zero",String.valueOf(num)); } handler.sendMessage(msg); } }.start();
Message message1=new Message(); message1.obj="new1"; Message message2=new Message(); message2.obj="new2"; Message message3=new Message(); message3.obj="new3"; handler.sendMessage(message1); handler.sendMessage(message2); handler.sendMessageAtFrontOfQueue(message3); // print // new3 // new1 // new2
Handler的一些问题
message 重复使用
This message is already in use
而崩溃。final Message message=new Message(); message.obj="message"; new Thread(){ @Override public void run() { super.run(); while(index<5){ index++; SystemClock.sleep(1000); handler.sendMessage(message); } } }.start(); //# print //This message is already in use
//MessageQueue if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } .... msg.markInUse(); // 这里标记了message是处于应用状态 msg.when = when; Message p = mMessages;
public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); }
m.flags = 0;
的方式将message的inUse的状态给清零了,同时还很好的进行消息的复用,并且加了同步锁。public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
new Thread(){ @Override public void run() { super.run(); while(index<5){ index++; message=Message.obtain(); message.obj="message"; SystemClock.sleep(1000); handler.sendMessage(message); } } }.start();
内存泄漏
handler1.sendMessageDelayed(msg,15000); Handler handler1=new Handler() { @Override public void handleMessage(Message msg) { if(msg.what==1){ ((TextView)findViewById(R.id.text)).setText((String)msg.obj); } } };
@Override protected void onDestroy() { super.onDestroy(); // 对于持有Runable对象的我们直接移除runnable就行 //handler.removeCallbacks(this); // null代表移除当前的handler消息队列,这里面的token是 handler.removeCallbacksAndMessages(null); }
弱引用解决内存泄漏
// 自动以一个类,通过T获取它持有的引用类 public class MyHandler<T> extends Handler { private final WeakReference<T> weak; MyHandler(T t,Handler.Callback callback){ weak = new WeakReference<T>(t); } } // MainActivity中改为 MyHandler handler= new MyHandler<>(this, new Handler.Callback() { ... }
源码
Looper从toast在线程报错说起
Looper.prepare();
和Looper.loop();
就能解决问题,那么为什么。new Thread(){ @Override public void run() { Toast.makeText(MainActivity.this, "this", Toast.LENGTH_SHORT).show(); } }.start(); // throw error // "Can't toast on a thread that has not called Looper.prepare()"
if (looper == null) { // Use Looper.myLooper() if looper is not specified. looper = Looper.myLooper(); if (looper == null) { throw new RuntimeException( "Can't toast on a thread that has not called Looper.prepare()"); } } mHandler = new Handler(looper, null) { .... }
@Override public void show(IBinder windowToken) { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.obtainMessage(SHOW, windowToken).sendToTarget(); }
Looper.prepare();
和Looper.loop();
做了什么。其实就一行代码,sThreadLocal.set(),这里的ThreadLocal,可以看作就是一个thread管理类,它记录管理thread的信息内容,这里ThreadLocal通过set获取了当前线程,然后它在你的线程中创建了一个looper对象,而loop()是结束这个looper这个对象(这里的结束不是说功能结束,而是准备工作结束了)。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)); }
if(Looper.myLooper()==null){ Looper.prepare(); Toast.makeText(MainActivity.this, "this1", Toast.LENGTH_SHORT).show(); Looper.loop(); }else{ Toast.makeText(MainActivity.this, "this2", Toast.LENGTH_SHORT).show(); }
那LOOP到底是什么
quit() {mQueue.quit(false);}
停止消息队列等等。... // 定义一个死循话。 for (;;) { // 获取队列中下一个message Message msg = queue.next(); } try{ // 分发message msg.target.dispatchMessage(msg); } // 消息放入缓冲池,标记这个信息in-use(不能使用),清理掉信息的所有内容 msg.recycleUnchecked();
// ActivityThread.java final Looper mLooper = Looper.myLooper(); public static void main(String[] args) { .... Looper.prepareMainLooper(); .... Looper.loop(); }
Handler扮演什么角色
public Handler(Callback callback, boolean async) { ... mLooper = Looper.myLooper(); mQueue = mLooper.mQueue; ... }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 消息内制定了这消息是谁发出的(出来混的都是要还的) msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } // 将消息送往消息对列 return queue.enqueueMessage(msg, uptimeMillis); }
消息队列MessageQueue
enqueueMessage(Message msg, long when) { for (;;) { ... msg.next = p; // invariant: p == prev.next prev.next = msg; ... }
总结
总结图解
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算