在总结锁屏代码之前,有两个中心思想要铭记于心
A) KeyguardHostView就是我们最终所要展示的界面,所以不论用什么方法手段,都要将这个KeyguardHostView添加到窗口中,后续填充它,都是细节问题
B) 那么问题来了,通常我们将一个view添加到窗口中会用什么方法呢?
答案有两种 1 WindowManager.addView() 2 LayoutInflater.inflate(resID, parentView, true); 而在锁屏代码中这两种方法都有用到
-----------------------------------------------华丽丽的分割线-------------------------------------------------------------------
接下来用一张图来解释流程
-------------------------------又是可爱的分割线--------------------------------------------------------
从图中可以看到, 开机后首先从PhoneWindowManager的systemReady方法调用两个包装类(KeyguardServiceDelegate.java KeyguardServiceWrapper.java)之后会调用到KeyguardService.java中的onSystemReady,进而调用锁屏代码的总调度使KeyguardViewMediator.java。它就是锁屏的CEO。做我们软件的都知道,CEO一般不会太牵涉代码问题,只管分配,协调工作,客户/供应链的沟通。在KeyguardViewMediator.java中的mExternallyEnabled变量就是总监与客户谈判的筹码,如果客户第三方通过KeyguardManager.diableKeyguard()方法禁用系统锁屏后,此变量会置为false,从而不会绘制系统锁屏界面,otherwise,将任务大手一甩直接丢给总经理KeyguardViewManager.java.具体代码如下:
<span style="font-size:12px;"> /** * Enable the keyguard if the settings are appropriate. */ private void doKeyguardLocked(Bundle options) { boolean isSimSecure = mUpdateMonitor.isSimPinSecure(); ///M: if another app is disabling us (except Sim Secure), then don't show if ((!mExternallyEnabled && !isSimSecure)|| PowerOffAlarmManager.isAlarmBoot()) { if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because externally disabled"); return; } 。。。 showLocked(options); } private void handleShow(Bundle options) { 。。。 mKeyguardViewManager.show(options); 。。。 } 通过以上代码,所有的任务都已经落实到KeyguardViewManager.java的头上,接下来看看这个总经理是如何工作的 /** * Show the keyguard. Will handle creating and attaching to the view manager * lazily. */ public synchronized void show(Bundle options) { if (DEBUG) Log.d(TAG, "show(); mKeyguardView=" + mKeyguardView); boolean enableScreenRotation = KeyguardUtils.shouldEnableScreenRotation(mContext); if (DEBUG) Log.d(TAG, "show() query screen rotation after"); /// M: Incoming Indicator for Keyguard Rotation @{ KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime(); /// @} <span style="color:#FF0000;">maybeCreateKeyguardLocked(enableScreenRotation, false, options);</span> if (DEBUG) Log.d(TAG, "show() maybeCreateKeyguardLocked finish"); maybeEnableScreenRotation(enableScreenRotation); // Disable common aspects of the system/status/navigation bars that are not appropriate or // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED // activities. Other disabled bits are handled by the KeyguardViewMediator talking // directly to the status bar service. int visFlags = View.STATUS_BAR_DISABLE_HOME; if (shouldEnableTranslucentDecor()) { mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; } if (DEBUG) Log.d(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")"); mKeyguardHost.setSystemUiVisibility(visFlags); mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); mKeyguardHost.setVisibility(View.VISIBLE); <span style="color:#FF0000;">mKeyguardView.show();</span> mKeyguardView.requestFocus(); if (DEBUG) Log.d(TAG, "show() exit; mKeyguardView=" + mKeyguardView); } 关键代码所以全部贴出来分析分析吧, <span style="color:#009900;">//有一点需要提前注意mKeyguardHost只是一个空View,mKeyguardView才是真正的KeyguardHostView</span> boolean enableScreenRotation用来判断是否允许屏幕旋转, KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime();此行代码是为了重新设置query的时间,比如未接来电,未读短信等,之所以重新set是为了仿照 iphone手机,绘制锁屏时,查询未读信息/未接电话的数目是针对本次锁屏后收到的未读信息/未接电话
maybeCreateKeyguardLocked(enableScreenRotation, false, options); 我擦~终于到关键代码了,此方法就是真正创建锁屏的方法,来一睹芳容吧 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force, Bundle options) { if (mKeyguardHost != null) { mKeyguardHost.saveHierarchyState(mStateContainer); } if (mKeyguardHost == null) { if (DEBUG) Log.d(TAG, "keyguard host is null, creating it..."); <span style="color:#990000;">mKeyguardHost = new ViewManagerHost(mContext);</span> int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; if (!mNeedsInput) { flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } final int stretch = ViewGroup.LayoutParams.MATCH_PARENT; final int type = WindowManager.LayoutParams.TYPE_KEYGUARD; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( stretch, stretch, type, flags, PixelFormat.TRANSLUCENT); lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; lp.windowAnimations = R.style.Animation_LockScreen; lp.screenOrientation = enableScreenRotation ? ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; if (ActivityManager.isHighEndGfx()) { lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; } lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; /// M: Poke user activity when operating Keyguard //lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; lp.setTitle("Keyguard"); mWindowLayoutParams = lp; ///M: skip add KeyguardHost into viewManager in AT case if (!KeyguardViewMediator.isKeyguardInActivity) { <span style="color:#CC0000;">mViewManager.addView(mKeyguardHost, lp);</span> } else { if (DEBUG) Log.d(TAG, "skip add mKeyguardHost into mViewManager for testing"); } KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mBackgroundChanger); } /// M: If force and keyguardView is not null, we should relase memory hold by old keyguardview if (force && mKeyguardView != null) { mKeyguardView.cleanUp(); } if (force || mKeyguardView == null) { mKeyguardHost.setCustomBackground(null); mKeyguardHost.removeAllViews(); <span style="color:#CC0000;">inflateKeyguardView(options);</span> mKeyguardView.requestFocus(); } updateUserActivityTimeoutInWindowLayoutParams(); mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); mKeyguardHost.restoreHierarchyState(mStateContainer); }此处红色代码中通过 mViewManager.addView(mKeyguardHost, lp);将初始话的mKeyguardHost(空view)通过WindowManager.addView的方式添加到窗口之中