Android框架浅析之锁屏(Keyguard)机制原理(转载)

出处:http://blog.csdn.net/qinjuning

最近终于成功的摆脱了FM收音机,迈向了新的模块:锁屏、状态栏、Launcher---姑且称之为“IDLE”小组,或许叫手机
美容小组,要是能施展下周星星同学的还我漂漂拳,岂不快哉。 OK,闲话打住,咱开始正文。

1
2
3
4
5
6
7
  本文主要内容:

      1、分析锁屏界面的组成 ;
      2、基于源代码分析锁屏相关类 ;
      3、提出一种在框架取消锁屏的方法 。

  花了一些时间研究Android原生的锁屏框架---Keyguard,今天就慢慢的讲解下我自己对这个模块的总结,因为目前还处于

理论学习的状况,很多细节以及功能上的实现有待后续的补充完整。

1
2
3
4
5
6
 本文分析适合Android2.2和2.3版本,Android4.0尚不清楚。整个锁屏源码基本上完全一样,只是改变了文件存放路径而已。
 本文分析版本具体是Android2.3版本。

 源文件路径主要有两个:
         frameworksbasepolicysrccomandroidinternalpolicyimpl   ---->锁屏框架
         frameworksbasecorejavacomandroidinternalwidget          ----> 提供了一些的自定义View.

一、锁屏界面的组成

1
   通常 Android手机上大家常见的界面只有一种,成功后即可解锁进入界面了。其实在Android手机中,正常的锁屏界面由

两种不同性质的界面组成:

1
2
3
4
5
6
第一种界面称之为LockScreen界面(为了叙述方便,我们姑且称为“解锁界面),即我们通常所见到的界面,手机厂商一般定制
      该界面。界面如下所示:


      该界面对应自定义View的是LockScreen.java类
       路径位于:frameworkspoliciesbasephonecomandroidinternalpolicyimplLockScreen.java

第二种界面称之为UnLockScreen(为了后文叙述方便,我们姑且称为“开锁界面”),一般由Android源码提供,有如下四种:
①、图案开锁界面 ---- PatternUnlockScreen.java类 (自定义LinearLayout)
路径位于:frameworkspoliciesbasephonecomandroidinternalpolicyimplPatternUnlockScreen.java
界面显示为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
         ②、PIN开锁界面 ---- SimUnlockScreen.java 类           (自定义LinearLayout)
                  路径位于:frameworkspoliciesbasephonecomandroidinternalpolicyimplSimUnlockScreen.java
                  界面显示为:   (图片省略)

         ③、密码开锁界面 ---- PasswordUnlockScreen.java类   (自定义LinearLayout)
                  路径位于:frameworkspoliciesbasephonecomandroidinternalpolicyimplPasswordUnlockScreen.java
                  界面显示为:


         ④、GoogleAccount 开锁界面 ,即Google账户开锁界面。一般用于当用户输入密码错误次数超过上限值时,系统会提示
   你输入Google账户去开锁。注意:开启它需要你手动设置账户与同步,否则该界面是不会出来的。
                对应的源文件是: AccountUnlockScreen.java类    (自定义LinearLayout)
                路径位于:frameworkspoliciesbasephonecomandroidinternalpolicyimplAccountUnlockScreen.java   
                界面显示为:


可以按照如下办法选择开启哪一种开锁界面: 设置—>位置和安全—>设置屏幕锁定  ,具体选择那种开锁界面。

显示规则
     当然,这两种界面的组合也是有很多变化的,总的规则如下:
        首先显示LockScreen界面,接着判断是否开启了UnLockScreen界面,如果设置了UnLockScreen界面,则进入对应的
UnLockScreen界面去解锁,才算成功解锁。但,存在一种特殊的情况,就是假如我们选择了图案  UnLockScreen界面,是不会
显示LockScreen界面,而只会显示UnLockScreen界面。

二、锁屏界面的实现

1
   我们知道, 任何一种界面都是由各种View/ViewGroup(当然包括自定义的)组成的,然后根据系统对应的状态值的改变去更新

这些View的显示状态,锁屏界面自然也是如此。锁屏界面的实现最顶层是采用了FrameLayout去控制的,当然内部也嵌套了很
多层,内嵌层数的增多的一点好处就是我们可以分开而治,具体针对每层去做相应的更新。难处就是看代码看的很蛋疼。

1
   当界面复杂时,我不得不提Google为开发人员提供的一款优秀工具了---Hierarchy Viewer ,通过它,我们很清晰的弄明白整

个View树的继承层次,一个布局结构,当然,看源代码也是必须的。
关于Hierarchy Viewer的使用请参考该博客:
《Android 实用工具Hierarchy Viewer实战》

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
   整个锁屏界面的继承层次如下(部分以及设置了图案开锁界面),更加完整的图请使用Hierarchy Viewer 工具查看。





  上图中比较重要的几个视图说明如下:
   LockPatternKeyguardView 继承至FrameLayout :作为LockScreen和UnLockScreen的载体,用来控制显示LockScreen
                  还是UnLockScreen界面。
   LockScreen 继承至FrameLayout   
   PatterUnlockScreen    ViewGroup类型 : 图案解锁界面
   KeyguardViewHost继承至FrameLayout, 该ViewGroup作为顶层View,作为WindowManager的装饰对象添加至窗口。 
    它和LockPatternKeyguardView关系相当于DecorView和我们Activity内设置的资源布局一样。  

三、锁屏机制的类结构说明

1
         看了几天代码,才稍微的理清了下头绪。看完后给我的感觉就是代码之间太BT了,几个类的唯一实例传来传去,太容易混

乱了。接下来我们分析下一些主要的类及其重要的函数,更多函数实现,大家可以自己参考源代码。

1
2
3
4
5
6
7
8
   PS : 由于这些类的结构图比较简单,因此就没画类图了。主要是从源码角度来分析这些代码逻辑。

 1、 KeyguardScreen 类    接口    

   功能:该接口的主要功能是为每个需要显示的界面:LockScreen或者UnLockScreen定义了四个方法,使其在不同的状态能够
      得到相应处理。优点就是:   利用设计原则的面向接口编程,减少对具体对象的依赖。
  路径:frameworksbasepolicysrccomandroidinternalpolicyimplKeyguardScreen.java
 其源代码释义如下:

[java] view plaincopyprint?
/**
* Common interface of each {@link android.view.View} that is a screen of
* {@link LockPatternKeyguardView}.
/

public interface KeyguardScreen {

/
* Return true if your view needs input, so should allow the soft
* keyboard to be displayed. /

boolean needsInput(); //View是否需要输入数值,即该界面需要键盘输入数值

/
* This screen is no longer in front of the user.*/

void onPause();//当该界面不处于前台界面时调用,包括处于GONE或者该界面即将被remove掉

/** This screen is going to be in front of the user. /

void onResume();//相对于onPause()方法,当该界面不处于前台界面时调用,处于VISIBLE状态时调用

/
* This view is going away; a hook to do cleanup. */

void cleanUp();//该界面即将被remove掉 ,即不在需要

}

2、KeyguardScreenCallback类 接口

1
2
3
  功能:每个需要显示的界面:LockScreen或者UnLockScreen都保存了该对象的唯一实例,用来向控制界面汇报情况。
  路径:frameworksbasepolicysrccomandroidinternalpolicyimplKeyguardScreenCallback.java
   其源代码释义如下:

[java] view plaincopyprint?
/** Within a keyguard, there may be several screens that need a callback
* to the host keyguard view.
/

public interface KeyguardScreenCallback extends KeyguardViewCallback {

/
* Transition to the lock screen*/

void goToLockScreen(); //当前界面跳转为LockScreen ,而不是UnLockScreen

/** Transition to the unlock screen.*/

void goToUnlockScreen();//LockScreen成功开锁 ,是否需要显示UnLockScreen,否则,直接开锁成功。

//忘记了开锁图案,即我们需要跳转到Google 账户去开锁。

void forgotPattern(boolean isForgotten);

boolean isSecure();//当前机器是否安全,例如:设置了图案、密码开锁等

//该函数还不太懂,可能是是否只需要验证UnlockScreen界面,即可成功开锁。

boolean isVerifyUnlockOnly();

/**Stay on me, but recreate me (so I can use a different layout).*/

void recreateMe(Configuration config); //重新根据手机当前状态,显示对应的Screen.

/** Take action to send an emergency call. */

void takeEmergencyCallAction(); //紧急呼叫时的处理行为.

1
2
3
4
5
6
7
8
9
/** Report that the user had a failed attempt to unlock with password or pattern.*/  
void reportFailedUnlockAttempt(); //在UnLockScreen界面登陆失败时处理  

/** Report that the user successfully entered their password or pattern.*/  
void reportSuccessfulUnlockAttempt();//在UnLockScreen界面登陆成功时处理  

/** Report whether we there's another way to unlock the device. 
 * @return true */  
boolean doesFallbackUnlockScreenExist();  

}

1
  其唯一实现类位于LockPatternKeyguardView类的内部类(稍后讲到)。

3、KeyguardViewCallback类 接口

1
2
3
     功能: 提供了一些接口用来接受用户操作Screen的结果。
     路径:frameworksbasepolicysrccomandroidinternalpolicyimplKeyguardViewCallback.java
     其源代码释义如下:

[java] view plaincopyprint?
/**
* The callback used by the keyguard view to tell the {@link KeyguardViewMediator}

* various things.
*/

public interface KeyguardViewCallback {

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 /** Request the wakelock to be poked for the default amount of time. */  
 void pokeWakelock();  //保存屏幕在一定时间内处于亮屏状况 , 默认时间为5s或者10s  
 /** Request the wakelock to be poked for a specific amount of time.  */  
 void pokeWakelock(int millis);//根据给定时间值,使屏幕在该事件段内保持亮屏状况  

 /** Report that the keyguard is done. 
  * @param authenticated Whether the user securely got past the keyguard. 
  *   the only reason for this to be false is if the keyguard was instructed 
  *   to appear temporarily to verify the user is supposed to get past the 
  *   keyguard, and the user fails to do so. */  
 //成功的完成开锁,可以进入手机界面了。参数为ture表示是否正大光明的开锁,例如:图案正确,密码输入正确。  
 void keyguardDone(boolean authenticated);   
 /**Report that the keyguard is done drawing. */  
 void keyguardDoneDrawing(); //整个锁屏界面draw()过程绘制完成时,回调该方法.  

}

1
2
3
4
5
6
                  其唯一实现类是   KeyguardViewMediator类(稍后讲到)

4、 KeyguardWindowController类 接口

     功能:提供通用 接口,判断该界面是否需要显示输入法窗口。
     其源代码释义如下:

[java] view plaincopyprint?
/**
* Interface passed to the keyguard view, for it to call up to control
* its containing window.
/

public interface KeyguardWindowController {

/
* Control whether the window needs input -- that is if it has
* text fields and thus should allow input method interaction. */

void setNeedsInput(boolean needsInput); //是否需要显示输入法,为true表示需要。该方法可以想上层报到是否需要显示输入法窗口

}

其唯一实现类是KeyguardViewManager类(稍后讲到)。

1
2
3
4
5、KeyguardViewManager类 

    功能:包装了WindowManager功能了,提供了添加、删除锁屏界面的功能。
      其源代码释义如下:     

[java] view plaincopyprint?
public class KeyguardViewManager implements KeyguardWindowController {

...

private WindowManager.LayoutParams mWindowLayoutParams;

private boolean mNeedsInput = false; //是否需要输入法 , 默认不需要

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 private FrameLayout mKeyguardHost;   //该ViewGroup作为顶层View,作为WindowManager添加至窗口  
 private KeyguardViewBase mKeyguardView; //具体窗口内容。  
 //以上两种的关系相当于DecorView和我们Activity内设置的资源文件一样  

 private boolean mScreenOn = false; //是否处于亮屏状态  
 //构造函数,初始化各种属性  
 public KeyguardViewManager(Context context, ViewManager viewManager,  
         KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {  
    ...  
 }  
 /** 
  * Helper class to host the keyguard view. 
  */  
 private static class KeyguardViewHost extends FrameLayout {  
     ... //KeyguardViewHost类  
 }  

 /** 
  * Show the keyguard.  Will handle creating and attaching to the view manager 
  * lazily. 
  */  //显示锁屏界面  
 public synchronized void show() {         
     if (mKeyguardHost == null) {  
         ...  
         mViewManager.addView(mKeyguardHost, lp);  
     }  
     if (mKeyguardView == null) {  
         ...  
         mKeyguardHost.addView(mKeyguardView, lp);  
         if (mScreenOn) {  
             mKeyguardView.onScreenTurnedOn();  
         }  
     }  
    ...  
 }  
 ...  

 /*** Hides the keyguard view */  
 public synchronized void hide() { //隐藏锁屏界面,也就是说我们成功的解锁了  
     if (mKeyguardHost != null) {  
         mKeyguardHost.setVisibility(View.GONE);  
             ...  
         }  
 }  
 //锁屏界面是否处于显示状态  
 public synchronized boolean isShowing() {  
     return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);  
 }  

}

}

6、 KeyguardUpdateMonitor.java类

1
2
3
   功能:该类的主要功能就是根据listener系统状态值的改变(例如:时间、SIM卡状态、电池电量;使用广播jianting),根据这种状态
       值的改变回调监听了该状态信息的对象实例。
   其源代码释义如下:     

[java] view plaincopyprint?
public class KeyguardUpdateMonitor {

...

private int mFailedAttempts = 0; //当前登录事,已经失败的次数

private ArrayList mInfoCallbacks; //保存所有监听对象 InfoCallback

private ArrayList mSimStateCallbacks ; //保存所有监听对象 SimStateCallback

private static class SimArgs { //Sim状态信息

...

}

/**
* Callback for general information relevant to lock screen.
/

interface InfoCallback {

//电池电量信息改变:参数含义分别如下:是否显示电量信息 、 是否插入电影充电、 当前电池电量值

void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel);

void onTimeChanged(); //时间发生了改变

//网络运营商状态发生了改变 ,例如从中国移动2G变为中国移动3G,或者无服务等 ;

void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn);

/
* Called when the ringer mode changes. /

void onRingerModeChanged(int state);

/
* 电话状态发生了改变 值可能为:EXTRA_STATE_IDLE、EXTRA_STATE_RINGING、EXTRA_STATE_OFFHOOK*/

void onPhoneStateChanged(String newState);

}

/** Callback to notify of sim state change. */

interface SimStateCallback {

void onSimStateChanged(IccCard.State simState); //Sim卡信息发生了改变,例如有正常状况变为ABSENT/MISSING状态

}

1
2
3
4
5
6
7
/*** Register to receive notifications about general keyguard information 
 * (see {@link InfoCallback}. */  
public void registerInfoCallback(InfoCallback callback) {  
    if (!mInfoCallbacks.contains(callback)) {  
        mInfoCallbacks.add(callback);  //注册一个监tingqi
    } ...  
}  

...

}

1
2
3
 7, LockPatternKeyguardView类  (自定义ViewGroup)
       功能:作为LockScreen和UnLockScreen界面的载体,控制显示哪个界面。
         其源代码释义如下:     

[java] view plaincopyprint?
public class LockPatternKeyguardView extends KeyguardViewBase {

...

private View mLockScreen;

private View mUnlockScreen;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private boolean mScreenOn = false;//是否亮屏  

enum Mode {  
    //当前显示界面的Mode Lock 或者UnLock  
}  
enum UnlockMode {  
    ...//开锁界面的几种不同Mode   
}  
//构造函数  
public LockPatternKeyguardView( ...) {       
    //KeyguardScreenCallback的实现对象  
    mKeyguardScreenCallback = new KeyguardScreenCallback() {  
       ...  
    };  
    ...  
}  
public void reset() {  
   ...//重置显示界面  
}  
private void recreateLockScreen() {  
...//重新构建LockScreen  
}  
private void recreateUnlockScreen() {  
   ...//重新构建UnlockScreen  
}  
private void recreateScreens() {  
   ...//重新构建该视图  
}  
public void verifyUnlock() {  
   ...   
}  
public void cleanUp() {  
... //清理资源对象  
}  
private boolean isSecure() {  
   ...//手机设置是否处于安全状态  
}  
private void updateScreen(final Mode mode) {  
   ...//根据参数(Lock/unLock),判断显示为LockScreen或者UnlockScreen界面  
}  
View createLockScreen() {  
...//创建lockScreen  
}  
View createUnlockScreenFor(UnlockMode unlockMode) {  
   ...//根据不同的Unlock Mode , 创建不同的UnlockScreen  
}  
private Mode getInitialMode() {  
   ...//得到初始化的状态Mode (lock or unlock).  
}  
/** Given the current state of things, what should the unlock screen be? */  
private UnlockMode getUnlockMode() {  
   ...//返回开锁的状态Unlock Mode  
}  
private void showTimeoutDialog() {  
    ... //输入密码超过一定次数时,提示30s后在登录的对话框  
}  
private void showAlmostAtAccountLoginDialog() {  
   ... //显示Google 账户登录对话框  
}  

}

1
2
3
4
   8、KeyguardViewBase类 抽象类  (自定义ViewGroup)

        功能:为LockPatternKeyguardView提供了一组通用的方法 。需要值得注意的方法就是他对某些KeyEvent的监听,
  当他消费监听到这些KeyEvent,我们的App就监听不到这些KeyEvent了 。常用的有KEYEVENT_VOLUME_UP/DOWN等。

[java] view plaincopyprint?
public abstract class KeyguardViewBase extends FrameLayout {

...

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

...

if (interceptMediaKey(event)) {

return true;

}

return super.dispatchKeyEvent(event);

}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private boolean interceptMediaKey(KeyEvent event) {  
    final int keyCode = event.getKeyCode();  
    if (event.getAction() == KeyEvent.ACTION_DOWN) {  
        switch (keyCode) {  
            ...//more keyevent  
            case KeyEvent.KEYCODE_VOLUME_UP:  
            case KeyEvent.KEYCODE_VOLUME_DOWN: {  
                ...  
                // Don't execute default volume behavior  
                return true; //直接返回不在向下传递处理  
            }  
        }  
    }   
    return false;  
}  

}

9、 KeyguardViewProperties.java 接口
功能:提供了创建界面的通用方法。
[java] view plaincopyprint?
public interface KeyguardViewProperties {

//创建一个KeyguardViewBase实例 , 实际是指LockPatternKeyguardView实例

KeyguardViewBase createKeyguardView(Context context,

KeyguardUpdateMonitor updateMonitor,

KeyguardWindowController controller);

1
boolean isSecure();  

}

1
      其唯一实现类是是LockPatternKeyguardViewProperties类(稍后讲到)。

10、LockPatternKeyguardViewProperties类
源代码释义如下:
[java] view plaincopyprint?
public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {

...

//创建一个LockPatternKeyguardView对象

public KeyguardViewBase createKeyguardView(Context context,

KeyguardUpdateMonitor updateMonitor,

KeyguardWindowController controller) {

return new LockPatternKeyguardView(context, updateMonitor,

mLockPatternUtils, controller);

}

//=============================================
// OK ,我知道你看的很纠结了,具体需要时参考源代码看是最明智的。
//=============================================

1
2
3
4
5
我知道代码贴的太多了,没办法,谁让它理解起来就那么费劲呢 ? 你可别犯愁,真正核心的类可还没出来。。

12、KeyguardViewMediator核心类 ,该类是唯一实现了KeyguardViewCallback的类。
       功能:  功能:该类提供了一些接口,由PhoneWindowManager)去访问控制Keyguard.... 
       该类的初始化是在PolicyWindowManager的构造函数中创建的。如下:

[java] view plaincopyprint?
public class PhoneWindowManager implements WindowManagerPolicy {

...

/** {@inheritDoc} */ //由SystemServer调用

public void init(Context context, IWindowManager windowManager,

LocalPowerManager powerManager) {

...//初始化该实例

mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);

}

}

1
   参照源代码,把一些重要的属性和方法的大意给分析下:

[java] view plaincopyprint?
public class KeyguardViewMediator implements KeyguardViewCallback, KeyguardUpdateMonitor.SimStateCallback {

private boolean mSystemReady; //启动成功 由SystemServer调用

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**Used to keep the device awake while to ensure the keyguard finishes opening before 
 * we sleep.*/ //在需要显示锁屏界面时,保持屏幕在某个时间段内为暗屏状态    
private PowerManager.WakeLock mShowKeyguardWakeLock;  
private KeyguardViewManager mKeyguardViewManager; //KeyguardViewManager实例  
/**  * External apps (like the phone app) can tell us to disable the keygaurd.*/  
//是否允许其他App禁止锁屏 , 例如来电时 禁止锁屏  
private boolean mExternallyEnabled = true;  
//处于锁屏状态 , 即显示锁屏  
private boolean mShowing = false;  
// true if the keyguard is hidden by another window  
private boolean mHidden = false; //被其他窗口掩盖 , 例如来电时锁屏被掩盖  
private boolean mScreenOn = false; // 是否亮屏  

public KeyguardViewMediator(Context context, PhoneWindowManager callback,  
        LocalPowerManager powerManager) {  
    ...  
    //构造相关实例对象  
    mKeyguardViewProperties = new LockPatternKeyguardViewProperties(  
            new LockPatternUtils(mContext), mUpdateMonitor);  

    mKeyguardViewManager = new KeyguardViewManager(  
            context, WindowManagerImpl.getDefault(), this,  
            mKeyguardViewProperties, mUpdateMonitor);  
    //解锁成功后发送的Intent  
    mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);  
    mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);  
}  
/** Called to let us know the screen was turned off. 
 *   @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER}, 
 *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or 
 *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}. 
 */  
//屏幕变灰暗  , 原因有如下:以及对应的逻辑处理。  
// 1、OFF_BECAUSE_OF_USER : 用户按下POWER键 , 当前是否处于锁屏界面,若是(mShowing)则重置显示界面,否则重新显示锁屏界面  
// 2、OFF_BECAUSE_OF_TIMEOUT : 屏幕超时,常见情况就是一段时间没有操作屏幕,手机处于灰暗状态。          处理行为:     
//     发送Action值为DELAYED_KEYGUARD_ACTION的广播,因为该类注册了该Intent广播,接受到时会调用doKeyguard()方法锁屏  
// 3、OFF_BECAUSE_OF_PROX_SENSOR:接打电话时,距离感应太近导致暗屏,此时由于PowerManager那儿已经处理了暗屏,不需要做任何事  
// 最后,如果以上逻辑都不成立,调用 doKeyguard()方法显示屏幕                         
public void onScreenTurnedOff(int why) {  
   ...  
}  
/** 
 * Let's us know the screen was turned on. 
 */  
public void onScreenTurnedOn() {  
    synchronized (this) {  
        ...  
        notifyScreenOnLocked();  //通知亮屏  
    }  
}  
/** Enable the keyguard if the settings are appropriate. */  
private void doKeyguard() {  
    synchronized (this) {  
        ...  
        showLocked();//显示锁屏界面  
    }  
}  
//该方法的调用时机就是当按下POWER键时,系统会回调该方法 keyCode值一般为 26  
public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {  
    //操作按键是否能唤醒屏幕   
    if (isWakeKeyWhenKeyguardShowing(keyCode)) {  
        // give the keyguard view manager a chance to adjust the state of the  
        // keyguard based on the key that woke the device before poking  
        // the wake lock  
        wakeWhenReadyLocked(keyCode);//开始唤醒屏幕咯  
        return true;  
    } else {  
        return false;  
    }  
}  
/** {@inheritDoc} */  //在一定时间内保存屏幕为亮屏状态  
public void pokeWakelock(int holdMs) {  
    ...  
}  
//表示成功得分完成了解锁操作  
public void keyguardDone(boolean authenticated, boolean wakeup) {  
    synchronized (this) {  
        //发送给Handler 进行异步处理  
        Message msg = mHandler.obtainMessage(KEYGUARD_DONE);  
        msg.arg1 = wakeup ? 1 : 0;  
        mHandler.sendMessage(msg);  
        if (authenticated) {  
            mUpdateMonitor.clearFailedAttempts(); //清除错误登录次数  
        }  
        ...  
    }  
}  
//Handler对象 , 异步处理  
private Handler mHandler = new Handler() {  
    @Override  
    public void handleMessage(Message msg) {  
        switch (msg.what) {  
          ...  //异步处理  
        }  
    }  
};  
//异步处理完成开锁成功  
private void handleKeyguardDone(boolean wakeup) {   
    handleHide(); //释放该Keyguard对应的窗口  
    mWakeLock.release();  
    mContext.sendBroadcast(mUserPresentIntent); //解锁成功,发送Intent信息  
}  
//显示锁屏界面  
private void handleShow() {  
    synchronized (KeyguardViewMediator.this) {  
        ...  
        mKeyguardViewManager.show();  
        mShowing = true;  
        ...  
    }  
}  
private void handleHide() {  
    synchronized (KeyguardViewMediator.this) {  
        //去除锁屏界面对应的窗口  
        mKeyguardViewManager.hide();  
        mShowing = false;  
       ...  
    }  
}  
//设置状态栏enable状态 , 例如:能否被下拉等  
private void adjustStatusBarLocked() {  
        ...  
        // if the keyguard is shown, allow the status bar to open  
        // only if the keyguard is insecure and is covered by another window  
        boolean enable = !mShowing || (mHidden && !isSecure());  
        mStatusBarManager.disable(enable ?StatusBarManager.DISABLE_NONE : StatusBarManager.DISABLE_EXPAND);  
    }  
}  

}

1
2
3
 该类的很多方法都是由PhoneWindowManager调用访问的。

     基本上把一些重要的类及其相关的方法给走了一遍吧,对看源码的还是很有帮助的。因为我的理解还不是很深入,可能有偏颇的

地方。希望以后能够组件完善起来。 阿门 !

问题:如何在框架中, 解除锁屏 ?

1
   引入:该问题是在CSDN论坛上回答一位网友引发的,最开始由于我也只是简单看了下,因此也就事论事回答了一个小问题。

随着看的越来越深入,慢慢的也在心里长生了涟漪。经过尝试、烧鸡验证,发现如下办法行的通,而且效果还比较好。风险应该
比较小吧。 希望正在在框架修改的同学,慎重行事。

1
   基本思路:毫无疑问,我的想法就是每次需要显示Keyguard---锁屏界面时,我们并不真正的去锁屏,而只是提供了一个空的

方法去给系统调用,让系统觉得我们“锁屏”了,同样也不去真正的隐藏“锁屏”界面,提供一个空壳给系统调用。由于可能涉及
到其它问题,例如:能否下拉状态栏,按下POWER键后,屏幕很快休眠等。Come on ,我们需要统一做处理。

1
2
3
所有步骤函数都发生在KeyguardViewMediator 类中,注释部分为我们所添加的。

  Step 1、  取消  真正的去锁屏实现

[java] view plaincopyprint?
//该方法会显示锁屏界面,我们使其成为一个空壳子

private void handleShow() {

synchronized (KeyguardViewMediator.this) {

if (DEBUG) Log.d(TAG, "handleShow");

if (!mSystemReady) return;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    playSounds(true);  
    //Begin : Modifid by qinjuning       

    //mKeyguardViewManager.show();  //     
    //mShowing = true;              //  
    //adjustUserActivityLocked();   //  
    //adjustStatusBarLocked();     //取消对状态栏的控制  

    //End  
    try {  
        ActivityManagerNative.getDefault().closeSystemDialogs("lock");  
    } catch (RemoteException e) {  
    }  
    mShowKeyguardWakeLock.release();  
}  

}

1
  Step 2、  取消  真正的去隐藏锁屏实现       

[java] view plaincopyprint?
//真正的隐藏屏幕实现

private void handleHide() {

synchronized (KeyguardViewMediator.this) {

if (DEBUG) Log.d(TAG, "handleHide");

if (mWakeAndHandOff.isHeld()) {

Log.w(TAG, "attempt to hide the keyguard while waking, ignored");

return;

}

// only play "unlock" noises if not on a call (since the incall UI

// disables the keyguard)

if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {

playSounds(false);

}

//Begin : Modifid by qinjuning

1
2
3
4
5
6
7
    //mKeyguardViewManager.hide();  
    //mShowing = false;   
    //adjustUserActivityLocked();         
    //adjustStatusBarLocked();         //取消对状态栏的控制  

    //End  
}  

}

1
2
3
以上两步行动后,存在一个Bug(问题),就是唤醒屏幕后,会在指定的时间内屏幕由亮变暗,我们还需要做如下修改

 Step 3、按下POWER键时, 解除屏幕由亮变暗的Bug

[java] view plaincopyprint?
private void handleWakeWhenReady(int keyCode) {

synchronized (KeyguardViewMediator.this) {

if (DBG_WAKE) Log.d(TAG, "handleWakeWhenReady(" + keyCode + ")");

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    // this should result in a call to 'poke wakelock' which will set a timeout  
    // on releasing the wakelock  
    if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) {  
        // poke wakelock ourselves if keyguard is no longer active  
        Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves");  
        //Begin : Modifid by qinjuning    
        //pokeWakelock();  //按下POWER键时, 解除屏幕由亮变暗的Bug     
        //End  
    }  
    /** 
     * Now that the keyguard is ready and has poked the wake lock, we can 
     * release the handoff wakelock 
     */  
    mWakeAndHandOff.release();  

    if (!mWakeLock.isHeld()) {  
        Log.w(TAG, "mWakeLock not held in mKeyguardViewManager.wakeWhenReadyTq");  
    }  
}  

}

1
2
3
   经过真机测试是通过的,但其他风险并不清楚。 这个方法只是提供了一个学习的途径吧。大家慎重行事。
上面Step 1、以及Step 2可以由如下方法代替:
       将属性mExternallyEnabled 设置为 false, 接下来需要显示界面时都不会继续走下去,如下函数:

[java] view plaincopyprint?
/**
* Enable the keyguard if the settings are appropriate.
*/ //显示界面

private void doKeyguard() {

synchronized (this) {

// if another app is disabling us, don't show

if (!mExternallyEnabled) { //mExternallyEnabled 为false

if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
        // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes  
        // for an occasional ugly flicker in this situation:  
        // 1) receive a call with the screen on (no keyguard) or make a call  
        // 2) screen times out  
        // 3) user hits key to turn screen back on  
        // instead, we reenable the keyguard when we know the screen is off and the call  
        // ends (see the broadcast receiver below)  
        // TODO: clean this up when we have better support at the window manager level  
        // for apps that wish to be on top of the keyguard  
        return;  
    }  
    ...  
}  

}

1
  该方法的一个缺点就是,假如存在重新调用了setKeyguardEnabled()设置该值,一切都是白搭( 但从源码看,这点不可能

出现,因为存在另一个判断依据:变量mNeedToReshowWhenReenabled , 其初始值为false,只有成功禁止锁屏之后才置为
true )。 因此,我们可以仿照这个方法,主动添加一个私有变量,禁止显示锁屏界面,即禁止doKeyguard()方法继续走下去。

1
2
3
 OK ,本文到此为止。讲的比较抽象。 大家看代码时多加理解才是王道 。

 显然易见,手机厂商,基于框架只需要修改LockScreen这个自定义ViewGroup即可,其他的一套Google已经为我们

封装好了。

1
  在框架层修改肯定不是最好的,对于第三方的App而言,实现不了该功能。还好,SDK为我们提供了接口类去处理隐藏锁屏接口

的方法,该类是KeyguardManager类,关于该类的简介请参考该博客:
《KeyguardManager简介 》

1
 我们可以通过KeyguardManager类实例获得一个KeyguardManager.KeyguardLock对象,进而调用相应方法去取消锁屏界面

和显示锁屏界面。
KeyguardManager.KeyguardLock的两个方法说明如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
      public void disableKeyguard ()
        功能:取消锁屏界面显示,同时禁止显示锁屏界面。除非你显示调用了reenableKeyguard()方法使能显示锁屏界面。
        public void reenableKeyguard ()

        功能: 使能显示锁屏界面,如果你之前调用了disableKeyguard()方法取消锁屏界面,那么会马上显示锁屏界面。 

 这两个方法最终都会调用到KeyguardViewMediator类的setKeyguardEnabled(boolean enable)方法。
        参数说明: enable = false  对应于disableKeyguard()方法,
                         enable = true  对应于reenableKeyguard()方法。

      该方法原型为: 位于KeyguardViewMediator类中

[java] view plaincopyprint?
/**
* Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide
* a way for external stuff to override normal keyguard behavior. For instance
* the phone app disables the keyguard when it receives incoming calls.
*/

public void setKeyguardEnabled(boolean enabled) {

synchronized (this) {

mExternallyEnabled = enabled; //保存值,该值会在doKeyguard()时用到,如果为false ,则不进行锁屏

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    if (!enabled && mShowing) {  
        if (mExitSecureCallback != null) {//该判断为false  
            ...   
            return ;  
        }  
        mNeedToReshowWhenReenabled = true;  //置为真,以便下次调用  
        hideLocked(); //已经显示了锁屏界面,则取消隐藏界面  
    } else if (enabled && mNeedToReshowWhenReenabled) { //重新显示锁屏界面  
        mNeedToReshowWhenReenabled = false;  

        if (mExitSecureCallback != null) {//该判断为false  

        } else {  
            showLocked(); //显示隐藏界面  
            ...  
        }  
    }  
}  

}

1
   使用这两个方法时,记得加上如下权限:android.permission.DISABLE_KEYGUARD

为了在亮屏时,达到取消显示界面的效果,我们还需要知道 一下两个广播:
屏幕变暗以及屏幕点亮的广播

1
2
3
4
                android.intent.action.SCREEN_ON  --- 屏幕变亮
                android.intent.action.SCREEN_OFF ---- 屏幕点暗

     关于这两个广播的说明请参考如下博客:http://www.2cto.com/kf/201111/109815.html

于是在监听到屏幕变暗/变亮时,通过KeyguardManager 类实现即可。对与用户而言,就相当于解除了锁屏界面了。
可能代码如下:
[java] view plaincopyprint?
//屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定

private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver(){

@Override

public void onReceive(Context context , Intent intent) {

String action = intent.getAction() ;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    Log.i(TAG, intent.toString());  

    if(action.equals("android.intent.action.SCREEN_OFF")  
            || action.equals("android.intent.action.SCREEN_ON") ){  
        mKeyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);  
        mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");   
        mKeyguardLock.disableKeyguard();  
        startActivity(zdLockIntent);  
    }  
}  

};

1
2
3
4
5
6
7
   关于参考/设计一个好的解锁界面以及仿正点闹钟滑动解锁,请看我的这篇博客:
   《 Android自定义锁屏实现----仿正点闹钟滑屏解锁》

    PS:如果觉得本文对你有帮助,请给顶一下。


   最后,可能有些同学在做App时,可能想获取系统的登录次数等,例如:登录失败次数等 ; Android系统已经 为我们提供好

了框架去处理,具体对应类是DevicePolicyManager类,关于该类的具体使用请参见该博客:
《【Android设备管理】 利用DevicePolicyManager执行屏幕锁定 》 。

1
    我也不再罗嗦了 ,大家认真学习吧 。 后面我会仔细分析下锁屏框架的一些具体处理函数 。

声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息

原文作者: 牛奶晓蔬

原文地址: http://my.eoe.cn/niunaixiaoshu/archive/15548.html

你可能感兴趣的:(Android框架浅析之锁屏(Keyguard)机制原理(转载))