欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android 8.0流程分析

程序员文章站 2022-06-09 23:16:46
...

1.keygaurd 锁屏重要类简介

PhoneWindowManager.java 

此类主要涉及各种按键响应的事件


KeyguardServiceDelegate.jav和KeyguardServiceWrapper.java

分别对KeyguardService进行了代理和包装,代理类里面有一个Scrim视图在keyguard崩溃时显示。包装类就是对keyguardService的简单包装,最终把调度都会传给keyguardService


keyguardService.java

其实它就是keyguard的入口,锁屏所有的往生都因它而起,这个类很简单,实例化了一个IKeyguardService.Stub供其他类bindservice时调用,需要特别注意的是整个keyguard的核心实力派 KeyguardViewMediator在这里诞生了。


 KeyguardViewMediator.java在keyguard中起着主要调度的作用,主要负责

1)查询锁屏状态,当前是锁屏还是解锁状态;在锁屏状态下,会限制输入事件。

2)PhoneWindowManager.java通过mKeyguardDelegate对象(KeyguardServiceDelegate.java)来使能KeyguardViewMediator.java,调用其中的方法;

3)响应SIM卡状态变化并对锁屏界面做相应的调整onSimStateChanged();

4)调用KeyguardBouncer.java的show()方法;使用ensureView()方法去加载实例化布局;调用KeyguardHostView.java的showPrimarySecurityScreen()方法去显示安全锁屏界面;

 

KeyguardSecurityView.java

是一个接口类,接口定义了各锁屏view最基本的方法,其内部方法都是抽象的只有声明没有实现,其方法实现都是在继承于这个接口的类中。


KeyguardHostView.java   

这里完成keyguardView布局,实例化。分析一个自定义的viewgroup,重点需要分析的是它的构造方法和onFinishInflate()方法


KeyguardUpdateMonitor.java 

监听系统状态值的改变如时间、SIM卡状态、电池电量等,状态值的改变会回调监听了该状态信息的对象实例。如果只是关注功能的话只需要看handle里面的每个消息调用的方法即可

KeyguardUpdateMonitorCallback.java

由mCallbacks来保存所有的具体观察者角色,可以通过registerCallback和removeCallback方法来实现具体观察者角色添加和删除。


2.keyugard锁屏流程简介

Keyguard锁屏的加载在灭屏的时候绘制的,这样可以确保的屏幕亮起来的时候,用户能第一时间看到锁屏界面。

锁屏界面的加载通常在有三种方式触发:android系统开机和screenOff(灭屏)后再screenOn(亮屏),超时灭屏。

2.1 power键灭屏流程

亮屏过程中涉及到两条关键的线程,分别是PowerManager Notifier Thread DisplayManagerThread

1.PowerManager Notifier Threadpower按键事件传到PowerManagerService.java中会分为两条线路进行事件处理,一条是我们下面流程图上标注的,这条Thread(Notifier.java)主要负责亮屏或者灭屏广播的发送,以及锁屏界面的加载。

灭屏广播的发送流程如下:

Notifier.java    handleLateInteractiveChange()->updatePendingBroadcastLocked()->NotifierHandler()->sendNextBroadcast()->

if(powerState == INTERACTIVE_STATE_AWAKE) {

           sendWakeUpBroadcast();//发送亮屏广播

        } else {

           sendGoToSleepBroadcast();//发送灭屏广播

        }

2.DisplayManager Thread:这条Thread会传送到DisplayManagerService.java中去,会负责进行屏幕状态的设置以及屏幕的熄灭与点亮(真正的屏幕操作者),此外跟屏幕相关的一些可见性操作都是这条Thread(DisplayPowerController.java)进行处理,例如屏幕亮度的变化、屏幕亮度的设置、屏幕的显示时机等等,此部分代码涉及到powerManagerService.java,后续会研究。


按下power键流程如下:(下图取之其他博客,流程有细微不同,大体一致)

android 8.0流程分析

KeyguardService.javaBinder的服务器端,客户端是KeyguardServiceDelegate.javaKeyguardServiceDelegate.java这个文件在调用过程中只起到过渡的作用,在这里并没有深入研究。KeyguardService.java会将所有的调用传递到KeyguardViewMediator.java中。


锁屏的加载绘制过程

KeyguardViewMediator.java的onFinishedGoingToSleep()是加载锁屏的入口,接下来对这个过程进行分析

android 8.0流程分析

android 8.0流程分析

2.2开机启动流程

开机启动到PhoneWindowManagersystemReady方法

先从开机启动到PhoneWindowManager类的systemReady方法调用开始介绍。开机启动流程其实也很复杂,但是本文重点在于Keyguard锁屏的展示,所以这里只是大体列出如何从开机启动调用到PhoneWindowManagersystemReady()方法。具体流程如下:
init
进程->zygote进程(java世界)->system server进程。
而在system server进程中,它的SystemServer.javamain函数会调用startOtherServices()方法,相关源码如下:

private void startOtherServices() {

    WindowManagerService wm = null;

    wm = WindowManagerService.main(context, inputManager,

            mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,

            !mFirstBoot, mOnlyCore);

    try {

        wm.systemReady();

    } catch (Throwable e) {

        reportWtf("makeing Window Manager Service ready", e);

    }

}
既然wm是WindowManagerServer类的实例,那就需要继续看一下这个类关于systemReady()方法的实现:
public class WindowManagerService extends IWindowManager.Stub

        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();

    public void systemReady() {

        mPolicy.systemReady();

    }

}

其中,mPolicy实例的生成依赖于Java的反射机制,具体流程如下:

public final class PolicyManager {

    private static final String POLICY_IMPL_CLASS_NAME = 

        "com.android.internal.policy.impl.Policy";

    private static final IPolicy sPolicy;

    static {

        try {

            // 获取了Policy的类类型

            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);

            // 通过Policy的类类型获取Policy的对象实例

            sPolicy = (IPolicy)policyClass.newInstance();

        } catch (Exception e) {

        }

    }

    public static Window makeNewWindowManager(Context context) {

        return sPolicy.makeNewWindowManager(context);

    }

}

 

// Policy中继续调用反射机制进行预加载

public class Policy implements IPolicy {

    public Window makeNewWindowManager(Context context) {

        return new PhoneWindowManager();

    }

}

通过上述代码的跟踪,终于来到了我们时序图的第一个类PhonewWindowManager的systemReady()方法了。


锁屏加载流程

既然上述分析到了PhoneWindowManager,我们也就是按照时序图,根据时序图上的每个类,进行相关源码分析(重点难点的源码我会中文注释)。

PhoneWindowManager

路径frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

public class PhoneWindowManager implements WindowManagerPolicy {

    public void systemReady() {

        // 调用Keygurad代理类的onSystemReady方法

        mKeyguardDelegate = new KeyguardServiceDelegate(mContext);

        mKeyguardDelegate.onSystemReady();

        // ... 省略不相关源码 

    }

}

KeyguardServiceDelegate

路径:frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java

public class KeyguardServiceDelegate {

    protected KeyguardServiceWrapper mKeyguardService;

    public void onSystemReady() {

        if (mKeyguardService != null) {

            mKeyguardService.onSystemReady();

        } else {

            mKeyguardState.systemIsReady = true;

        }   

    } 

}

KeyguardServiceWrapper

路径:frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java

public class KeyguardServiceWrapper implements IKeyguardService {

    private IkeyguardService mService;

    public KeyguardServiceWrapper(Context context, IKeyguardService service) {

        // 构造函数中对mService进行了初始化

        mService = service;

    }

    public void onSystemReady() {

        try {

            mService.onSystemReady();

        } catch (RemoteException e) {

            Slog.w(TAG , "Remote Exception", e); 

        }   

    }

}

由于mService是IKeyguardService接口实现类的实例,并且mService又是在KeyguardServiceWrapper的构造函数中传递进来初始化的。所以,我们又需要回到KeyguardServiceDelegate类,去看一下KeyguardServiceWrapper初始化的过程,相关代码如下:

public class KeyguardServiceDelegate {

    public static final String KEYGUARD_PACKAGE = "com.android.systemui";

    public static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService";

 

    /**

     * 使用bindService的方式来绑定服务。利用bindService的方式:

     * 调用者与服务绑定在一起,调用者退出,服务即终止。

     * ps => bind方式绑定服务,服务的执行顺序为:

     * onCreate()->onBind()->onUnbind()->onDestroy()

     */

    public void bindService(Context context) {

        Intent intent = new Intent();

        intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);

        if (!context.bindServiceAsUser(intent, mKeyguardConnection,

                Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {

            Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);

            mKeyguardState.showing = false;

            mKeyguardState.showingAndNotOccluded = false;

            mKeyguardState.secure = false;

            mKeyguardState.deviceHasKeyguard = false;

            hideScrim();

        } else {

            if (DEBUG) Log.v(TAG, "*** Keyguard started");

        }   

    }   

 

    private final ServiceConnection mKeyguardConnection = new ServiceConnection() {

        @Override

        public void onServiceConnected(ComponentName name, IBinder service) {

            if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");

            // 当和服务绑定后,这IKeyguardService.Stub.asInterface(service)获取的就是KeyguardService的类实例

            mKeyguardService = new KeyguardServiceWrapper(mContext,

                    IKeyguardService.Stub.asInterface(service));

            if (mKeyguardState.systemIsReady) {

                // If the system is ready, it means keyguard crashed and restarted.

                mKeyguardService.onSystemReady();

                // This is used to hide the scrim once keyguard displays.

                mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(

                        mShowListenerWhenConnect));

                mShowListenerWhenConnect = null;

            }

            if (mKeyguardState.bootCompleted) {

                mKeyguardService.onBootCompleted();

            }

        }

 

        @Override

        public void onServiceDisconnected(ComponentName name) {

            if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");

            mKeyguardService = null;

        }

    };

}
接下来,就需要去看一下KeyguardService类的onSystemReady方法了。
KeyguardService

路径:/frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java

public class KeyguardService extends Service {

    private KeyguardViewMediator mKeyguardViewMediator;

    @Override // Binder interface

    public void onSystemReady() {

        // 检查调用进程是否具体SYSTEM权限

        checkPermission();

        // 真正的锁屏入口

        mKeyguardViewMediator.onSystemReady();

    }

}

Wow,终于到了我们的主角KeyguardViewMediator登场了。


KeyguardViewMediator
路径:
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
虽然KeyguardViewMediator是锁屏的入口,但是从这里到锁屏的真正展现还有很长一段路。接下来,为了方便,我们都是基于当前类的函数进行分析。

public class KeyguardViewMediator extends SystemUI {

    // 开机显示锁屏入口函数   

    public void onSystemReady() {

        synchronized (this) {

            if (DEBUG) Log.d(TAG, "onSystemReady");

            mSystemReady = true;            

            // 判断是否使用生物识别解锁(类似:人脸识别、声音识别等)

            if (mLockPatternUtils.usingBiometricWeak()

                    && mLockPatternUtils.isBiometricWeakInstalled()) {

                if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");

                mUpdateMonitor.setAlternateUnlockEnabled(false);

            } else {

                mUpdateMonitor.setAlternateUnlockEnabled(true);

            }

            // 进行锁屏预处理判断等操作

            doKeyguardLocked(null);

        }

    }

}

onKeyguardLocked()
其中,doKeyguardLocked是来做启动锁屏界面的预处理方法,我们来看一下这个函数的具体实现:

public class KeyguardViewMediator extends SystemUI {

    private void doKeyguardLocked(Bundle options) {

        if (!mExternallyEnabled) {

            // 其他应用禁止锁屏呈现,例如接电话等操作.

            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");

            return;

        }

 

        // 判断锁屏是否正在展示

        if (mStatusBarKeyguardViewManager.isShowing()) {

            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");

            resetStateLocked();

            return;

        }

 

        // 判断是否无sim卡也可使用手机

        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);

        // 获取sim卡状态

        final boolean absent = SubscriptionManager.isValidSubscriptionId(

            mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));

        final boolean disabled = SubscriptionManager.isValidSubscriptionId(

            mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));

        final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()

                || ((absent || disabled) && requireSim);

 

        if (!lockedOrMissing && shouldWaitForProvisioning()) {

            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"

                    + " and the sim is not locked or missing");

            return;

        }

 

        if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {

            // Settings中没有启用锁屏

            return;

        }

 

        if (mLockPatternUtils.checkVoldPassword()) {

            setShowingLocked(false);

            hideLocked();

            return;

        }

 

        // 经过上述判断后,去展示锁屏

        showLocked(options);

    }

}

注意showLocked(options)方法调用,这个是启动锁屏的关键方法。这里的options传递的值为null。

showLocked()

public class KeyguardViewMediator extends SystemUI {

   private void showLocked(Bundle options) {

        if (DEBUG) Log.d(TAG, "showLocked");

        // 获取PARTIAL_WAKE_LOCK,不受电源键影响,不让CPU进入休眠状态 

        mShowKeyguardWakeLock.acquire();

        // 发送msg.what为SHOW类型的message

        Message msg = mHandler.obtainMessage(SHOW, options);

        mHandler.sendMessage(msg);

    }

}

注意:
mShowKeyguardWakeLock.acquire(); ⇒ 获取之后是无法让CPU休眠,不要忘记释放,不让会增加系统功耗。

跟mShowKeyguardWakeLock相关的代码如下:

  1. public class KeyguardViewMediator extends SystemUI {
  2.     private PowerManager.WakeLock mShowKeyguardWakeLock;
  3.     private void setupLocked() {
  4.         // 获取了PARTIAL_WAKE_LOCK锁,即不受电源键控制,即使按下电源键也不能使系统进入休眠状态
  5.         mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
  6.         mShowKeyguardWakeLock.setReferenceCounted(false);
  7.     }
  8.     private void showLocked(Bundle options) {
  9.         // 获取PARTIAL_WAKE_LOCK
  10.         mShowKeyguardWakeLock.acquire();
  11.     }
  12.     private void handleShow(Bundle options) {
  13.         synchronized (KeyguardViewMediator.this) {
  14.             // 释放PARTIAL_WAKE_LOCK
  15.             mShowKeyguardWakeLock.release();
  16.         }
  17.     }
  18. }


handleShow()
既然是Handler Message机制,那我们就要去看一下mHandler类实例是如何处理SHOW类型的消息了。mHandle处理SHOW类型消息的方法如下:

public class KeyguardViewMediator extends SystemUI {

    private Handler mHandler = new Handler(Looper.myLooper(), null /*callback*/, true /*async*/) {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case SHOW:

                    handleShow((Bundle) msg.obj);
            }
        }
    }

    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            if (!mSystemReady) {
                // 系统未Ready,则不呈现锁屏
                return;
            } else {
                if (DEBUG) Log.d(TAG, "handleShow");
            }
            setShowingLocked(true);
            // 展示锁屏界面
            mStatusBarKeyguardViewManager.show(options);
            mHiding = false;
            resetKeyguardDonePendingLocked();
            mHideAnimationRun = false;
            updateActivityLockScreenState();
            adjustStatusBarLocked();
            userActivity();
            // Do this at the end to not slow down display of the keyguard.
            playSounds(true);
            mShowKeyguardWakeLock.release();
        }
        mKeyguardDisplayManager.show();
    }
}

锁屏解锁之向上滑动显示解锁界面分析

onInterceptTouchEvent和onTouchEvent调用时序

解锁动作是由手势触发的,在研究解锁流程之前需要明白onInterceptTouchEvent与onTouchEvent的区别与使用。


onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。
onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。
关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。


由于onInterceptTouchEvent()的机制比较复杂,总结一下,基本的规则是:
1.down事件首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

下面用一个简单的实验说明上述复杂的规则。视图自底向上共3层,其中LayoutView1和LayoutView2就是LinearLayout,MyTextView就是TextView:

android 8.0流程分析

通常外围的layoutview1,layoutview2,只是布局的容器不需要响应触屏的点击事件,仅仅Mytextview需要相应点击。但这只是一般情况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的情况是,动态更换响应对象。

那么首先看一下默认的触屏事件的在两个函数之间的传递流程。如下图:


android 8.0流程分析

如果仅仅想让MyTextView来响应触屏事件,让MyTextView的OnTouchEvent返回true,那么事件流就变成如下图,可以看到layoutview1,layoutview2已经不能进入OnTouchEvent:

android 8.0流程分析

另外一种情况,就是外围容器想独自处理触屏事件,那么就应该在相应的onInterceptTouchEvent函数中返回true,表示要截获触屏事件,比如layoutview1作截获处理,处理流变成如下图:

android 8.0流程分析

以此类推,我们可以得到各种具体的情况,整个layout的view类层次中都有机会截获,而且能看出来外围的容器view具有优先截获权。
当我们去做一些相对来讲具有更复杂的触屏交互效果的应用时候,经常需要动态变更touch event的处理对象,比如launcher待机桌面和主菜单(见下图),从滑动屏幕开始到停止滑动过程当中,只有外围的容器view才可以处理touch event,否则就会误点击上面的应用图标或者widget.反之在静止不动的状态下则需要能够响应图标(子view)的touch事件

锁屏的解锁操作是在锁屏界面向上滑动实现的,通过向上滑动调出解锁界面(如图案、PIN、密码解锁界面),在解锁界面输入正确的密码之后解锁显示launcher。向上滑动如何调出解锁界面,需要分析PanelView的onTouchEvent事件,用户向上滑动的整个touch事件分析如下: 

android 8.0流程分析

1、用户手指touch屏幕,产生touch down事件,最底层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件,touch down事件在此没有被拦截,再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent,从子View开始往父View传递,一级级往父View传递,也都没有消耗touch down事件;
2、用户移动手指,产生touch move事件,最底层view StatusBarWindowView会执行onInterceptTouchEvent,看是否需要拦截touch事件,touch move事件在此也没有被拦截,再一级级往子View传递,都没有被拦截,之后执行OnTouchEvent,从子View开始往父View传递,一级级往父View传递,到PanelView这里当手指移动的距离达到一定的阈值会调用onTrackingStarted从而设置mTracking的值为true,接收此touch move事件,之后的touch事件直接传到此View。在用户滑动过程会调用setExpandedHeightInternal,进而调用NotificationPanelView的onHeightUpdated进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理;
3、用户抬起手指,产生touch up事件,PanelView接收到这个事件后会调用endMotionEvent,如果手指从down到up之间移动的距离达到一定阈值会调用onTrackingStopped,从而调出解锁界面;
代码分析如下:

PanelView.java
 @Override

    public boolean onTouchEvent(MotionEvent event) {

        ...

        final float x = event.getX(pointerIndex);

        final float y = event.getY(pointerIndex);

 

        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {

            mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();

            mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);

        }

 

        switch (event.getActionMasked()) {

            ...

            case MotionEvent.ACTION_MOVE:

                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) > mTouchSlop

                        && (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();

                        removeCallbacks(mPeekRunnable);

                        mPeekPending = false;

                        onTrackingStarted();//向上滑动时,手指移动的距离达到一定的阈值会调用onTrackingStarted,设置mTracking值为true,从而接收touch事件

                    }

                }

                final float newHeight = Math.max(0, h + mInitialOffsetOnTouch);

                if (newHeight > mPeekHeight) {

                    if (mPeekAnimator != null) {

                        mPeekAnimator.cancel();

                    }

                    mJustPeeked = false;

                }

                if (-h >= getFalsingThreshold()) {

                    mTouchAboveFalsingThreshold = true;

                    mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);

                }

                if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) {

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

                }

 

                trackMovement(event);

                break;

 

            case MotionEvent.ACTION_UP:

            case MotionEvent.ACTION_CANCEL:

                trackMovement(event);

                endMotionEvent(event, x, y, false /* forceCancel */);

                break;

        }

        return !mGestureWaitForTouchSlop || mTracking;

    }

 

    protected void onTrackingStarted() {

        endClosing();

        mTracking = true;

        mCollapseAfterPeek = false;

        mBar.onTrackingStarted();

        notifyExpandingStarted();

        notifyBarPanelExpansionChanged();

    }

 

NotificationPanelView.java

    @Override

    protected void onHeightUpdated(float expandedHeight,  boolean isMovingInKeyguard) {

        mExpandedHeightTpv = expandedHeight;

        LogUtils.d(TAG,"--------onHeightUpdated");

        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {

            positionClockAndNotifications(isMovingInKeyguard);//锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理

        }

        ...

    }

endMotionEvent调出解锁界面流程如下:
->PanelView.endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel)
->NotificationPanelView.onTrackingStopped(boolean expand)
->PhoneStatusBar.onTrackingStopped(boolean expand)
->PhoneStatusBar.showBouncer()
->StatusBarKeyguardViewManager.dismiss()
->StatusBarKeyguardViewManager.dismiss(boolean authenticated)
->StatusBarKeyguardViewManager.showBouncer(boolean authenticated)
->KeyguardBouncer.show(boolean resetSecuritySelection, boolean authenticated)
->KeyguardBouncer.mShowRunnable
调用KeyguardBouncer.show(boolean resetSecuritySelection, boolean authenticated)方法并没有重新加载解锁界面,解锁界面是在灭屏时调用StatusBarKeyguardViewManager.reset后重新inflate进来的,在此处只是通过KeyguardBouncer.mShowRunnable将这个解锁界面显示出来,灭屏时解锁界面的调用栈如下:
“aaa@qq.com” prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at com.android.keyguard.KeyguardSecurityContainer.showSecurityScreen(KeyguardSecurityContainer.java:584)
at com.android.keyguard.KeyguardSecurityContainer.showPrimarySecurityScreen(KeyguardSecurityContainer.java:416)
at com.android.keyguard.KeyguardHostView.onFinishInflate(KeyguardHostView.java:168)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:867)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:994)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:854)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
- locked <0xd005> (a java.lang.Object[])
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
at com.android.systemui.statusbar.phone.KeyguardBouncer.inflateView(KeyguardBouncer.java:317)
at com.android.systemui.statusbar.phone.KeyguardBouncer.ensureView(KeyguardBouncer.java:307)
at com.android.systemui.statusbar.phone.KeyguardBouncer.needsFullscreenBouncer(KeyguardBouncer.java:347)
at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.showBouncerOrKeyguard(StatusBarKeyguardViewManager.java:130)
at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.reset(StatusBarKeyguardViewManager.java:192)

至此把用户设定的解锁界面(图案、密码、PIN码)显示出来


Android6.0亮屏流程之Keyguard Window绘制

亮灭屏问题一直是Android模块最常见的问题之一。

影响亮屏快慢的因素大致有三种:1.设置背光流程出问题了,导致屏幕黑屏,2.window绘制时间过长,导致屏幕block时间过长;3.底层surfacecontroller准备时间过长。

而根据遇到的亮屏慢的问题,基本上都是由于window绘制时间过长,导致屏幕亮屏慢

最近处理的几个亮屏慢的问题,其中关键log信息基本都是:

10-28 09:02:59.002  1393  1462 I DisplayPowerController: Blocking screen on until initial contents have been drawn.

10-28 09:03:05.020  1393  1462 I DisplayPowerController: Unblocked screen on after 6018 ms

由于在android亮屏流程中大致描述的亮屏所走的流程点,但是仅仅只能作为一个粗略的点做参考,但是看到上面的两条信息,我们可以去追溯一下代码流程:

在每一次屏幕电源状态发生改变的都会调用的到DisplayPowerController中的updatePowerState方法,在该方法中如果屏幕状态发生改变的话,会去调用到animateScreenStateChange,在setScreenState方法里面去设置屏幕状态。那么就到了该问题的主要流程了。

在DisplayPowerController.java中的setScreenState()方法中,有代码:

  if (mPowerState.getColorFadeLevel() == 0.0f) {  
        blockScreenOn();  
    } else {  
        unblockScreenOn();  
    }  
    mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);  
}  
  
// Return true if the screen isn't blocked.  
首先会调用到blockScreenOn方法,先看看该方法以及后面要调用到的unblockScreenOn的方法
private void blockScreenOn() {  
    if (mPendingScreenOnUnblocker == null) {  
        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);  
        mPendingScreenOnUnblocker = new ScreenOnUnblocker();  
        mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();  
        Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");  
    }  
}  
   
private void unblockScreenOn() {  
    if (mPendingScreenOnUnblocke != null) {  
        mPendingScreenOnUnblocker = null;  
        long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;  
        Slog.i(TAG, "Unblocked screen on after " + delay + " ms");  
        Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);  
    }  

在blockScreenOn函数中其实就是创建了一个mPendingScreenOnUnblocker 对象,当该对象为空时,mPendingScreenOnUnblocker == null;返回值才为true。animateScreenStateChange才能继续往下执行下去。

亮屏过程中绘制window过程是通过mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker); 调用到PhoneWindowManager中的screenTurningOn()

private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {  
    @Override  
    public void onScreenOn() {  
        Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);  
        msg.setAsynchronous(true);  
        mHandler.sendMessage(msg);  
    }  
}  
可以看到mPendingScreenOnUnblocker是继承了WindowManagerPolicy.ScreenOnListener这个接口,而该接口同样是作为一个callback到PhoneWindowManager那边去绘制window,当all window for drawn 完成之后,会回调到mPendingScreenOnUnblocker的onScreenOn,会去unblock屏幕


这里用一个图来详细描述


可以看到 整个过程调用的点较为混乱,这里只能简单描述一下其调用过程当PowerManagerService发出的wakeup请求到DisplayPowerController这边,在DisplayPowerConrtoller中setScreenState方法中会调用到PhoneWindowManager模块的screenTurningOn方法去通过window绘制屏幕。

由于在灭屏之后亮屏首先现实的界面应该是锁屏界面,所以亮屏首先应该去绘制keyguard,并且keyguard经常会出现超时,当出现keyguard超时时会打印:

WindowManager:Keyguard drawn timeout. Setting mKeyguardDrawComplete

当出现上述打印,便可以确定时keyguard模块绘制锁屏超时,需要锁屏的人去确认超时原因。锁屏超时时间最长也只有2秒时间。因为

android 8.0流程分析

可以看到 整个过程调用的点较为混乱,这里只能简单描述一下其调用过程当PowerManagerService发出的wakeup请求到DisplayPowerController这边,在DisplayPowerConrtoller中setScreenState方法中会调用到PhoneWindowManager模块的screenTurningOn方法去通过window绘制屏幕。

由于在灭屏之后亮屏首先现实的界面应该是锁屏界面,所以亮屏首先应该去绘制keyguard,并且keyguard经常会出现超时,当出现keyguard超时时会打印:

WindowManager:Keyguard drawn timeout. Setting mKeyguardDrawComplete

当出现上述打印,便可以确定时keyguard模块绘制锁屏超时,需要锁屏的人去确认超时原因。锁屏超时时间最长也只有2秒时间。因为

if (mKeyguardDelegate != null) {  
    mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);  
    mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);  
    mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);  
} 

当mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);正常执行完成的情况下会调用到finishKeyguardDrawn,当未能正常执行,超过1秒还未完成便会发送消息MSG_KEYGUARD_DRAWN_TIMEOUT强制执行finishKeyguardDrawn,    

 在finishKeyguardDrawn完成后会检查所有的亮屏后所依赖的window是否都绘制完成,在mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,WAITING_FOR_DRAWN_TIMEOUT);//WAITING_FOR_DRAWN_TIMEOUT = 1000 中会去检查window绘制,如上,当超过1秒后,会自动release等待的window,然后正常亮屏。