2024年安卓最新Android 面试总结 - View(1),头条测试开发面试题

尾声

在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:


展示学习笔记

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

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

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

如果 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;

mTreeObserver = new ViewTreeObserver(context);

}

}

}

mHandler 对象是在 AttachInfo 的构造函数中赋值的。

ViewRootImpl 的构造函数:

// android.view.ViewRootImpl

public final class ViewRootImpl implements ViewParent,

View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

final ViewRootHandler mHandler = new ViewRootHandler();

public ViewRootImpl(Context context, Display display, IWindowSession session,

boolean useSfChoreographer) {

mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,

context);

}

}

mAttachInfo 是在 ViewRootImpl 的构造函数中创建的,并且传入了 mHandler 对象。mHandler 的类型是 ViewRootHandler。

final class ViewRootHandler extends Handler {}

ViewRootHandler 继承了 Handler,mHandler 对象被创建时使用的无参构造方法。所以 new ViewRootHandler() 会调用父类的构造方法 Handler()

// android.os.Handler

public class Handler {

public Handler() {

this(null, false);

}

public Handler(@Nullable Callback callback, boolean async) {

if (FIND_POTENTIAL_LEAKS) {

final Class<? extends Handler> klass = getClass();

if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&

(klass.getModifiers() & Modifier.STATIC) == 0) {

Log.w(TAG, "The following Handler class should be static or leaks might occur: " +

klass.getCanonicalName());

}

}

// 1

mLooper = Looper.myLooper();

if (mLooper == null) {

throw new RuntimeException(

"Can’t create handler inside thread " + Thread.currentThread()

  • " that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

尾声

开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。再等等,说不定下一个黄金浪潮就被你等到了。

  • 330页 PDF Android核心笔记

  • 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题

  • PDF和思维脑图,包含知识脉络 + 诸多细节

  • Android进阶系统学习视频

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

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

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

尾声

开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。再等等,说不定下一个黄金浪潮就被你等到了。

  • 330页 PDF Android核心笔记

[外链图片转存中…(img-b7Lp2jYc-1715803120270)]

  • 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题

[外链图片转存中…(img-wmFi06Yg-1715803120270)]

[外链图片转存中…(img-PZrMGwNv-1715803120271)]

  • PDF和思维脑图,包含知识脉络 + 诸多细节

[外链图片转存中…(img-LG5FpRWI-1715803120271)]

  • Android进阶系统学习视频

[外链图片转存中…(img-yANPDRPJ-1715803120271)]

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值