频道栏目
首页 > 程序开发 > 移动开发 > Android > 正文
android 8.0开发流程分析
2018-07-04 13:39:26      个评论    来源:wm的博客  
收藏   我要投稿

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&世界杯外围投注官网39;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&世界杯外围投注官网39;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相关的世界杯外围投注网站如下:

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作截获处理,处理流变成如下图:

\

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

锁屏的解锁操作是在锁屏界面向上滑动实现的,通过向上滑动调出解锁界面(如图案、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将这个解锁界面显示出来,灭屏时解锁界面的调用栈如下:

[email protected]” 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&世界杯外围投注官网39;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,然后正常亮屏。

  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.  
  9. private void showLocked(Bundle options) {
  10. // 获取PARTIAL_WAKE_LOCK
  11. mShowKeyguardWakeLock.acquire();
  12. }
  13.  
  14. private void handleShow(Bundle options) {
  15. synchronized (KeyguardViewMediator.this) {
  16. // 释放PARTIAL_WAKE_LOCK
  17. mShowKeyguardWakeLock.release();
  18. }
  19. }
  20. }
点击复制链接 与好友分享!回本站首页
上一篇:Flutter与Android原生的交互实例讲解
下一篇:Android Studio依赖方式实例讲解
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑--致力于做实用的IT技术学习网站