2024年Android最全Android 面试总结 - View,2024年最新蚂蚁金服社招三面

结尾

  • 腾讯T4级别Android架构技术脑图;查漏补缺,体系化深入学习提升

img

  • 一线互联网Android面试题含详解(初级到高级专题)

这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率

img

有Android开发3-5年基础,希望突破瓶颈,成为架构师的小伙伴,可以关注我

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

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

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

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

}

}

}

Logcat 日志:

2021-08-08 17:08:05.156 E/MainActivity: btn: 308 168

这样简单的方法为什么可以这么方便的获取宽高呢?

先看看 View.post 的方法做了啥:

// android.view.View

public class View implements Drawable.Callback, KeyEvent.Callback,

AccessibilityEventSource {

/**

  • Causes the Runnable to be added to the message queue.

  • The runnable will be run on the user interface thread.

  • @param action The Runnable that will be executed.

  • @return Returns true if the Runnable was successfully placed in to the

  •     message queue.  Returns false on failure, usually because the
    
  •     looper processing the message queue is exiting.
    
  • @see #postDelayed

  • @see #removeCallbacks

*/

public boolean post(Runnable action) {

// 1

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

return attachInfo.mHandler.post(action);

}

// Postpone the runnable until we know on which thread it needs to run.

// Assume that the runnable will be successfully placed after attach.

// 2

getRunQueue().post(action);

return true;

}

}

View 的 post 方法传入了 Runnable 对象。方法注释上说:将Runnable添加到消息队列。Runnable将在用户界面线程上运行。消息队列 message queue 不就是 Handler 机制中的 MessageQueue 吗,照这注释上的意思是传进来的 Runnable 对象最终通过 Handler 来执行到主线程。到底是不是这样,我们接着往下看。

注释 1 处 mAttachInfo 对象是 ViewRootImpl 的构造方法中创建的,然后经过层层 View 的 dispatchAttachedToWindow 方法传递给 View 的 mAttachInfo 的。当我们在生命周期 onCreate、onStart() 和 onResume() 时,ViewRootImpl 还没有被创建,所以 mAttachInfo 对象为空。一会儿梳理下 ViewRootImpl 在什么时候创建的。注释 2 处调用了 getRunQueue() 的 post 并传入 Runnable 对象

private HandlerActionQueue mRunQueue;

// android.view.View#getRunQueue()

private HandlerActionQueue getRunQueue() {

if (mRunQueue == null) {

mRunQueue = new HandlerActionQueue();

}

return mRunQueue;

}

如果 mRunQueue 对象为空,则创建,最后返回 HandlerActionQueue 类型的对象 mRunQueue。接下来看 HandlerActionQueue。

// android.view.HandlerActionQueue

public class HandlerActionQueue {

private HandlerAction[] mActions;

private int mCount;

public void post(Runnable action) {

postDelayed(action, 0);

}

public void postDelayed(Runnable action, long delayMillis) {

final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

synchronized (this) {

if (mActions == null) {

mActions = new HandlerAction[4];

}

mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);

mCount++;

}

}

public void executeActions(Handler handler) {

synchronized (this) {

final HandlerAction[] actions = mActions;

for (int i = 0, count = mCount; i < count; i++) {

final HandlerAction handlerAction = actions[i];

handler.postDelayed(handlerAction.action, handlerAction.delay);

}

mActions = null;

mCount = 0;

}

}

private static class HandlerAction {

final Runnable action;

final long delay;

public HandlerAction(Runnable action, long delay) {

this.action = action;

this.delay = delay;

}

public boolean matches(Runnable otherAction) {

return otherAction == null && action == null

|| action != null && action.equals(otherAction);

}

}

}

HandlerActionQueue 内部维护了一个初试大小为 4 的数组,HandlerAction 是 Runnable 对象和 delay 延时时间的一个包装类。HandlerActionQueue 的 post 方法调用了 postDelayed 传入了 Runnable 对象,第二个参数延时时间固定为 0,postDelayed 方法将 Runnable 对象和延时时间包装为 HandlerAction 对象并添加到数组中。

到这儿,View.post 方法就走完了。上面一会儿梳理下 ViewRootImpl 在什么时候创建的,现在来看下。

ViewRootImpl 在什么时候创建的


ViewRootImpl 是我们 View 体系中非常重要的类,其中 绘制、布局、测量,刷新布局等等都是在它内部发起的,而且 Activity 是和 ViewRootImpl 一一对应的,也就是一个 Activity 都有一个 ViewRootImpl 对象。具体我们在这里不在延伸了。

我们知道 Activity 的启动流程会走 ActivityThread 的 performLaunchActivity 方法,其中会调用到 Activity 的 onCreate,同样 onStart 会在 ActivityThread 的 performStart 方法调用,onResume 会在 performResumeActivity 中调用。看下这三个生命周期方法 debug 截图。

onCreate.png

onStart.png

onResume.png

在这里只关注 onResume,从 ActivityThread 的 performResumeActivity 方法开始:

// android.app.ActivityThread

public final class ActivityThread extends ClientTransactionHandler {

@Override

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,

String reason) {

// TODO Push resumeArgs into the activity for consideration

// 1

final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

if (r.window == null && !a.mFinished && willBeVisible) {

r.window = r.activity.getWindow();

View decor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

ViewManager wm = a.getWindowManager();

WindowManager.LayoutParams l = r.window.getAttributes();

a.mDecor = decor;

l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

l.softInputMode |= forwardBit;

if (r.mPreserveWindow) {

a.mWindowAdded = true;

r.mPreserveWindow = false;

// Normally the ViewRoot sets up callbacks with the Activity

// in addView->ViewRootImpl#setView. If we are instead reusing

// the decor view we have to notify the view root that the

// callbacks may have changed.

ViewRootImpl impl = decor.getViewRootImpl();

if (impl != null) {

impl.notifyChildRebuilt();

}

}

if (a.mVisibleFromClient) {

if (!a.mWindowAdded) {

a.mWindowAdded = true;

// 2

wm.addView(decor, l);

} else {

a.onWindowAttributesChanged(l);

}

}

// If the window has already been added, but during resume

// we started another activity, then don’t yet make the

// window visible.

} else if (!willBeVisible) {

if (localLOGV) Slog.v(TAG, “Launch " + r + " mStartedActivity set”);

r.hideForNow = true;

}

}

}

performResumeActivity 方法中,注释 1 处调用了 performResumeActivity 方法,最终会调用 Activity 的 onResume 方法。注释 2 处调用了 WindowManagerImpl 对象 wm 的 addView 方法。

// android.view.WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {

@UnsupportedAppUsage

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {

applyDefaultToken(params);

mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,

mContext.getUserId());

}

}

WindowManagerImpl 的 addView 方法内部调用了 WindowManagerGlobal 对象 mGlobal 的 addView 方法。

// android.view.WindowManagerGlobal

public final class WindowManagerGlobal {

public void addView(View view, ViewGroup.LayoutParams params,

Display display, Window parentWindow, int userId) {

// 1

ViewRootImpl root;

View panelParentView = null;

synchronized (mLock) {

// 2

root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

// do this last because it fires off messages to start doing things

try {

// 3

root.setView(view, wparams, panelParentView, userId);

} catch (RuntimeException e) {

// BadTokenException or InvalidDisplayException, clean up.

if (index >= 0) {

removeViewLocked(index, true);

}

throw e;

}

}

}

}

在注释 1 处声明了 ViewRootImpl 类型变量 root;在注释 2 处创建了 ViewRootImpl 对象并赋值给 root;在注释 3 处调用了 ViewRootImpl 的 setView 方法。

到这儿 ViewRootImpl 的创建过程梳理完了。了解 ViewRootImpl 的创建过程 Activity 的显示流程非常有帮助,因为 Activity 的显示就是从 ViewRootImpl 创建后调用 setView 方法开始的。

Activity 是什么时候显示的


// android.view.ViewRootImpl

public final class ViewRootImpl implements ViewParent,

View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

/**

  • We have one child

*/

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,

int userId) {

// Schedule the first layout -before- adding to the window

// manager, to make sure we do the relayout before receiving

// any other events from the system.

requestLayout();

}

@Override

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

@UnsupportedAppUsage

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

// 1

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

// 2

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

final class TraversalRunnable implements Runnable {

@Override

public void run() {

doTraversal();

}

}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

void doTraversal() {

if (mTraversalScheduled) {

mTraversalScheduled = false;

// 1

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

if (mProfile) {

Debug.startMethodTracing(“ViewAncestor”);

}

// 2

performTraversals();

if (mProfile) {

Debug.stopMethodTracing();

mProfile = false;

}

}

}

private void performTraversals() {

// Execute enqueued actions on every traversal in case a detached view enqueued an action

// 1

getRunQueue().executeActions(mAttachInfo.mHandler);

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

performLayout(lp, mWidth, mHeight);

performDraw();

}

}

ViewRootImpl 的 setView 方法中调用了 requestLayout 方法。到这儿调用了应用开发中经常调用的方法 requestLayout。requestLayout 方法调用了 scheduleTraversals 方法,在 scheduleTraversals 方法中注释 1 处添加了同步屏障,简单来说添加同步屏障的目的是让 Handler 消息队列中先不执行同步消息,遇到异步消息就执行,在异步消息执行后,需要主动移除同步屏障(这是 Handler 中很重要的知识点,后面专门文章学习 Handler)。在 scheduleTraversals 方法的注释 2 处,调用 mChoreographer 的 postCallback 方法,传入 mTraversalRunnable 对象,这里设计屏幕刷新机制,简单的说,等下次 Vsync 信号来的时候回调用 mTraversalRunnable 的 run (后面专门文章学习屏幕刷新机制)。TraversalRunnable 的 run 方法中调用了 doTraversal 方法,在 doTraversal 方法中注释 1 处移除了同步屏障,注释 2 处调用了 performTraversals 方法。在 performTraversals 方法中注释 1 处调用了 getRunQueue 方法返回的 HandlerActionQueue 对象的 executeActions 方法,传入了 mAttachInfo.mHandler,现在需要确定下 mAttachInfo.mHandler 这个 Handler 是否是主线程的 Handler,还有就是 HandlerActionQueue 的 executeActions 是怎么处理的。

先来看看 mAttachInfo.mHandler:AttachInfo 是 View 的静态内部类,

// android.view.View

public final class View {

final static class AttachInfo {

/**

  • A Handler supplied by a view’s {@link android.view.ViewRootImpl}. This

  • handler can be used to pump events in the UI events queue.

*/

final Handler mHandler;

/**

  • Creates a new set of attachment information with the specified

  • events handler and thread.

  • @param handler the events handler the view must use

*/

AttachInfo(IWindowSession session, IWindow window, Display display,

ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,

Context context) {

mSession = session;

mWindow = window;

mWindowToken = window.asBinder();

mDisplay = display;

mViewRootImpl = viewRootImpl;

mHandler = handler;

mRootCallbacks = effectPlayer;

尾声

如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

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

这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

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

架构篇

《Jetpack全家桶打造全新Google标准架构模式》

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

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

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

集中精力,对基础和重要的事情做深度研究。

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

这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。[外链图片转存中…(img-tkTxqq8k-1715608910886)]

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

[外链图片转存中…(img-SWcWu0xI-1715608910887)]

架构篇

《Jetpack全家桶打造全新Google标准架构模式》
[外链图片转存中…(img-Cnw8Ub20-1715608910887)]

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值