Android 10.0 滑动解锁流程(上)

简介: Android 10.0 滑动解锁流程(上)

学习笔记:

滑动解锁相对于来说逻辑还是简单的,说白了就是对事件的处理,然后做一些事。

这里主要从锁屏的界面Layout结构、touchEvent事件分发、解锁动作逻辑几个方面进行源码的分析。

锁屏的界面Layout结构分析

StatusbarWindowView

整个锁屏界面的顶级 View 就是 StatusbarWindowView;

StatusBar#createAndAddWindows()

// StatusBar.java
    public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
        makeStatusBarView(result);
        mNotificationShadeWindowController.attach();
        // 添加视图
        mStatusBarWindowController.attach();
    }

StatusBarWindowController#attach()

// StatusBarWindowController.java
    public void attach() {
        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                mBarHeight,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.setFitInsetsTypes(0 /* types */);
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        mWindowManager.addView(mStatusBarView, mLp);
        mLpChanged.copyFrom(mLp);
    }

StatusBarWindow 是在 StatusBar 的 createAndAddWindows() 流程中调用StatusBarWindowController.attach() 添加到窗口上的, type为WindowManager.LayoutParams.TYPE_STATUS_BAR

Layout结构

锁屏界面的Layout结构可以简单概括为以下结构:

  • mStatusBarWindow--> R.layout.super_status_bar
  • notification_panel--> R.layout.status_bar_expanded
  • keyguardBouncer-->R.layout.keyguard_bouncer

mStatusBarWindow-->notification_panel-->notification_container_parent-->keyguard_header(锁屏状态栏)
                |                    |
                |                    -->keyguard_bottom_area (lock_icon和充电状态等)
                |                    |
                |                    -->keyguard_status_view (锁屏时钟日期)
                |                    |
                |                    -->keyguard_up_slide (箭头提示动画)
                |
                -->keyguardBouncer(安全锁界面)

在这里 keyguardBouncer 加载就不分析了,前面有说过的,见锁屏加载分析


touchEvent事件分发


我们这里分析上滑解锁过程中的touchEvent事件分发

android中的事件分发概念:事件序列。


事件序列


在Android系统中,一个单独的事件基本上是没什么作用的,只有一个事件序列,才有意义。一个事件序列正常情况下,定义为 DOWN、MOVE(0或者多个)、UP/CANCEL。事件序列以DOWN事件开始,中间会有0或者多个MOVE事件,最后以UP事件或者CANCEL事件结束。

DOWN事件作为序列的开始,有一个很重要的职责,就是寻找事件序列的接受者,怎么理解呢?framework 在DOWN事件的传递过程中,需要根据View事件处理方法(onTouchEvent)的返回值来确定事件序列的接受者。如果一个View的onTouchEvent事件,在处理DOWN事件的时候返回true,说明它愿意接受并处理该事件序列。


上滑解锁


当用户移动手指时,产生touch down事件,最外层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件。再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent从子View开始一级级往父View传递,到PanelView这里当手指移动的距离达到一定的阈值会调用onTrackingStarted从而设置mTracking的值为true,onTouchEvent返回true,接收此touch move事件,之后的touch事件直接传到此View。

在用户滑动过程会调用setExpandedHeightInternal,进而调用NotificationPanelView的onHeightUpdated进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理。

当用户抬起手指时,产生touch up事件,PanelView接收到这个事件后会调用endMotionEvent,如果手指从down到up之间移动的距离达到一定阈值会调用onTrackingStopped。

1.硬件发出指令:按下,移动,抬起

2.input接收

3.代码执行相应操作:ACTION_DOWN,ACTION_MOVE,ACTION_UP

PanelView#onInterceptTouchEvent()

// PanelView.java
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return mTouchHandler.onInterceptTouchEvent(event);
    }

PanelViewController

// PanelViewController.java
   public class TouchHandler implements View.OnTouchListener {
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
                    && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
                return false;
            }
            /*
             * If the user drags anywhere inside the panel we intercept it if the movement is
             * upwards. This allows closing the shade from anywhere inside the panel.
             *
             * We only do this if the current content is scrolled to the bottom,
             * i.e canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
             * gesture
             * possible.
             */
            int pointerIndex = event.findPointerIndex(mTrackingPointer);
            if (pointerIndex < 0) {
                pointerIndex = 0;
                mTrackingPointer = event.getPointerId(pointerIndex);
            }
            final float x = event.getX(pointerIndex);
            final float y = event.getY(pointerIndex);
            boolean canCollapsePanel = canCollapsePanelOnTouch();
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mStatusBar.userActivity();
                    mAnimatingOnDown = mHeightAnimator != null;
                    mMinExpandHeight = 0.0f;
                    mDownTime = SystemClock.uptimeMillis();
                    if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
                            || mPeekAnimator != null) {
                        cancelHeightAnimator();
                        cancelPeek();
                        mTouchSlopExceeded = true;
                        return true;
                    }
                    mInitialTouchY = y;
                    mInitialTouchX = x;
                    mTouchStartedInEmptyArea = !isInContentBounds(x, y);
                    mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
                    mJustPeeked = false;
                    mMotionAborted = false;
                    mPanelClosedOnDown = isFullyCollapsed();
                    mCollapsedAndHeadsUpOnDown = false;
                    mHasLayoutedSinceDown = false;
                    mUpdateFlingOnLayout = false;
                    mTouchAboveFalsingThreshold = false;
                    addMovement(event);
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    final int upPointer = event.getPointerId(event.getActionIndex());
                    if (mTrackingPointer == upPointer) {
                        // gesture is ongoing, find a new pointer to track
                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
                        mTrackingPointer = event.getPointerId(newIndex);
                        mInitialTouchX = event.getX(newIndex);
                        mInitialTouchY = event.getY(newIndex);
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
                        mMotionAborted = true;
                        mVelocityTracker.clear();
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float h = y - mInitialTouchY;
                    addMovement(event);
                    if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
                        float hAbs = Math.abs(h);
                        float touchSlop = getTouchSlop(event);
                        if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
                                && hAbs > Math.abs(x - mInitialTouchX)) {
                            cancelHeightAnimator();
                            startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
                            return true;
                        }
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    mVelocityTracker.clear();
                    break;
            }
            return false;
        }
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (mInstantExpanding || (mTouchDisabled
                    && event.getActionMasked() != MotionEvent.ACTION_CANCEL) || (mMotionAborted
                    && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
                return false;
            }
            // If dragging should not expand the notifications shade, then return false.
            if (!mNotificationsDragEnabled) {
                if (mTracking) {
                    // Turn off tracking if it's on or the shade can get stuck in the down position.
                    onTrackingStopped(true /* expand */);
                }
                return false;
            }
            // On expanding, single mouse click expands the panel instead of dragging.
            if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    expand(true);
                }
                return true;
            }
            /*
             * We capture touch events here and update the expand height here in case according to
             * the users fingers. This also handles multi-touch.
             *
             * If the user just clicks shortly, we show a quick peek of the shade.
             *
             * Flinging is also enabled in order to open or close the shade.
             */
            int pointerIndex = event.findPointerIndex(mTrackingPointer);
            if (pointerIndex < 0) {
                pointerIndex = 0;
                mTrackingPointer = event.getPointerId(pointerIndex);
            }
            final float x = event.getX(pointerIndex);
            final float y = event.getY(pointerIndex);
            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
                mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
            }
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                    mJustPeeked = false;
                    mMinExpandHeight = 0.0f;
                    mPanelClosedOnDown = isFullyCollapsed();
                    mHasLayoutedSinceDown = false;
                    mUpdateFlingOnLayout = false;
                    mMotionAborted = false;
                    mPeekTouching = mPanelClosedOnDown;
                    mDownTime = SystemClock.uptimeMillis();
                    mTouchAboveFalsingThreshold = false;
                    mCollapsedAndHeadsUpOnDown =
                            isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
                    addMovement(event);
                    if (!mGestureWaitForTouchSlop || (mHeightAnimator != null
                            && !mHintAnimationRunning) || mPeekAnimator != null) {
                        mTouchSlopExceeded =
                                (mHeightAnimator != null && !mHintAnimationRunning)
                                        || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
                        cancelHeightAnimator();
                        cancelPeek();
                        onTrackingStarted();
                    }
                    if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
                            && !mStatusBar.isBouncerShowing()) {
                        startOpening(event);
                    }
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    final int upPointer = event.getPointerId(event.getActionIndex());
                    if (mTrackingPointer == upPointer) {
                        // gesture is ongoing, find a new pointer to track
                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
                        final float newY = event.getY(newIndex);
                        final float newX = event.getX(newIndex);
                        mTrackingPointer = event.getPointerId(newIndex);
                        startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
                        mMotionAborted = true;
                        endMotionEvent(event, x, y, true /* forceCancel */);
                        return false;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    addMovement(event);
                    float h = y - mInitialTouchY;
                    // If the panel was collapsed when touching, we only need to check for the
                    // y-component of the gesture, as we have no conflicting horizontal gesture.
                    if (Math.abs(h) > getTouchSlop(event)
                            && (Math.abs(h) > Math.abs(x - mInitialTouchX)
                            || mIgnoreXTouchSlop)) {
                        mTouchSlopExceeded = true;
                        if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
                            if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
                                startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                                h = 0;
                            }
                            cancelHeightAnimator();
                            // 向上滑动时,手指移动的距离达到一定的阈值会调用onTrackingStarted,
                            // 设置mTracking值为true,从而接收touch事件
                            onTrackingStarted();
                        }
                    }
                    float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
                    if (newHeight > mPeekHeight) {
                        if (mPeekAnimator != null) {
                            mPeekAnimator.cancel();
                        }
                        mJustPeeked = false;
                    } else if (mPeekAnimator == null && mJustPeeked) {
                        // The initial peek has finished, but we haven't dragged as far yet, lets
                        // speed it up by starting at the peek height.
                        mInitialOffsetOnTouch = mExpandedHeight;
                        mInitialTouchY = y;
                        mMinExpandHeight = mExpandedHeight;
                        mJustPeeked = false;
                    }
                    newHeight = Math.max(newHeight, mMinExpandHeight);
                    if (-h >= getFalsingThreshold()) {
                        mTouchAboveFalsingThreshold = true;
                        mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
                    }
                    if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking)
                            && !isTrackingBlocked()) {
                        // 用户滑动过程会调用setExpandedHeightInternal
                        setExpandedHeightInternal(newHeight);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    addMovement(event);
                    endMotionEvent(event, x, y, false /* forceCancel */);
                    break;
            }
            return !mGestureWaitForTouchSlop || mTracking;
        }
    }

移动过程中:主要在调用了两个方法。

// PanelViewController.java
protected void onTrackingStarted() {
        endClosing();
        mTracking = true;
        mBar.onTrackingStarted();
        notifyExpandingStarted();
        notifyBarPanelExpansionChanged();
    }

// PanelViewController.java
public void setExpandedHeightInternal(float h) {
        if (isNaN(h)) {
            Log.wtf(TAG, "ExpandedHeight set to NaN");
        }
        if (mExpandLatencyTracking && h != 0f) {
            DejankUtils.postAfterTraversal(
                    () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
            mExpandLatencyTracking = false;
        }
        float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
        if (mHeightAnimator == null) {
            float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
            if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
                setOverExpansion(overExpansionPixels, true /* isPixels */);
            }
            mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
        } else {
            mExpandedHeight = h;
            if (mOverExpandedBeforeFling) {
                setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
            }
        }
        // If we are closing the panel and we are almost there due to a slow decelerating
        // interpolator, abort the animation.
        if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
            mExpandedHeight = 0f;
            if (mHeightAnimator != null) {
                mHeightAnimator.end();
            }
        }
        mExpandedFraction = Math.min(1f,
                fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
        // 进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理 
        onHeightUpdated(mExpandedHeight);
        notifyBarPanelExpansionChanged();
    }

下面主要从:onHeightUpdated、notifyBarPanelExpansionChanged 两方法作为入口。

先看 NotificationPanelViewController#onHeightUpdated()

// NotificationPanelViewController.java
    @Override
    protected void onHeightUpdated(float expandedHeight) {
        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
            // 更新时钟位置将设置顶部填充,这可能会触发新的面板高度并重新定位时钟。
            // 这是一个循环依赖项,应该避免,否则会出现堆栈溢出。
            if (mStackScrollerMeasuringPass > 2) {
                if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
            } else {
                //锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
                positionClockAndNotifications();
            }
        }
        if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
                && !mQsExpansionFromOverscroll) {
            float t;
            if (mKeyguardShowing) {
                // 在Keyguard上,将QS扩展线性插值到面板扩展
                t = expandedHeight / (getMaxPanelHeight());
            } else {
                // In Shade, interpolate linearly such that QS is closed whenever panel height is
                // minimum QS expansion + minStackHeight
                float
                        panelHeightQsCollapsed =
                        mNotificationStackScroller.getIntrinsicPadding()
                                + mNotificationStackScroller.getLayoutMinHeight();
                float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
                t =
                        (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
                                - panelHeightQsCollapsed);
            }
            float
                    targetHeight =
                    mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
            setQsExpansion(targetHeight);
        }
        updateExpandedHeight(expandedHeight);
        updateHeader();
        // 更新通知半透明
        updateNotificationTranslucency();
        updatePanelExpanded();
        updateGestureExclusionRect();
        if (DEBUG) {
            mView.invalidate();
        }
    }

到这里了就一起看个滑动解锁的堆栈:

09-19 05:48:41.853  1477  1477 D yexiao  : java.lang.Throwable:
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.keyguard.KeyguardSecurityContainer.showNextSecurityScreenOrFinish(KeyguardSecurityContainer.java:710)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:214)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:196)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.systemui.statusbar.phone.KeyguardBouncer.show(KeyguardBouncer.java:167)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.showBouncer(StatusBarKeyguardViewManager.java:434)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.systemui.statusbar.phone.StatusBar.showBouncerIfKeyguard(StatusBar.java:3959)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.systemui.statusbar.phone.StatusBar.makeExpandedInvisible(StatusBar.java:2506)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.systemui.statusbar.phone.PhoneStatusBarView$1.run(PhoneStatusBarView.java:65)
09-19 05:48:41.853  1477  1477 D yexiao  :      at android.os.Handler.handleCallback(Handler.java:938)
09-19 05:48:41.853  1477  1477 D yexiao  :      at android.os.Handler.dispatchMessage(Handler.java:99)
09-19 05:48:41.853  1477  1477 D yexiao  :      at android.os.Looper.loop(Looper.java:223)
09-19 05:48:41.853  1477  1477 D yexiao  :      at android.app.ActivityThread.main(ActivityThread.java:7945)
09-19 05:48:41.853  1477  1477 D yexiao  :      at java.lang.reflect.Method.invoke(Native Method)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603)
09-19 05:48:41.853  1477  1477 D yexiao  :      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

再看一个滑动后到密码安全锁(即密码解锁)的堆栈:

09-17 11:20:35.891  1473  1473 D yexiao: : java.lang.Throwable
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.keyguard.KeyguardSecurityContainer.showNextSecurityScreenOrFinish(KeyguardSecurityContainer.java:710)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:214)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:196)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.KeyguardBouncer.show(KeyguardBouncer.java:167)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.onPanelExpansionChanged(StatusBarKeyguardViewManager.java:297)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.PanelViewController.notifyBarPanelExpansionChanged(PanelViewController.java:1011)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.PanelViewController.setExpandedHeightInternal(PanelViewController.java:727)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.PanelViewController$TouchHandler.onTouch(PanelViewController.java:1338)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.NotificationPanelViewController$18.onTouch(NotificationPanelViewController.java:3229)
09-17 11:20:35.891  1473  1473 D yexiao: :      at android.view.View.dispatchTouchEvent(View.java:14385)
09-17 11:20:35.891  1473  1473 D yexiao: :      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
09-17 11:20:35.891  1473  1473 D yexiao: :      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2792)
09-17 11:20:35.891  1473  1473 D yexiao: :      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3126)
09-17 11:20:35.891  1473  1473 D yexiao: :      at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2806)
09-17 11:20:35.891  1473  1473 D yexiao: :      at com.android.systemui.statusbar.phone.NotificationShadeWindowView.dispatchTouchEvent(NotificationShadeWindowView.java:173)
09-17 11:20:35.891  1473  1473 D yexiao: :      at android.view.View.dispatchPointerEvent(View.java:14656)
// 省略部分Log....


相关文章
|
28天前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
72 6
|
27天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
29天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
26 3
|
30天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
24 2
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
39 3
|
29天前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
18 0
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
50 4
|
2月前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
51 0
|
3月前
|
Android开发
我的Android进阶修炼:安卓启动流程之init(1)
本文深入分析了Android系统中的init进程,包括其源码结构、主要功能以及启动流程的详细注解,旨在帮助读者理解init作为用户空间的1号进程在Android启动过程中的关键作用。
65 1
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
152 0
下一篇
无影云桌面