在我看来android有一张俊俏脸叫launcher,而这张脸上有一张面纱叫锁屏,就像诗中写的“千呼万唤始出来,犹抱琵琶半遮面”玩android就要从这面纱玩起。
这张面纱虽然美丽,但是有多少人知道她的故事呢?要讲述这个故事就要从一个类开始,这个类叫做SystemServer,学习android的人都知道,有了这个类才有android绚丽的开始。
先抛开SystemServer其它的成员不说,今天就只从wm这个成员说起。wm是WindowManagerService的对象,从名字我们就可以想象WindowManagerService这个类是做什么的,Window是窗口,Manager是管理者,Service是服务,所以可以简单翻译成管理窗口的服务。窗口是实体,服务是虚体,这个类是从虚到实的一个转变。这样我们就很明白了,我们通过WindowManagerService这个类来管理Window这个实体的,那我们又是通过什么来管理Service这个虚体的呢?这时我们就要提到另一个类叫做ServiceManager,这个类的内容很简单,也就几个成员,但是他的贡献可不那么简单了,我们也先从名字分析,Service是服务,Manager是管理者,简单翻译就是服务的管理者。到这里我们就大致明白了,ServiceManager是管理服务的,而通过他我们可以管理WindowManagerService,而通过WindowManagerService我们可以管理Window。所以顺理成章我们只要抓住ServiceManager就可以管理Window了。
说了这么多有人会问了,这与SystemServer和锁屏有什么关系呢?先别着急,听我慢慢道来。WindowManagerService和ServiceManager都是SystemServer这个类中的成员。为什么他们会同时出现在这里呢?这就是SystemServer的神奇之处了,android手机系统启动到某个阶段就会调用SystemServer这个类,这个类是做什么用的呢?同样我们还是先从名字说起,System系统,Server是服务器,简单说就是系统的服务器,听着这个名字就会知道到他有多霸气了吧。霸气是霸气,但是我们不能被吓到,其实这个累只简单的做了三件事,第一件把系统服务和某些特殊的服务和我们自定义的一些服务托付给ServiceManager管理。第二件托付完成之后告诉系统的其它类,系统启动完毕了,也就是systemReady,你们该干啥干啥吧。第三件你们都好好干活哦,我一直监视着你们呢。最后他转变成了一个监工,谁偷懒我就抽谁。这下我们明白了为什么ServiceManager和WindowManagerService会同时出现在SystemServer这个家族中了吧。
说到这里有人又会问了,这和锁屏有什么关系呢?还是那句话,先别着急,我们慢慢说。上面我们提到,systemReady干了一件事,是告诉系统其它类,系统启动完毕,你们该干什么干什么,对了,在这个阶段systemReady同样会告诉WindowManagerService,让他去做自己的事情,从代码中我们可以看出
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
所以我们就可以从WindowManagerService的systemReady知道WindowManagerService要去干什么了
public void systemReady() {
mPolicy.systemReady();
}
这里很简单他只是告诉另一个类系统启动完毕了,在这里我们先插个小曲,说说这个mPolicy,这是WindowManagerPolicy的对象,WindowManagerPolicy当然我们仍然从名字说起,Window窗口,Manager管理者,Policy代理,所以就叫做窗口管理者的代理,明白了吧,就像一家公司,这家公司有很多代理,代理会帮公司做部分业务,而这个代理不属于这家公司。就这么简单。从代码中我们可以看出WindowManagerPolicy这个代理是个接口,就像公司代理的接头人,引荐人一样,只告诉别人我们这有一家公司,至于公司做什么的,您去了就会有人给你介绍。而介绍WindowManagerPolicy这个代理公司做什么是PhoneWindowManager,所以我们要知道WindowManagerPolicy在systemReady时做了什么,就要去找PhoneWindowManager,他会告诉我们接下来做什么
/** {@inheritDoc} */
public void systemReady() {
// tell the keyguard
mKeyguardMediator.onSystemReady();
android.os.SystemProperties.set("dev.bootcomplete", "1");
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
public void run() {
updateSettings();
}
});
}
}
很明显了,这里做了几件事情,第一件告诉mKeyguardMediator系统启动完毕了,你该做什么事情就做吧,不要总是闲着,第二件去保存dev.bootcomplete这么一个值,说boot启动完成了,可能有的代码里没有这个,视情况而定。第三件去更新了两个东西,至于这两个是什么,这里就暂时不再讲解,有兴趣的童鞋可以自己学习。
说到这里,我们美丽的面纱锁屏开始浮出水面了,那就是mKeyguardMediator这个成员,mKeyguardMediator这个成员是KeyguardViewMediator的对象,这个对象是做什么的呢?照样,我们还是从名字分析,Keyguard守卫,指的就是锁屏了,View视图,Mediator传递者,所以简单可理解为锁屏视图的传递者,这下明白了吧,这个类是用来传递视图的。所以我们就去看KeyguardViewMediator的onSystemReady就知道在做什么了
public void onSystemReady() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
doKeyguardLocked();
}
}
这里我们可以看到他只做了一件事,就是调用doKeyguardLocked这个方法了,阅读代码,我们可以看到在这个方法中,做了很多屏障,屏障是什么呢?就是if什么,然后不成立就return返回,这些都是守护和必要的返回。最终冲破屏障到了一个叫showLocked的方法,就像流水线一样我们只要接着往下看就成了
private void showLocked() {
if (DEBUG) Log.d(TAG, "showLocked");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW);
mHandler.sendMessage(msg);
}
这个方法中做了两件事,第一件启动WakeLock,至于这个是做什么用的,这里暂时不做解释,有兴趣的童鞋可以做进一步了解,第二件事就是发了一个SHOW消息,这时有时流水线了,我们直接看接受到这个消息时做了什么
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TIMEOUT:
handleTimeout(msg.arg1);
return ;
case SHOW:
handleShow();
return
很明白了去调用了一个叫handleShow的方法,由于这个方法比较重要,这里我把代码全部贴出来
private void handleShow() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleShow");
if (!mSystemReady) return;
mKeyguardViewManager.show();
mShowing = true;
adjustUserActivityLocked();
adjustStatusBarLocked();
try {
ActivityManagerNative.getDefauKeyguardViewManagerlt().closeSystemDialogs("lock");
} catch (RemoteException e) {
}
// Do this at the end to not slow down display of the keyguard.
playSounds(true);
mShowKeyguardWakeLock.release();
}
}
这个方法中也做了几件事,第一件事mKeyguardViewManager.show();很明显去show了一个东西,这个东西是什么我们一会再说,第二件事adjustUserActivityLocked();adjustStatusBarLocked();去做了一些锁屏时的设置,具体是什么设置,这里不再解释,有兴趣的童鞋可以字节去研究,第三件事playSounds(true);很明显去播放了声音,这个声音就是锁屏时的声音,第四件事mShowKeyguardWakeLock.release();这个和刚才的那个mShowKeyguardWakeLock.acquire();是对应的,具体是什么,有兴趣的童鞋可以自行研究,这里不再赘述。
现在我们就看mKeyguardViewManager.show();这个,mKeyguardViewManager是KeyguardViewManager的对象,同样还是名字分析法来解读KeyguardViewManager类,Keyguard守卫,也就是锁屏,View视图,Manager管理者,所以就是锁屏视图的管理者,这个和刚才提到的传递着不同,这个是用来管理锁屏视图的。现在我们去解读这个类。在KeyguardViewManager这个类的show方法中是一堆代码,这里我们不在一一讲解,只捡重要的说,我们可以看到mKeyguardHost这样一个方法,是这样写的if (mKeyguardHost == null) 然后做了一些事情,做了什么事情暂且放下不说,先来说说mKeyguardHost这个方法,这个方法是一个FrameLayout,这个FrameLayout想必大家不陌生吧,就是一个布局,至于是一个什么样的布局,请大家自己研究,接着我们说说那个if (mKeyguardHost == null)中做了什么,还是一堆代码,简单点说就是当mKeyguardHost 等于空的时候就去new这么个东西,然后对他做一些布局调整,最终调用mViewManager.addView(mKeyguardHost, lp);把mKeyguardHost添加到了mViewManager中去了,mViewManager是做什么用的,我们先来看看,是ViewManager的对象,ViewManager是做什么的呢?我们依然可以从名字中看出来是管理View的。这样我们就得到了一个mKeyguardHost这样一个布局,得到这个布局后做了什么呢?接着往下看
if (enableScreenRotation) {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
} else {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
}
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
if (mKeyguardView == null) {
if (DEBUG) Log.d(TAG, "keyguard view is null, creating it...");
mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
mKeyguardView.setId(R.id.lock_screen);
mKeyguardView.setCallback(mCallback);
final ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mKeyguardHost.addView(mKeyguardView, lp);
if (mScreenOn) {
mKeyguardView.show();
}
}
// Disable aspects of the system/status/navigation bars that are not appropriate or
// useful for the lockscreen 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_BACK
| View.STATUS_BAR_DISABLE_HOME
);
mKeyguardHost.setSystemUiVisibility(visFlags);
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
mKeyguardHost.setVisibility(View.VISIBLE);
mKeyguardView.requestFocus();
}
很明显了,首先判断一下是否支持横竖屏,就是当屏幕横着的时候是否也把屏幕中的内容横过来,然后是updateViewLayout,来刷新mKeyguardHost这个布局,接着是去判断mKeyguardView是不是为空,为空就去得到这么个东西,然后对他进行必要的设置调整,最终把mKeyguardView这个布局添加到刚才的那个布局mKeyguardHost中,再判断屏幕是亮着的mScreenOn就去show这个mKeyguardView。这里我们解释一下mKeyguardHost.addView(mKeyguardView, lp);句话,就是把一个布局放到另一个布局中去,当然了需要先设置一下这个布局的大小了,放的位置了等等,就像我们有两张纸,把小的一张放到大的那张上面一样。在这段代码下面还有几行代码,大家一看就很明白了,就是mKeyguardView不为空的时候去更新布局,设置,最终把他显示出来等等,这些是在我们第二次,第三次...锁屏和第一次初始化之后会走这里,这里做个小插曲,从这几行代码中我们可以知道锁屏界面在开机之后一直都是存在的只是隐藏了起来看不到而已,其它的就不再赘述。
接着我们就去说说mKeyguardView.show();这个东西,mKeyguardView这个是KeyguardViewBase的对象,而KeyguardViewBase是什么呢?从名字可以看出来是锁屏视图的基础,说白了就是锁屏布局都是建立在KeyguardViewBase这个布局上的,所以可以肯定这个布局很重要。所以我们就必须去看看这个类,进去之后我们会发现他是个抽象类,啥是抽象类呢?给大家解释一下哦,土话就是他是个空架子,没啥实质内容,有人问了,没啥实质内容要他做什么呢?还有那实质内容区哪里了呢?好,现在我就给大家解释一下他的作用,在我理解他就是承上启下,承上启下大家应该不陌生,小学的时候在写作文的时候老师总是说要承上启下,当然他还有其他作用,具体是什么大家就去学一下java了。那实质内容去哪里了呢?很显然,谁继承了他,实质内用就在谁那里,这样我们可以找到继承他的类LockPatternKeyguardView,在这个类中我们可以看到show的实质内用
@Override
public void show() {
// Emulate activity life-cycle for both lock and unlock screen.
if (mLockScreen != null) {
((KeyguardScreen) mLockScreen).onResume();
}
if (mUnlockScreen != null) {
((KeyguardScreen) mUnlockScreen).onResume();
}
if (usingFaceLock() && !mHasOverlay) {
// Note that show() gets called before the screen turns off to set it up for next time
// it is turned on. We don't want to set a timeout on the FaceLock area here because it
// may be gone by the time the screen is turned on again. We set the timeout when the
// screen turns on instead.
showFaceLockArea();
} else {
hideFaceLockArea();
}
}
很明显了,首先去调用了mLockScreen的onResume,然后可能调用mUnlockScreen的onResume,为什么我会说可能呢?前一个我怎么不说可能呢?因为mLockScreen,我们刚才提到了,说锁屏界面一直存在,只是隐藏了,对了mLockScreen个东西自从我们开机之后是一直都存在的,有人问了,那他有没有不存在的时候呢?有,我还真遇到过,不过这时候的锁屏已经没有了,就是你按power键灭屏,再按power键亮屏,没有锁屏界面。意思就是你的手机有问题了,需要解决了,怎么解决呢,通过前面的学习,可能有的童鞋已经知道,就是重新开机了。那我什么又说mUnlockScreen这个是可能的呢?首先我们应该搞明白mLockScreen和mUnlockScreen的区别,这个很容易把人搞糊涂,认为一个是上锁一个是解锁,这样认为就大错特错了,mLockScreen是我们最常见的锁屏例如长按解锁,滑动解锁等等吧,统称动作类锁屏,而mUnlockScreen也是我们常见的解锁,例如密码解锁,画线解锁等等吧,统称密码类解锁,这样说不知道大家是否可以理解。这样一说有人就犯糊涂了,这两句话怎么能并排写呢?这样不是有可能出现两个锁屏界面吗?我可以肯定的告诉你,如果不采取措施,就是会出现两个锁屏,大家可以试试,如果你的手机真的出现两个解锁,不能说他不好,如果你的手机只出现一个解锁,说明设计你手机的程序员做了处理。下面的几句就不做解释,自己研究。
接下来我们说说((KeyguardScreen) mLockScreen).onResume();这个,这个是什么呢?也就是mLockScreen,这个我们很容易就能找到new的地方了
View createLockScreen() {
View lockView = null;
lockView = new LockScreen(
mContext,
mConfiguration,
mLockPatternUtils,
mUpdateMonitor,
mKeyguardScreenCallback);
这个地方不同代码可能不同,因为大家会在这里改动,但大致相同,就是去new了一个LockScreen,而LockScreen又是什么呢?当我们找到他的时候就会发现大家熟悉的东西出现在了我们面前
class LockScreen extends LinearLayout implements KeyguardScreen {
他就是一个layout布局,最终我们的锁屏就是一个布局了。我们要写我们自己的锁屏就从这里下手吧。布局我就不做解释了,这是做android的必修课了。当然了这里面包含了,当什么情况下解锁,未接短息的显示,未接电话的显示,充电的显示,播放mp3是如何显示,等等丰富的内用,我们的面纱锁屏之所以炫美,就是在这里实现的。另外我们发现LockScreen还implements了KeyguardScreen,那KeyguardScreen又是做什么的呢?我们去看看
public interface KeyguardScreen {
/**
* Return true if your view needs input, so should allow the soft
* keyboard to be displayed.
*/
boolean needsInput();
/**
* This screen is no longer in front of the user.
*/
void onPause();
/**
* This screen is going to be in front of the user.
*/
void onResume();
/**
* This view is going away; a hook to do cleanup.
*/
void cleanUp();
}
我们可以看到他也是个接口,有四个方法,分别是needsInput是否需要输入法,onPause,View在你眼前消失的时候调用,onResume,View在你眼前出现的时候调用,cleanUp,清除的时候调用。说明白了大家就会知道为什么要implements这个东西了 。
至此开机之后出现锁屏界面的过程已经分析完毕,当然大家只需要取其精华即可,正如我开篇寄语中所说,给android大师们取乐,给后来者抛砖引玉,希望大家不要在背后骂我就谢天谢地。