android 8.0流程分析
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 Thread:power按键事件传到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键流程如下:(下图取之其他博客,流程有细微不同,大体一致)
KeyguardService.java是Binder的服务器端,客户端是KeyguardServiceDelegate.java,KeyguardServiceDelegate.java这个文件在调用过程中只起到过渡的作用,在这里并没有深入研究。KeyguardService.java会将所有的调用传递到KeyguardViewMediator.java中。
锁屏的加载绘制过程
KeyguardViewMediator.java的onFinishedGoingToSleep()是加载锁屏的入口,接下来对这个过程进行分析
2.2开机启动流程
开机启动到PhoneWindowManager的systemReady方法
先从开机启动到PhoneWindowManager类的systemReady方法调用开始介绍。开机启动流程其实也很复杂,但是本文重点在于Keyguard锁屏的展示,所以这里只是大体列出如何从开机启动调用到PhoneWindowManager的systemReady()方法。具体流程如下:
init进程->zygote进程(java世界)->system server进程。
而在system server进程中,它的SystemServer.java中main函数会调用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相关的代码如下:
public class KeyguardViewMediator extends SystemUI { private PowerManager.WakeLock mShowKeyguardWakeLock; private void setupLocked() { // 获取了PARTIAL_WAKE_LOCK锁,即不受电源键控制,即使按下电源键也不能使系统进入休眠状态 mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); mShowKeyguardWakeLock.setReferenceCounted(false); } private void showLocked(Bundle options) { // 获取PARTIAL_WAKE_LOCK mShowKeyguardWakeLock.acquire(); } private void handleShow(Bundle options) { synchronized (KeyguardViewMediator.this) { // 释放PARTIAL_WAKE_LOCK mShowKeyguardWakeLock.release(); } } }
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:
通常外围的layoutview1,layoutview2,只是布局的容器不需要响应触屏的点击事件,仅仅Mytextview需要相应点击。但这只是一般情况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的情况是,动态更换响应对象。
那么首先看一下默认的触屏事件的在两个函数之间的传递流程。如下图:
如果仅仅想让MyTextView来响应触屏事件,让MyTextView的OnTouchEvent返回true,那么事件流就变成如下图,可以看到layoutview1,layoutview2已经不能进入OnTouchEvent:
另外一种情况,就是外围容器想独自处理触屏事件,那么就应该在相应的onInterceptTouchEvent函数中返回true,表示要截获触屏事件,比如layoutview1作截获处理,处理流变成如下图:
锁屏的解锁操作是在锁屏界面向上滑动实现的,通过向上滑动调出解锁界面(如图案、PIN、密码解锁界面),在解锁界面输入正确的密码之后解锁显示launcher。向上滑动如何调出解锁界面,需要分析PanelView的onTouchEvent事件,用户向上滑动的整个touch事件分析如下:
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)
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秒时间。因为
可以看到 整个过程调用的点较为混乱,这里只能简单描述一下其调用过程当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,然后正常亮屏。
上一篇: 现在后端都在用什么数据库存储数据?
下一篇: SQLite3 命令行操作指南
推荐阅读