最近在改android锁屏代码,对他的代码有了一个初步的认识,以下是我的一点总结。
先贴一些前辈们写的博客,我是参照着他们的经验再分析的源代码。
1.
锁屏代码的结构:
1.锁屏大总管:KeyguardViewMediator(不可直接获得他的实例对象)
从代码中的注释就可以看出这个类有多牛掰:调度锁屏相关的请求。包括查看锁屏状态、电源管理事件(决定是否显示锁屏或重置)、
window manager的回调函数(通知它锁屏显示的时间)、改变锁屏view状态的触摸事件等。我们在屏幕关闭的时候显示锁屏界面,所以当我们
打开屏幕的时候可以立即显示锁屏界面。锁屏的ui线程就是在他里面创建的,所以所有改变界面的事件都应该通过发消息给里面的handler来更新界面。
这个类是在KeyguardService服务启动的时候就创建,而锁屏所在的application中设置了android:persistent="true"属性,所以这个是一直存在在系统里的。
这个类的构造函数中可以看出,他申请了PowerManager,UserManager,注册了mBroadcastReceiver广播接收器(用于时间到后自动锁屏),AlarmManager,
KeyguardUpdateMonitor(锁屏关键的一个管理器),WindowManager,
KeyguardViewManager(锁屏状态管理器),ContentResolver(用于读取系统配置,根据上次设置来加载相应的锁屏界面),SoundPool,SearchManager。
这个慢慢流程是在开机启动的时候启动的,在系统启动完后就不需要执行了,要不然每次进锁屏界面还不是卡死。。所以他是一个长期在内存中的类,
那么其申请的以上类也都会长期贮藏在系统中,其中由我们自己控制的有两个类,KeyguardViewManager和KeyguardUpdateMonitor。所以,在上面两个类中可以保存一些锁屏相关的状态参数,而不用担心被系统杀死。
KeyguardViewMediator这个类控制着整个keyguard的状态,framework通过绑定KeyguardService.java这个服务获取到控制锁屏的KeyguardViewMediator类,
通过他控制锁屏。framework具体实现是通过代理frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java。
在系统启动好后调用onSystemReady(),向KeyguardUpdateMonitor中注册了一个mUpdateCallback回调函数,类型是KeyguardUpdateMonitorCallback,这个
是更新锁屏界面的回调函数总集合。如果想增加一些显示内容,那么就可以在里面添加回调函数,在KeyguardUpdateMonitor中注册监听和更新。
2.KeyguardUpdateMonitor类:更新锁屏显示内容的管理器。可以称之为:KeyguardContext,保存了一些keyguard的状态信息。通过getInstance获得实例。
构造函数中:注册Settings.Global.DEVICE_PROVISIONED监听器(收到后就会注销),注册各种你关心的广播和数据库监听器(用来更新界面)。
在这个类中,执行了所有的更新界面操作,当锁屏界面添加了一个显示内容的view,就会在它加载完时注册一个KeyguardUpdateMonitorCallback到这个类里,由
这个类统一管理所有更新界面的回调操作。不得不说这个模式很牛逼,把控制层和显示层都隔离开来了,解耦合,改界面的话灰常方便。
3.KeyguardViewManager类:这个類也是非同小可,他主要功能为:创建、展示、隐藏和重置锁屏界面。
这个类也不能直接获得他的对象,创建的时候会传递一个mViewMediatorCallback回调函数过去,状态改变时通过它通知总管类
KeyguardViewMediator来改变锁屏。
在这个类中:创建了ViewManagerHost对象,他是整个锁屏界面的根目录,是一个framelayout。
在这个根目录下又创建了KeyguardHostView对象,他就是我们的主角,他通过inflateKeyguardView()方法加载进布局,父布局是ViewManagerHost对象。
4.KeyguardViewBase类:在主角登场之前先介绍这个类,他是整個锁屏界面KeyguardHostView的基类。他拦截了所有媒体控制键事件。
在KeyguardViewManager类创建主角KeyguardHostView的时候将mViewMediatorCallback这个回调函数也通过setViewMediatorCallback传递过来。
那么所有的锁屏view都拥有了mViewMediatorCallback这个回调函数,也就是有了控制锁屏的能力!!!
5.KeyguardHostView类:主角登场!!!这个类就是我们看到的锁屏界面。对应布局文件:keyguard_host_view.xml(他有两套布局,我们这只看port的)。
经历了上述这么复杂的流程,终于看到了锁屏界面,汗- -||
在他的构造函数中,申请了AppWidgetHost,AppWidgetManager,KeyguardSecurityModel,KeyguardViewStateManager,在构造函数中判断是否需要显示
音乐控制条(transportcontrolview).
在加载完后onFinishInflate中:初始化各个组件,赋值给他们对应的回调函数和属性。如果此时系统还没启动完成,那么就创建一个runnable,推迟更新界面,
否则直接更新界面——updateAndAddWidgets().
在updateAndAddWidgets中:1) cleanupAppWidgetIds清除相同id的widget;2)addDefaultWidgets() 添加默认的widget,就是含有+号的界面,可以添加其他widget;3)addWidgetsFromSettings():从设置中加载对应的widget。addDefaultStatusWidget(int index)在index页添加默认的widget——显示时间界面.
addWidget(int appId, int pageIndex, boolean updateDbIfFailed),添加其他widget——照相界面和音乐播放界面等。4)....其他初始化;5)mSwitchPageRunnable
切换正确的页面,延迟执行,当他们都初始化完毕后再执行,所以用了个runnable。
以上初始化完毕后,会调用到showSecurityScreen(),匹配设置中的值,加载对应的解锁布局。所有的解锁布局都是实现了KeyguardSecurityView接口,在里面写一些通用的方法,这就是传说中的模板设计模式了吧?
showSecurityScreen中:1)getSecurityView获取对应锁屏模式的布局——getSecurityViewIdForMode()调用到该方法返回布局id;2)遍历当前锁屏界面中的子元素,如果有相同id的就把他赋值给view;3)如果没找到对应的布局,那么就获取布局id-getLayoutIdFor()重新加载布局并添加到mSecurityViewContainer容器中。
3)如果是KeyguardSelectorView(默认的滑动解锁界面),那么就添加一个carrierText,他是显示当前sim卡状态的view,对应@layout/keyguard_eca。
申请在阿阿阿阿