笔者出生在江西一个偏远的山村。虽然出生时已经不是那个温饱都是问题的年代,但是也谈不上有个幸福的童年。家里很穷。幼儿园并没有读,因为家里觉得花那个钱没有必要,小学才开始学拼音字母。我的童年,就是和我的姐姐弟弟在山中的梯田里放牛,抓泥鳅,割鱼草。
大学上的是北大青鸟,一个成人培训机构,没有学历。在深圳打拼了6年了,一直在一些小公司打转。年初疫情突然爆发,市场经济进入寒冬,我也被公司裁员了。
这也让我意识到,本身学历就不高,如果还不发奋让自己的技术更上一个层次的话,肯定是没有未来可言的。幸而有朋友在阿里任职,给了我一个内推的机会。所以,疫情在家的这段时间,我决定逼自己一把,开始着手准备阿里的面试。终于在五一节前拿到了P5的offer。下面是我的面试经验分享。
当应用程序第一次启动时,会同时自动开启1条主线程,用于处理UI相关的事件(如更新、操作等)
人为手动开启的线程,执行耗时操作(如网络请求、数据加载等)
线程间通讯的数据单元(即Handler接受 & 处理的消息对象),用于存储需要操作的通信信息
一种数据结构(先进先出),存储Handler发送过来的消息(Message)
Handler为主线程与子线程的通信媒介,是线程消息的主要处理者。用于添加消息(Message)到消息队列(Message Queue),处理循环器(Looper)分派过来的消息(Message)
消息队列(Message Queue)与处理者(Handler)的通信媒介,用于消息循环,即
(1)消息获取:循环取出消息队列(Message Queue)的消息(Message)
(2)消息分发:将取出的消息(Message)发送给对应的处理者(Handler)
每个线程只能拥有1个Looper,1个Looper可绑定多个线程的Handler,即多个线程可往1个Looper所持有的MessageQueue中发送消息,提供线程间通信的可能
// 步骤1:自定义Handler子类(继承Handler类) & 复写handleMessage()方法class mHandler extends Handler {// 通过复写handlerMessage() 从而确定更新UI的操作@Overridepublic void handleMessage(Message msg) {...// 需执行的UI操作}}// 步骤2:在主线程中创建Handler实例private Handler mhandler = new mHandler();// 步骤3:创建所需的消息对象Message msg = Message.obtain(); // 实例化消息对象msg.what = 1; // 消息标识msg.obj = "AA"; // 消息内容存放// 步骤4:在工作线程中 通过Handler发送消息到消息队列中// 可通过sendMessage() / post()// 多线程可采用AsyncTask、继承Thread类、实现RunnablemHandler.sendMessage(msg);// 步骤5:开启工作线程(同时启动了Handler)// 多线程可采用AsyncTask、继承Thread类、实现Runnable
// 步骤1:在主线程中 通过匿名内部类 创建Handler类对象private Handler mhandler = new Handler(){// 通过复写handlerMessage()从而确定更新UI的操作@Overridepublic void handleMessage(Message msg) {...// 需执行的UI操作}};// 步骤2:创建消息对象Message msg = Message.obtain(); // 实例化消息对象msg.what = 1; // 消息标识msg.obj = "AA"; // 消息内容存放// 步骤3:在工作线程中 通过Handler发送消息到消息队列中// 多线程可采用AsyncTask、继承Thread类、实现RunnablemHandler.sendMessage(msg);// 步骤4:开启工作线程(同时启动了Handler)// 多线程可采用AsyncTask、继承Thread类、实现Runnable
// 步骤1:在主线程中创建Handler实例private Handler mhandler = new mHandler();// 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容// 需传入1个Runnable对象mHandler.post(new Runnable() {@Overridepublic void run() {... // 需执行的UI操作 }});// 步骤3:开启工作线程(同时启动了Handler)// 多线程可采用AsyncTask、继承Thread类、实现Runnable
在主线程中创建
(1)循环器 对象(Looper)
(2)消息队列 对象(Message Queue)
(3)Handler对象
Looper、Message Queue均属于主线程,创建Message Queue后,Looper自动进入消息循环。此时,Handler自动绑定了主线程的Looper、Message Queue
工作线程通过Handler发送消息(Message)到消息队列(Message Queue)中,该消息内容=工作线程对UI的操作
消息出队:Looper循环取出消息队列(Message Queue)中的消息(Message)
消息分发:Looper将去除的消息(Message)发送给创建该消息的处理者(Handler)
在消息循环过程中,若消息队列为空,则线程阻塞。
处理者Handler接受循环器Looper发送过来的消息(Message)
处理者Handler根据消息(Message)进行UI操作
(1)1个线程(Thread)只能绑定1个循环器(Looper),但可以有多个处理者
(2)1个循环器(Looper)可绑定多个处理者(Handler)
(3)1个处理者(Handler)只能绑定1个循环器(Looper)
Handler机制包括3个重要类:1、处理者 Handler2、循环器 Looper3、消息队列 MessageQueue
记录一次Handler使用步骤
Looper.prepareMainLooper()
为主线程(UI线程)创建1个循环器对象(Looper),同时也会自动创建1个对应的消息队列对象(MessageQueue)
该方法在主线程(UI线程)创建时自动调用,不需手动生成。在Android应用进程启动时,会默认创建1个主线程(ActiviyThread,也叫UI线程),创建时,会自动调用ActivityThread的1个静态main方法=应用程序的入口main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象
public static void main(String[] args) {... // 仅贴出关键代码Looper.prepareMainLooper(); // 1\. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)// 方法逻辑类似Looper.prepare()// 注:prepare():为子线程中创建1个Looper对象ActivityThread thread = new ActivityThread(); // 2\. 创建主线程Looper.loop(); // 3\. 自动开启 消息循环 ->>下面将详细分析}
Looper.prepare()
为当前线程(子线程)创建1个循环器对象(Looper),同时也会自动创建1个对应的消息队列对象(MessageQueue)
需要在子线程中手动调用改方法
public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}// 1\. 判断sThreadLocal是否为null,否则抛出异常//即 Looper.prepare()方法不能被调用两次 = 1个线程中只能对应1个Looper实例// 注:sThreadLocal = 1个ThreadLocal对象,用于存储线程的变量sThreadLocal.set(new Looper(true));// 2\. 若为初次Looper.prepare(),则创建Looper对象 & 存放在ThreadLocal变量中// 注:Looper对象是存放在Thread线程里的// 源码分析Looper的构造方法->>分析a}/** * 分析a:Looper的构造方法**/private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);// 1\. 创建1个消息队列对象(MessageQueue)// 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)mRun = true;mThread = Thread.currentThread();}
创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象,即 主线程的Looper对象自动生成,不需手动生成;
而子线程的Looper对象则需手动通过Looper.prepare()创建,在子线程若不手动创建Looper对象 则无法生成Handler对象;
根据Handler的作用(在主线程更新UI),故Handler实例的创建场景 主要在主线程
生成Looper & MessageQueue对象后,则会自动进入消息循环:Looper.loop()
/** * 源码分析: Looper.loop()* 作用:消息循环,即从消息队列中获取消息、分发消息到Handler* 特别注意:* a. 主线程的消息循环不允许退出,即无限循环* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()*/public static void loop() {...// 仅贴出关键代码// 1\. 获取当前Looper的消息队列final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}// myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常// 即loop()执行前必须执行prepare(),从而创建1个Looper实例final MessageQueue queue = me.mQueue;// 获取Looper实例中的消息队列对象(MessageQueue)// 2\. 消息循环(通过for循环)for (;;) {// 2.1 从消息队列中取出消息Message msg = queue.next(); if (msg == null) {return;}// next():取出消息队列里的消息// 若取出的消息为空,则线程阻塞// ->> 分析1 // 2.2 派发消息到对应的Handlermsg.target.dispatchMessage(msg);// 把消息Message派发给消息对象msg的target属性// target属性实际是1个handler对象// ->>分析2// 3\. 释放消息占据的资源msg.recycle();}
}/** * 分析1:queue.next()* 定义:属于消息队列类(MessageQueue)中的方法* 作用:出队消息,即从 消息队列中 移出该消息*/Message next() {...// 仅贴出关键代码// 该参数用于确定消息队列中是否还有消息// 从而决定消息队列应处于出队消息状态 or 等待状态int nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态 nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序if (msg != null) {if (now
总结:
(1)消息循环的操作 = 消息出队 + 分发给对应的Handler实例
(2)分发给对应的Handler的过程:根据出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现 消息处理 的操作
(3)特别注意:在进行消息分发时(dispatchMessage(msg)),会进行1次发送方式的判断:
若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg)
/** * 具体使用*/private Handler mhandler = new Handler(){// 通过复写handlerMessage()从而确定更新UI的操作@Overridepublic void handleMessage(Message msg) {...// 需执行的UI操作}};/** * 源码分析:Handler的构造方法* 作用:初始化Handler对象 & 绑定线程* 注:* a. Handler需绑定 线程才能使用;绑定后,Handler的消息处理会在绑定的线程中执行* b. 绑定方式 = 先指定Looper对象,从而绑定了 Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)* c. 即:指定了Handler对象的 Looper对象 = 绑定到了Looper对象所在的线程*/public Handler() {this(null, false);// ->>分析1}
/** * 分析1:this(null, false) = Handler(null,false)*/public Handler(Callback callback, boolean async) {...// 仅贴出关键代码// 1\. 指定Looper对象mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}// Looper.myLooper()作用:获取当前线程的Looper对象;若线程无Looper对象则抛出异常// 即 :若线程中无创建Looper对象,则也无法创建Handler对象// 故 若需在子线程中创建Handler对象,则需先创建Looper对象// 注:可通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象// 2\. 绑定消息队列对象(MessageQueue)mQueue = mLooper.mQueue;// 获取该Looper对象中保存的消息队列对象(MessageQueue)// 至此,保证了handler对象 关联上 Looper对象中MessageQueue}
当创建Handler对象时,则通过 构造方法 自动关联当前线程的Looper对象 & 对应的消息队列对象(MessageQueue),从而 自动绑定了 实现创建Handler对象操作的线程
总结:
具体使用
Message msg = Message.obtain(); // 实例化消息对象msg.what = 1; // 消息标识msg.obj = "AA"; // 消息内容存放
源码分析
/** * 源码分析:Message.obtain()* 作用:创建消息对象* 注:创建Message对象可用关键字new 或 Message.obtain(),建议使用obtain()创建消息对象,避免每次都使用new重新分配内存。(当池内无消息对象可复用,则用关键词new创建)*/public static Message obtain() {// Message内部维护了1个Message池,用于Message消息对象的复用// 使用obtain()则是直接从池内获取synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}// 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存}// 若池内无消息对象可复用,则还是用关键字new创建return new Message();}
具体使用
mHandler.sendMessage(msg);
源码分析
/** * 源码分析&#xff1a;mHandler.sendMessage(msg)* 定义&#xff1a;属于处理器类&#xff08;Handler&#xff09;的方法* 作用&#xff1a;将消息 发送 到消息队列中&#xff08;Message ->> MessageQueue&#xff09;*/public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);// ->>分析1}/** * 分析1&#xff1a;sendMessageDelayed(msg, 0)**/public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis <0) {delayMillis &#61; 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() &#43; delayMillis);// ->> 分析2}/** * 分析2&#xff1a;sendMessageAtTime(msg, SystemClock.uptimeMillis() &#43; delayMillis)**/public boolean sendMessageAtTime(Message msg, long uptimeMillis) {// 1\. 获取对应的消息队列对象&#xff08;MessageQueue&#xff09;MessageQueue queue &#61; mQueue;// 2\. 调用了enqueueMessage方法 ->>分析3return enqueueMessage(queue, msg, uptimeMillis);}/** * 分析3&#xff1a;enqueueMessage(queue, msg, uptimeMillis)**/private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {// 1\. 将msg.target赋值为this// 即 &#xff1a;把 当前的Handler实例对象作为msg的target属性msg.target &#61; this;// 请回忆起上面说的Looper的loop()中消息循环时&#xff0c;会从消息队列中取出每个消息msg&#xff0c;然后执行msg.target.dispatchMessage(msg)去处理消息// 实际上则是将该消息派发给对应的Handler实例 // 2\. 调用消息队列的enqueueMessage&#xff08;&#xff09;// 即&#xff1a;Handler发送的消息&#xff0c;最终是保存到消息队列->>分析4return queue.enqueueMessage(msg, uptimeMillis&#xff09;;}/** * 分析4&#xff1a;queue.enqueueMessage(msg, uptimeMillis&#xff09;* 定义&#xff1a;属于消息队列类&#xff08;MessageQueue&#xff09;的方法* 作用&#xff1a;入队&#xff0c;即 将消息 根据时间 放入到消息队列中&#xff08;Message ->> MessageQueue&#xff09;* 采用单链表实现&#xff1a;提高插入消息、删除消息的效率*/boolean enqueueMessage(Message msg, long when) {...// 仅贴出关键代码synchronized (this) {msg.markInUse();msg.when &#61; when;Message p &#61; mMessages;boolean needWake;// 判断消息队列里有无消息// a. 若无&#xff0c;则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态&#xff0c;则唤醒if (p &#61;&#61; null || when &#61;&#61; 0 || when
// 最终回调Handler.handleMessage()处理消息
总结
Handler发送消息的本质 &#61;
将消息对象的target属性设置为当前Handler实例&#xff08;将Message绑定到Handler&#xff0c;使执行消息循环时将消息派发给对应的Handler实例&#xff09;
获取对应的消息队列对象MessageQueue&#xff0c;调用MessageQueue.enqueueMessage()&#xff0c;将Handler需发送消息入队到绑定线程的消息队列中。
之后&#xff0c;随着Looper对象的无限消息循环&#xff0c;不断从消息队列中取出Handler发送的消息&根据target分发到对应Handler&#xff0c;最终回调Handler.handleMessage()处理消息
具体使用
private Handler mhandler &#61; new Handler()&#xff1b;// 与方式1的使用不同&#xff1a;此处无复写Handler.handleMessage()
源码分析
/** * 源码分析&#xff1a;Handler的构造方法* 作用&#xff1a;* a. 在此之前&#xff0c;主线程创建时隐式创建Looper对象、MessageQueue对象* b. 初始化Handler对象、绑定线程 & 进入消息循环* 此处的源码分析类似方式1&#xff0c;此处不作过多描述*/
具体使用
mHandler.post(new Runnable() {&#64;Overridepublic void run() {//传入1个Ruunable对象&#xff0c;复写run()从而指定UI操作... // 需执行的UI操作 }});
源码分析
/** * 源码分析&#xff1a;Handler.post&#xff08;Runnable r&#xff09;* 定义&#xff1a;属于处理者类&#xff08;Handler&#xff09;中的方法* 作用&#xff1a;定义UI操作、将Runnable对象封装成消息对象 & 发送 到消息队列中&#xff08;Message ->> MessageQueue&#xff09;* 注&#xff1a;* a. 相比sendMessage()&#xff0c;post&#xff08;&#xff09;最大的不同在于&#xff0c;更新的UI操作可直接在重写的run&#xff08;&#xff09;中定义* b. 实际上&#xff0c;Runnable并无创建新线程&#xff0c;而是发送 消息 到消息队列中*/public final boolean post(Runnable r){return sendMessageDelayed(getPostMessage(r), 0);// getPostMessage(r) 的源码分析->>分析1// sendMessageDelayed&#xff08;&#xff09;的源码分析 ->>分析2}/** * 分析1&#xff1a;getPostMessage(r)* 作用&#xff1a;将传入的Runable对象封装成1个消息对象**/private static Message getPostMessage(Runnable r) {// 1\. 创建1个消息对象&#xff08;Message&#xff09;Message m &#61; Message.obtain();// 注&#xff1a;创建Message对象可用关键字new 或 Message.obtain()// 建议&#xff1a;使用Message.obtain()创建&#xff0c;// 原因&#xff1a;因为Message内部维护了1个Message池&#xff0c;用于Message的复用&#xff0c;使用obtain&#xff08;&#xff09;直接从池内获取&#xff0c;从而避免使用new重新分配内存// 2\. 将 Runable对象 赋值给消息对象&#xff08;message&#xff09;的callback属性m.callback &#61; r;// 3\. 返回该消息对象return m;} // 回到调用原处/** * 分析2&#xff1a;sendMessageDelayed(msg, 0)* 作用&#xff1a;实际上&#xff0c;从此处开始&#xff0c;则类似方式1 &#61; 将消息入队到消息队列&#xff0c;* 即 最终是调用MessageQueue.enqueueMessage&#xff08;&#xff09;**/public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis <0) {delayMillis &#61; 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() &#43; delayMillis);// 请看分析3}/** * 分析3&#xff1a;sendMessageAtTime(msg, SystemClock.uptimeMillis() &#43; delayMillis)**/public boolean sendMessageAtTime(Message msg, long uptimeMillis) {// 1\. 获取对应的消息队列对象&#xff08;MessageQueue&#xff09;MessageQueue queue &#61; mQueue;// 2\. 调用了enqueueMessage方法 ->>分析3return enqueueMessage(queue, msg, uptimeMillis);}/** * 分析4&#xff1a;enqueueMessage(queue, msg, uptimeMillis)**/private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {// 1\. 将msg.target赋值为this// 即 &#xff1a;把 当前的Handler实例对象作为msg的target属性msg.target &#61; this;// 请回忆起上面说的Looper的loop()中消息循环时&#xff0c;会从消息队列中取出每个消息msg&#xff0c;然后执行msg.target.dispatchMessage(msg)去处理消息// 实际上则是将该消息派发给对应的Handler实例 // 2\. 调用消息队列的enqueueMessage&#xff08;&#xff09;// 即&#xff1a;Handler发送的消息&#xff0c;最终是保存到消息队列return queue.enqueueMessage(msg, uptimeMillis&#xff09;;}// 注&#xff1a;实际上从分析2开始&#xff0c;源码 与 sendMessage&#xff08;Message msg&#xff09;发送方式相同
消息对象的创建 &#61; 内部 根据Runnable对象而封装
发送到消息队列的逻辑 &#61; 方式1中sendMessage&#xff08;Message msg&#xff09;
工作流程类似&#xff0c;区别在于
1、Handler.post不需外部创建消息对象&#xff0c;而是内部根据传入的Runnable对象封装消息对象
2、回调的消息处理方法是&#xff1a;复写Runnable对象的run()
Handler的一般用法 &#61; 新建Handler子类&#xff08;内部类&#xff09; 、匿名Handler内部类
/** * 方式1&#xff1a;新建Handler子类&#xff08;内部类&#xff09;*/ public class MainActivity extends AppCompatActivity {public static final String TAG &#61; "carson&#xff1a;";private Handler showhandler;// 主线程创建时便自动创建Looper & 对应的MessageQueue// 之后执行Loop()进入消息循环&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1\. 实例化自定义的Handler类对象->>分析1//注&#xff1a;此处并无指定Looper&#xff0c;故自动绑定当前线程(主线程)的Looper、MessageQueueshowhandler &#61; new FHandler();// 2\. 启动子线程1new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 1;// 消息标识msg.obj &#61; "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3\. 启动子线程2new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 2;// 消息标识msg.obj &#61; "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}// 分析1&#xff1a;自定义Handler子类class FHandler extends Handler {// 通过复写handlerMessage() 从而确定更新UI的操作&#64;Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.d(TAG, "收到线程1的消息");break;case 2:Log.d(TAG, " 收到线程2的消息");break;}}}}/** * 方式2&#xff1a;匿名Handler内部类*/ public class MainActivity extends AppCompatActivity {public static final String TAG &#61; "carson&#xff1a;";private Handler showhandler;// 主线程创建时便自动创建Looper & 对应的MessageQueue// 之后执行Loop()进入消息循环&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1\. 通过匿名内部类实例化的Handler类对象//注&#xff1a;此处并无指定Looper&#xff0c;故自动绑定当前线程(主线程)的Looper、MessageQueueshowhandler &#61; new Handler(){// 通过复写handlerMessage()从而确定更新UI的操作&#64;Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.d(TAG, "收到线程1的消息");break;case 2:Log.d(TAG, " 收到线程2的消息");break;}}};// 2\. 启动子线程1new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 1;// 消息标识msg.obj &#61; "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3\. 启动子线程2new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 2;// 消息标识msg.obj &#61; "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}
严重警告:This Handler class should be static or leaks might occur(null)
警告原因&#61;该Handler类由于没有设置为静态类&#xff0c;可能会导致内存泄露。
主线程的Looper对象的生命周期 &#61; 该应用程序的生命周期
在Java中&#xff0c;非静态内部类 & 匿名内部类都默认持有 外部类的引用
/** * 方式1&#xff1a;新建Handler子类&#xff08;内部类&#xff09;*/ public class MainActivity extends AppCompatActivity {public static final String TAG &#61; "carson&#xff1a;";private Handler showhandler;// 主线程创建时便自动创建Looper & 对应的MessageQueue// 之后执行Loop()进入消息循环&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1\. 实例化自定义的Handler类对象->>分析1//注&#xff1a;此处并无指定Looper&#xff0c;故自动绑定当前线程(主线程)的Looper、MessageQueueshowhandler &#61; new FHandler();// 2\. 启动子线程1new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 1;// 消息标识msg.obj &#61; "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3\. 启动子线程2new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 2;// 消息标识msg.obj &#61; "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}// 分析1&#xff1a;自定义Handler子类class FHandler extends Handler {// 通过复写handlerMessage() 从而确定更新UI的操作&#64;Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.d(TAG, "收到线程1的消息");break;case 2:Log.d(TAG, " 收到线程2的消息");break;}}}}/** * 方式2&#xff1a;匿名Handler内部类*/ public class MainActivity extends AppCompatActivity {public static final String TAG &#61; "carson&#xff1a;";private Handler showhandler;// 主线程创建时便自动创建Looper & 对应的MessageQueue// 之后执行Loop()进入消息循环&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1\. 通过匿名内部类实例化的Handler类对象//注&#xff1a;此处并无指定Looper&#xff0c;故自动绑定当前线程(主线程)的Looper、MessageQueueshowhandler &#61; new Handler(){// 通过复写handlerMessage()从而确定更新UI的操作&#64;Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.d(TAG, "收到线程1的消息");break;case 2:Log.d(TAG, " 收到线程2的消息");break;}}};// 2\. 启动子线程1new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 1;// 消息标识msg.obj &#61; "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3\. 启动子线程2new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 2;// 消息标识msg.obj &#61; "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}
}
从上述示例代码可知&#xff1a;
上述的Handler实例的消息队列有2个分别来自线程1、2的消息&#xff08;分别 为延迟1s、6s&#xff09;
在Handler消息队列 还有未处理的消息 / 正在处理消息时&#xff0c;消息队列中的Message持有Handler实例的引用
由于Handler &#61; 非静态内部类 / 匿名内部类&#xff08;2种使用方式&#xff09;&#xff0c;故又默认持有外部类的引用&#xff08;即MainActivity实例&#xff09;&#xff0c;引用关系如下图
上述的引用关系会一直保持&#xff0c;直到Handler消息队列中的所有消息被处理完毕
在Handler消息队列 还有未处理的消息 / 正在处理消息时&#xff0c;此时若需销毁外部类MainActivity&#xff0c;但由于上述引用关系&#xff0c;垃圾回收器&#xff08;GC&#xff09;无法回收MainActivity&#xff0c;从而造成内存泄漏。如下图&#xff1a;
&#xff08;1&#xff09;当Handler消息队列 还有未处理的消息 / 正在处理消息时&#xff0c;存在引用关系&#xff1a; “未被处理 / 正处理的消息 -> Handler实例 -> 外部类”
&#xff08;2&#xff09;若出现 Handler的生命周期 > 外部类的生命周期 时&#xff08;即 Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁时&#xff09;&#xff0c;将使得外部类无法被垃圾回收器&#xff08;GC&#xff09;回收&#xff0c;从而造成 内存泄露
从上面可看出&#xff0c;造成内存泄露的原因有2个关键条件&#xff1a;
1、存在“未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系
2、Handler的生命周期 > 外部类的生命周期
&#xff08;1&#xff09;原理
1、将Handler的子类设置成 静态内部类&#xff1a;默认不持有外部类的引用&#xff0c;从而使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 的引用关系 不复存在。
2、使用WeakReference弱引用持有Activity实例&#xff1a;弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时&#xff0c;一旦发现了只具有弱引用的对象&#xff0c;不管当前内存空间足够与否&#xff0c;都会回收它的内存
&#xff08;2&#xff09;解决代码
public class MainActivity extends AppCompatActivity {public static final String TAG &#61; "carson&#xff1a;";private Handler showhandler;// 主线程创建时便自动创建Looper & 对应的MessageQueue// 之后执行Loop()进入消息循环&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1\. 实例化自定义的Handler类对象->>分析1//注&#xff1a;// a. 此处并无指定Looper&#xff0c;故自动绑定当前线程(主线程)的Looper、MessageQueue&#xff1b;// b. 定义时需传入持有的Activity实例&#xff08;弱引用&#xff09;showhandler &#61; new FHandler(this);// 2\. 启动子线程1new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 1;// 消息标识msg.obj &#61; "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3\. 启动子线程2new Thread() {&#64;Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg &#61; Message.obtain();msg.what &#61; 2;// 消息标识msg.obj &#61; "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}// 分析1&#xff1a;自定义Handler子类// 设置为&#xff1a;静态内部类private static class FHandler extends Handler{// 定义 弱引用实例private WeakReference
}
&#xff08;1&#xff09;原理
当 外部类&#xff08;此处以Activity为例&#xff09; 结束生命周期时&#xff08;此时系统会调用onDestroy&#xff08;&#xff09;&#xff09;&#xff0c;清除 Handler消息队列里的所有消息&#xff08;调用removeCallbacksAndMessages(null)&#xff09;
不仅使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 不复存在&#xff0c;同时 使得 Handler的生命周期&#xff08;即 消息存在的时期&#xff09; 与 外部类的生命周期 同步
&#xff08;2&#xff09;解决代码
&#64;Overrideprotected void onDestroy() {super.onDestroy();mHandler.removeCallbacksAndMessages(null);// 外部类Activity生命周期结束时&#xff0c;同时清空消息队列 & 结束Handler生命周期}
通过创建一个Handler子类的对象&#xff0c;每个acvivity只需一个Handler对象。后台进程可通过两种方式Handler进行通信&#xff1a;message和Runnable对象&#xff0c;其结果实质都是将在Handler的队列中放入内容&#xff0c;message是放置信息&#xff0c;可以传递一些参数&#xff0c;Handler获取这些信息并将判度如何处理&#xff0c;而Runnable则是直接给出处理的方法。队列就是依次执行&#xff0c;Handler会处理完一个消息或者执行完某个处理在进行下一步&#xff0c;这样不会出现多个线程同时要求进行UI处理而引发的混乱现象。
这些队列中的内容&#xff08;无论Message还是Runnable&#xff09;可以要求马上执行&#xff0c;延迟一定时间执行或者指定某个时刻执行&#xff0c;如果将他们放置在队列头&#xff0c;则表示具有最高有限级别&#xff0c;立即执行。这些函数包括有:sendMessage(), sendMessageAtFrontOfQueue(), sendMessageAtTime(), sendMessageDelayed()以及用于在队列中加入Runnable的post(), postAtFrontOfQueue(), postAtTime(),postDelay()。
简历首选内推方式&#xff0c;速度快&#xff0c;效率高啊&#xff01;然后可以在拉钩&#xff0c;boss&#xff0c;脉脉&#xff0c;大街上看看。简历上写道熟悉什么技术就一定要去熟悉它&#xff0c;不然被问到不会很尴尬&#xff01;做过什么项目&#xff0c;即使项目体量不大&#xff0c;但也一定要熟悉实现原理&#xff01;不是你负责的部分&#xff0c;也可以看看同事是怎么实现的&#xff0c;换你来做你会怎么做&#xff1f;做过什么&#xff0c;会什么是广度问题&#xff0c;取决于项目内容。但做过什么&#xff0c;达到怎样一个境界&#xff0c;这是深度问题&#xff0c;和个人学习能力和解决问题的态度有关了。大公司看深度&#xff0c;小公司看广度。大公司面试你会的&#xff0c;小公司面试他们用到的你会不会&#xff0c;也就是岗位匹配度。
面试过程一定要有礼貌&#xff01;即使你觉得面试官不尊重你&#xff0c;经常打断你的讲解&#xff0c;或者你觉得他不如你&#xff0c;问的问题缺乏专业水平&#xff0c;你也一定要尊重他&#xff0c;谁叫现在是他选择你&#xff0c;等你拿到offer后就是你选择他了。
另外&#xff0c;描述问题一定要慢&#xff01;不要一下子讲一大堆&#xff0c;慢显得你沉稳、自信&#xff0c;而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide&#xff0c;所以会有个弊端&#xff0c;当我们在面试讲解很容易不知道某个方法怎么读&#xff0c;这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准&#xff0c;有些面试官不耐烦会说“你到底说的是哪个&#xff1f;”这时我们会容易乱了阵脚。正确的发音&#43;沉稳的描述&#43;好听的嗓音决对是一个加分项&#xff01;
最重要的是心态&#xff01;心态&#xff01;心态&#xff01;重要事情说三遍&#xff01;面试时间很短&#xff0c;在短时间内对方要摸清你的底子还是比较不现实的&#xff0c;所以&#xff0c;有时也是看眼缘&#xff0c;这还是个看脸的时代。
希望大家都能找到合适自己满意的工作&#xff01;
如果需要PDF版本可以在GitHub中自行领取&#xff01;
进阶学习视频
附上&#xff1a;我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 &#xff08;含BAT、小米、华为、美团、滴滴&#xff09;和我自己整理Android复习笔记&#xff08;包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。&#xff09;
sageDelayed()以及用于在队列中加入Runnable的post(), postAtFrontOfQueue(), postAtTime(),postDelay()。
简历首选内推方式&#xff0c;速度快&#xff0c;效率高啊&#xff01;然后可以在拉钩&#xff0c;boss&#xff0c;脉脉&#xff0c;大街上看看。简历上写道熟悉什么技术就一定要去熟悉它&#xff0c;不然被问到不会很尴尬&#xff01;做过什么项目&#xff0c;即使项目体量不大&#xff0c;但也一定要熟悉实现原理&#xff01;不是你负责的部分&#xff0c;也可以看看同事是怎么实现的&#xff0c;换你来做你会怎么做&#xff1f;做过什么&#xff0c;会什么是广度问题&#xff0c;取决于项目内容。但做过什么&#xff0c;达到怎样一个境界&#xff0c;这是深度问题&#xff0c;和个人学习能力和解决问题的态度有关了。大公司看深度&#xff0c;小公司看广度。大公司面试你会的&#xff0c;小公司面试他们用到的你会不会&#xff0c;也就是岗位匹配度。
面试过程一定要有礼貌&#xff01;即使你觉得面试官不尊重你&#xff0c;经常打断你的讲解&#xff0c;或者你觉得他不如你&#xff0c;问的问题缺乏专业水平&#xff0c;你也一定要尊重他&#xff0c;谁叫现在是他选择你&#xff0c;等你拿到offer后就是你选择他了。
另外&#xff0c;描述问题一定要慢&#xff01;不要一下子讲一大堆&#xff0c;慢显得你沉稳、自信&#xff0c;而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide&#xff0c;所以会有个弊端&#xff0c;当我们在面试讲解很容易不知道某个方法怎么读&#xff0c;这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准&#xff0c;有些面试官不耐烦会说“你到底说的是哪个&#xff1f;”这时我们会容易乱了阵脚。正确的发音&#43;沉稳的描述&#43;好听的嗓音决对是一个加分项&#xff01;
最重要的是心态&#xff01;心态&#xff01;心态&#xff01;重要事情说三遍&#xff01;面试时间很短&#xff0c;在短时间内对方要摸清你的底子还是比较不现实的&#xff0c;所以&#xff0c;有时也是看眼缘&#xff0c;这还是个看脸的时代。
希望大家都能找到合适自己满意的工作&#xff01;
如果需要PDF版本可以在GitHub中自行领取&#xff01;
进阶学习视频
[外链图片转存中…(img-Qn7UrxWC-1612426315641)]
附上&#xff1a;我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 &#xff08;含BAT、小米、华为、美团、滴滴&#xff09;和我自己整理Android复习笔记&#xff08;包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。&#xff09;
[外链图片转存中…(img-QwqD3Rcz-1612426315642)]