2024年Android最新【问答集】Android主线程任务队列耗时如何分析?(1),2024年最新最新Android高级面试题汇总

尾声

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

    final MessageQueue queue = me.mQueue;



    for (;;) {

        Message msg = queue.next(); // might block

        if (msg == null) {

            // No message indicates that the message queue is quitting.

            

            // Q1、这个地方为什么可以return呢?第一个猜测是系统对主线程的message进行了非null验证,保证了到这里的msg不会为null,不然主线程的loop方法都退出了。第二个猜测是系统有什么方法可以把loop方法再调用一下,保证循环可用。这个需要再做研究。

            return;

        }



        // here1:关键就在这里,开始的时候,会去打印一条日志

        

        // This must be in a local variable, in case a UI event sets the logger

        final Printer logging = me.mLogging;

        if (logging != null) {

            logging.println(">>>>> Dispatching to " + msg.target + " " +

                    msg.callback + ": " + msg.what);

        }

        	// here2:msg分发给对应的target(也就是handler)进行处理

            msg.target.dispatchMessage(msg);



        // here3:关键就在这里,结束的时候,也会去打印一条日志,通过计算两条日志的时间差,就可以得到每条消息的处理时间

        if (logging != null) {

            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);

        }

    }

}



分析:



1、主要的方法就是靠注释:here1-here3



2、分析源码的时候新发现了一个问题(注释:Q1),要去思考一下。



#### []( )验证1——生命周期耗时



public class MainActivity extends AppCompatActivity {

private static final String TAG = "MainActivity";

private long lastTime;



@Override

protected void onCreate(Bundle savedInstanceState) {

    long startTime = System.currentTimeMillis();

    Log.i(TAG, "onCreate:" + startTime);



    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    mockSpendTimeOperate();

    

    Looper.myLooper().setMessageLogging(new Printer() {

        @Override

        public void println(String x) {

            Log.i(TAG, x);

            if (lastTime != 0L) {

                long spendTime = System.currentTimeMillis() - lastTime;

                lastTime = 0L;

                Log.i(TAG, "println: " + spendTime);

            } else {

                lastTime = System.currentTimeMillis();

            }

        }

    });

    Log.i(TAG, "onCreate:" + (System.currentTimeMillis() - startTime));

}



private void mockSpendTimeOperate() {

    try {

        Thread.sleep(1000L);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

}

}




这里我预期的是:能够把onCreate等生命周期方法的执行时间给打印出来,但是结果和预期并不符合。



##### []( )结果:



![image-20210802204749204.png](https://img-blog.csdnimg.cn/img_convert/20bf637e244bdc9f9ce26e958d5c53df.png)



失败了。。。



用这种方法是统计不到的 Activity 的生命周期方法。



> Q2:为什么会统计不到呢?

> 

> 是我我这种方法不对?还是原理就没理解好呢?

> 

> 看来还是要对Activity的生命周期方法调用过程再做一个分析。



> Q3:那统计耗时的方法有哪些呢?

> 

> 1、手动加日志,再计算

> 

> 2、字节码插入日志代码进行统计(忘了叫啥了,这个得再学一下)

> 

> 3、还有啥呢?



那既然不能统计生命周期的方法,就再看看它能不能统计到我们自己发的msg耗时:



#### []( )验证2:统计我们自己写的代码、发的msg



public class MainActivity extends AppCompatActivity {

private static final String TAG = "MainActivity";

private long lastTime;



@Override

protected void onCreate(Bundle savedInstanceState) {

    long startTime = System.currentTimeMillis();

    Log.i(TAG, "onCreate:" + startTime);



    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);



    // here:就用一个点击事件来测一下

    Button btnTestMsg = findViewById(R.id.btn_testMsg);

    btnTestMsg.setOnClickListener(v -> mockSpendTimeOperate());



    mockSpendTimeOperate();



    // 首先拿到 主线程的 looper,把Printer对象设置进去。

    // 这里的目的就是统计耗时

    Looper.myLooper().setMessageLogging(new Printer() {

        @Override

        public void println(String x) {

            Log.i(TAG, x);

            if (lastTime != 0L) {

                long spendTime = System.currentTimeMillis() - lastTime;

                lastTime = 0L;

                Log.i(TAG, "msg spendTime: " + spendTime);

            } else {

                lastTime = System.currentTimeMillis();

            }

        }

    });

    Log.i(TAG, "onCreate:" + (System.currentTimeMillis() - startTime));

}



private void mockSpendTimeOperate() {

    try {

        Thread.sleep(1000L);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

}

}




这里用点击事件来测一下,能不能打印出时间来呢?



##### []( )结果:



![image-20210802215027114.png](https://img-blog.csdnimg.cn/img_convert/24cca54ea71f4b0b703c406c15d9c7d2.png)



这次和预期的完全符合了



然后再来看看系统给的日志:



> Dispatching to Handler (android.view.ViewRootImpl V i e w R o o t H a n d l e r ) 770 e e c 3 a n d r o i d . v i e w . V i e w ViewRootHandler) {770eec3} android.view.View ViewRootHandler)770eec3android.view.ViewPerformClick@9ede30f: 0



![image-20210802215316683.png](https://img-blog.csdnimg.cn/img_convert/47ce7eb2761580c80e6de143f60e619a.png)



**所以Button点击事件的msg信息为:**



target:ViewRootImpl$ViewRootHandler,处理msg的handler。也是发送这条msg的Handler。



callback:android.view.View$PerformClick,msg的callback实际就是一个Runnable,就是一坨可执行的代码。



Message.java



/package/ Runnable callback;




callback和handlerMessage方法的关系如下:



Handler.java



public void dispatchMessage(@NonNull Message msg) {

if (msg.callback != null) {

    handleCallback(msg);

} else {

    if (mCallback != null) {

        if (mCallback.handleMessage(msg)) {

            return;

        }

    }

    handleMessage(msg);

}

}

private static void handleCallback(Message message) {

    message.callback.run();

}



msg.what:找到具体的事件,一般都是通过Switch去匹配。



**因此,Button的点击就执行了callback的run方法,如下:**



private final class PerformClick implements Runnable {

@Override

public void run() {

    recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);

    performClickInternal();

}

}

private boolean performClickInternal() {

    // Must notify autofill manager before performing the click actions to avoid scenarios where

    // the app has a click listener that changes the state of views the autofill service might

    // be interested on.

    notifyAutofillManagerOnClick();



    return performClick();

}



public boolean performClick() {

    // We still need to call this method to handle the cases where performClick() was called

    // externally, instead of through performClickInternal()

    notifyAutofillManagerOnClick();



    final boolean result;

    final ListenerInfo li = mListenerInfo;

    if (li != null && li.mOnClickListener != null) {

        playSoundEffect(SoundEffectConstants.CLICK);

        

        // here1:执行了我们的onClick事件

        li.mOnClickListener.onClick(this);

        result = true;

    } else {

        result = false;

    }



    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);



    notifyEnterOrExitForAutoFillIfNeeded(true);



    return result;

}



看注释here1处,最终执行了我们的onClick方法。



**那还有一个问题?msg是在哪里发送的呢?**



首先我们知道发送msg的Handler是android.view.ViewRootImpl$ViewRootHandler,但一个Handler是可以在多处使用的,我们试试这样找:



在点击事件里把调用栈打印出来:



    Button btnTestMsg = findViewById(R.id.btn_testMsg);

    btnTestMsg.setOnClickListener(v -> {

        mockSpendTimeOperate();

        Exception dumpStack = new Exception("dumpStack");

        dumpStack.printStackTrace();

    });



![image-20210802222659171.png](https://img-blog.csdnimg.cn/img_convert/7b0486c762a026f7997041450103460e.png)



很可惜,毫无收获,思路不对。



那我们再回过去看看



private final class PerformClick implements Runnable {

@Override

public void run() {

    recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);

    performClickInternal();

}

}




前面说了,最终会调用这坨代码,那看看它在哪调用的就好了(即这个callback是在哪设置给msg的)。



好在幸运的是,这个 PerformClick 是private的。



直接搜素 `new PerformClick()`找到唯一使用地方:



![image-20210802223357490.png](https://img-blog.csdnimg.cn/img_convert/917a4919684bdc1378e3580f47e5e352.png)



是在 `onTouchEvent()`中的。看到这肯定就知道是走的事件分发了,一切都串联了起来。正着分析,反着分析,都是一样的。



也就是从 Activity 传到 view 的那套东西(事件方法:Activity-Window-DecorView-ViewGroup)了。



但到这里还是没有回答:msg是从哪里发出来的呢???



> Q4:问题又来了,点击事件的msg是从哪里发出来的呢?

> 

> 还没搞懂,再朝着这个方向学一下。

> 



### 面试复习笔记:

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。


**《960页Android开发笔记》**

![](https://img-blog.csdnimg.cn/img_convert/2c5e307f3eed6cfefcd723038a10d051.webp?x-oss-process=image/format,png)

**《1307页Android开发面试宝典》**

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

![](https://img-blog.csdnimg.cn/img_convert/82f747a90bd1d26bc24dc14d6682ff2d.webp?x-oss-process=image/format,png)

**《507页Android开发相关源码解析》**

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。


**《960页Android开发笔记》**

[外链图片转存中...(img-ONCiZh0V-1715670815245)]

**《1307页Android开发面试宝典》**

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

[外链图片转存中...(img-mrFzqVb9-1715670815246)]

**《507页Android开发相关源码解析》**

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值