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

最近终于成功的摆脱了FM收音机,迈向了新的模块:锁屏、状态栏、Launcher---姑且称之为“IDLE”小组,或许叫手机

美容小组,要是能施展下周星星同学的还我漂漂拳,岂不快哉。 OK,闲话打住,咱开始正文。

本文主要内容:

1、分析锁屏界面的组成 ;

2、基于源代码分析锁屏相关类 ;

3、提出一种在框架取消锁屏的方法 。

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

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

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

本文分析版本具体是Android2.3版本。

源文件路径主要有两个:

frameworks\base\policy\src\com\android\internal\policy\impl\ ---->锁屏框架

frameworks\base\core\java\com\android\internal\widget\ ----> 提供了一些的自定义View.

一、锁屏界面的组成


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

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

第一种界面称之为LockScreen界面(为了叙述方便,我们姑且称为“解锁界面),即我们通常所见到的界面,手机厂商一般定制

该界面。界面如下所示:

该界面对应自定义View的是LockScreen.java类

路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\LockScreen.java

第二种界面称之为UnLockScreen(为了后文叙述方便,我们姑且称为“开锁界面”),一般由Android源码提供,有如下四种:

①、图案开锁界面 ---- PatternUnlockScreen.java类 (自定义LinearLayout)

路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\PatternUnlockScreen.java

界面显示为:

②、PIN开锁界面 ---- SimUnlockScreen.java 类 (自定义LinearLayout)

路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\SimUnlockScreen.java

界面显示为: (图片省略)

③、密码开锁界面 ---- PasswordUnlockScreen.java类 (自定义LinearLayout)

路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\PasswordUnlockScreen.java

界面显示为:

④、GoogleAccount 开锁界面 ,即Google账户开锁界面。一般用于当用户输入密码错误次数超过上限值时,系统会提示

你输入Google账户去开锁。注意:开启它需要你手动设置账户与同步,否则该界面是不会出来的。

对应的源文件是: AccountUnlockScreen.java类 (自定义LinearLayout)

路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\AccountUnlockScreen.java

界面显示为:

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

显示规则

当然,这两种界面的组合也是有很多变化的,总的规则如下:

首先显示LockScreen界面,接着判断是否开启了UnLockScreen界面,如果设置了UnLockScreen界面,则进入对应的

UnLockScreen界面去解锁,才算成功解锁。但,存在一种特殊的情况,就是假如我们选择了图案 UnLockScreen界面,是不会

显示LockScreen界面,而只会显示UnLockScreen界面。

二、锁屏界面的实现


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

这些View的显示状态,锁屏界面自然也是如此。锁屏界面的实现最顶层是采用了FrameLayout去控制的,当然内部也嵌套了很

多层,内嵌层数的增多的一点好处就是我们可以分开而治,具体针对每层去做相应的更新。难处就是看代码看的很蛋疼。

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

个View树的继承层次,一个布局结构,当然,看源代码也是必须的。

关于Hierarchy Viewer的使用请参考该博客:

Android 实用工具Hierarchy Viewer实战

整个锁屏界面的继承层次如下(部分以及设置了图案开锁界面),更加完整的图请使用Hierarchy Viewer 工具查看。

上图中比较重要的几个视图说明如下:

LockPatternKeyguardView 继承至FrameLayout :作为LockScreen和UnLockScreen的载体,用来控制显示LockScreen

还是UnLockScreen界面。

LockScreen 继承至FrameLayout

PatterUnlockScreen ViewGroup类型 : 图案解锁界面

KeyguardViewHost继承至FrameLayout, 该ViewGroup作为顶层View,作为WindowManager的装饰对象添加至窗口。

它和LockPatternKeyguardView关系相当于DecorView和我们Activity内设置的资源布局一样。

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


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

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

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

1、 KeyguardScreen 类 接口

功能:该接口的主要功能是为每个需要显示的界面:LockScreen或者UnLockScreen定义了四个方法,使其在不同的状态能够

得到相应处理。优点就是: 利用设计原则的面向接口编程,减少对具体对象的依赖。

路径:\frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardScreen.java

其源代码释义如下:

[java]
view plain
copy
print
?

  1. /**
  2. * Common interface of each {@link android.view.View} that is a screen of
  3. * {@link LockPatternKeyguardView}.
  4. */
  5. public interface KeyguardScreen {
  6. /** Return true if your view needs input, so should allow the soft
  7. * keyboard to be displayed. */
  8. boolean needsInput(); //View是否需要输入数值,即该界面需要键盘输入数值
  9. /** This screen is no longer in front of the user.*/
  10. void onPause();//当该界面不处于前台界面时调用,包括处于GONE或者该界面即将被remove掉
  11. /** This screen is going to be in front of the user. */
  12. void onResume();//相对于onPause()方法,当该界面不处于前台界面时调用,处于VISIBLE状态时调用
  13. /** This view is going away; a hook to do cleanup. */
  14. void cleanUp();//该界面即将被remove掉 ,即不在需要
  15. }
/**
 * 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类 接口

功能:每个需要显示的界面:LockScreen或者UnLockScreen都保存了该对象的唯一实例,用来向控制界面汇报情况。

路径:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardScreenCallback.java

其源代码释义如下:

[java]
view plain
copy
print
?

  1. /** Within a keyguard, there may be several screens that need a callback
  2. * to the host keyguard view.
  3. */
  4. public interface KeyguardScreenCallback extends KeyguardViewCallback {
  5. /** Transition to the lock screen*/
  6. void goToLockScreen(); //当前界面跳转为LockScreen ,而不是UnLockScreen
  7. /** Transition to the unlock screen.*/
  8. void goToUnlockScreen();//LockScreen成功开锁 ,是否需要显示UnLockScreen,否则,直接开锁成功。
  9. //忘记了开锁图案,即我们需要跳转到Google 账户去开锁。
  10. void forgotPattern(boolean isForgotten);
  11. boolean isSecure();//当前机器是否安全,例如:设置了图案、密码开锁等
  12. //该函数还不太懂,可能是是否只需要验证UnlockScreen界面,即可成功开锁。
  13. boolean isVerifyUnlockOnly();
  14. /**Stay on me, but recreate me (so I can use a different layout).*/
  15. void recreateMe(Configuration config); //重新根据手机当前状态,显示对应的Screen.
  16. /** Take action to send an emergency call. */
  17. void takeEmergencyCallAction(); //紧急呼叫时的处理行为.
  18. /** Report that the user had a failed attempt to unlock with password or pattern.*/
  19. void reportFailedUnlockAttempt(); //在UnLockScreen界面登陆失败时处理
  20. /** Report that the user successfully entered their password or pattern.*/
  21. void reportSuccessfulUnlockAttempt();//在UnLockScreen界面登陆成功时处理
  22. /** Report whether we there‘s another way to unlock the device.
  23. * @return true */
  24. boolean doesFallbackUnlockScreenExist();
  25. }
/** 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();  //紧急呼叫时的处理行为.

    /** 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();
}

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

3、KeyguardViewCallback类 接口

功能: 提供了一些接口用来接受用户操作Screen的结果。

路径:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardViewCallback.java

其源代码释义如下:

[java]
view plain
copy
print
?

  1. /**
  2. * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
  3. * various things.
  4. */
  5. public interface KeyguardViewCallback {
  6. /** Request the wakelock to be poked for the default amount of time. */
  7. void pokeWakelock(); //保存屏幕在一定时间内处于亮屏状况 , 默认时间为5s或者10s
  8. /** Request the wakelock to be poked for a specific amount of time. */
  9. void pokeWakelock(int millis);//根据给定时间值,使屏幕在该事件段内保持亮屏状况
  10. /** Report that the keyguard is done.
  11. * @param authenticated Whether the user securely got past the keyguard.
  12. * the only reason for this to be false is if the keyguard was instructed
  13. * to appear temporarily to verify the user is supposed to get past the
  14. * keyguard, and the user fails to do so. */
  15. //成功的完成开锁,可以进入手机界面了。参数为ture表示是否正大光明的开锁,例如:图案正确,密码输入正确。
  16. void keyguardDone(boolean authenticated);
  17. /**Report that the keyguard is done drawing. */
  18. void keyguardDoneDrawing(); //整个锁屏界面draw()过程绘制完成时,回调该方法.
  19. }
/**
  * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
  * various things.
  */
 public interface KeyguardViewCallback {

     /** 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()过程绘制完成时,回调该方法.
 }

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

4、 KeyguardWindowController类 接口

功能:提供通用 接口,判断该界面是否需要显示输入法窗口。

其源代码释义如下:

[java]
view plain
copy
print
?

  1. /**
  2. * Interface passed to the keyguard view, for it to call up to control
  3. * its containing window.
  4. */
  5. public interface KeyguardWindowController {
  6. /** Control whether the window needs input -- that is if it has
  7. * text fields and thus should allow input method interaction. */
  8. void setNeedsInput(boolean needsInput); //是否需要显示输入法,为true表示需要。该方法可以想上层报到是否需要显示输入法窗口
  9. }
/**
 * 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类(稍后讲到)。

5、KeyguardViewManager

功能:包装了WindowManager功能了,提供了添加、删除锁屏界面的功能。

其源代码释义如下:

[java]
view plain
copy
print
?

  1. public class KeyguardViewManager implements KeyguardWindowController {
  2. ...
  3. private WindowManager.LayoutParams mWindowLayoutParams;
  4. private boolean mNeedsInput = false; //是否需要输入法 , 默认不需要
  5. private FrameLayout mKeyguardHost; //该ViewGroup作为顶层View,作为WindowManager添加至窗口
  6. private KeyguardViewBase mKeyguardView; //具体窗口内容。
  7. //以上两种的关系相当于DecorView和我们Activity内设置的资源文件一样
  8. private boolean mScreenOn = false; //是否处于亮屏状态
  9. //构造函数,初始化各种属性
  10. public KeyguardViewManager(Context context, ViewManager viewManager,
  11. KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {
  12. ...
  13. }
  14. /**
  15. * Helper class to host the keyguard view.
  16. */
  17. private static class KeyguardViewHost extends FrameLayout {
  18. ... //KeyguardViewHost类
  19. }
  20. /**
  21. * Show the keyguard. Will handle creating and attaching to the view manager
  22. * lazily.
  23. */ //显示锁屏界面
  24. public synchronized void show() {
  25. if (mKeyguardHost == null) {
  26. ...
  27. mViewManager.addView(mKeyguardHost, lp);
  28. }
  29. if (mKeyguardView == null) {
  30. ...
  31. mKeyguardHost.addView(mKeyguardView, lp);
  32. if (mScreenOn) {
  33. mKeyguardView.onScreenTurnedOn();
  34. }
  35. }
  36. ...
  37. }
  38. ...
  39. /*** Hides the keyguard view */
  40. public synchronized void hide() { //隐藏锁屏界面,也就是说我们成功的解锁了
  41. if (mKeyguardHost != null) {
  42. mKeyguardHost.setVisibility(View.GONE);
  43. ...
  44. }
  45. }
  46. //锁屏界面是否处于显示状态
  47. public synchronized boolean isShowing() {
  48. return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
  49. }
  50. }
  51. }
public class KeyguardViewManager implements KeyguardWindowController {
	 ...
     private WindowManager.LayoutParams mWindowLayoutParams;
     private boolean mNeedsInput = false; //是否需要输入法 , 默认不需要

     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类

功能:该类的主要功能就是根据监视系统状态值的改变(例如:时间、SIM卡状态、电池电量;使用广播监听),根据这种状态

值的改变回调监听了该状态信息的对象实例。

其源代码释义如下:

[java]
view plain
copy
print
?

  1. public class KeyguardUpdateMonitor {
  2. ...
  3. private int mFailedAttempts = 0; //当前登录事,已经失败的次数
  4. private ArrayList<InfoCallback> mInfoCallbacks; //保存所有监听对象 InfoCallback
  5. private ArrayList<SimStateCallback> mSimStateCallbacks ; //保存所有监听对象 SimStateCallback
  6. private static class SimArgs { //Sim状态信息
  7. ...
  8. }
  9. /**
  10. * Callback for general information relevant to lock screen.
  11. */
  12. interface InfoCallback {
  13. //电池电量信息改变:参数含义分别如下:是否显示电量信息 、 是否插入电影充电、 当前电池电量值
  14. void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel);
  15. void onTimeChanged(); //时间发生了改变
  16. //网络运营商状态发生了改变 ,例如从中国移动2G变为中国移动3G,或者无服务等 ;
  17. void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn);
  18. /** Called when the ringer mode changes. */
  19. void onRingerModeChanged(int state);
  20. /** 电话状态发生了改变 值可能为:EXTRA_STATE_IDLE、EXTRA_STATE_RINGING、EXTRA_STATE_OFFHOOK*/
  21. void onPhoneStateChanged(String newState);
  22. }
  23. /** Callback to notify of sim state change. */
  24. interface SimStateCallback {
  25. void onSimStateChanged(IccCard.State simState); //Sim卡信息发生了改变,例如有正常状况变为ABSENT/MISSING状态
  26. }
  27. /*** Register to receive notifications about general keyguard information
  28. * (see {@link InfoCallback}. */
  29. public void registerInfoCallback(InfoCallback callback) {
  30. if (!mInfoCallbacks.contains(callback)) {
  31. mInfoCallbacks.add(callback); //注册一个监听器
  32. } ...
  33. }
  34. ...
  35. }
public class KeyguardUpdateMonitor {
    ...
    private int mFailedAttempts = 0;  //当前登录事,已经失败的次数
    private ArrayList<InfoCallback> mInfoCallbacks; //保存所有监听对象 InfoCallback
    private ArrayList<SimStateCallback> 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状态
    }

    /*** Register to receive notifications about general keyguard information
     * (see {@link InfoCallback}. */
    public void registerInfoCallback(InfoCallback callback) {
        if (!mInfoCallbacks.contains(callback)) {
            mInfoCallbacks.add(callback);  //注册一个监听器
        } ...
    }
   ...
 }

7, LockPatternKeyguardView类 (自定义ViewGroup)

功能:作为LockScreen和UnLockScreen界面的载体,控制显示哪个界面。

其源代码释义如下:

[java]
view plain
copy
print
?

  1. public class LockPatternKeyguardView extends KeyguardViewBase {
  2. ...
  3. private View mLockScreen;
  4. private View mUnlockScreen;
  5. private boolean mScreenOn = false;//是否亮屏
  6. enum Mode {
  7. //当前显示界面的Mode Lock 或者UnLock
  8. }
  9. enum UnlockMode {
  10. ...//开锁界面的几种不同Mode
  11. }
  12. //构造函数
  13. public LockPatternKeyguardView( ...) {
  14. //KeyguardScreenCallback的实现对象
  15. mKeyguardScreenCallback = new KeyguardScreenCallback() {
  16. ...
  17. };
  18. ...
  19. }
  20. public void reset() {
  21. ...//重置显示界面
  22. }
  23. private void recreateLockScreen() {
  24. ...//重新构建LockScreen
  25. }
  26. private void recreateUnlockScreen() {
  27. ...//重新构建UnlockScreen
  28. }
  29. private void recreateScreens() {
  30. ...//重新构建该视图
  31. }
  32. public void verifyUnlock() {
  33. ...
  34. }
  35. public void cleanUp() {
  36. ... //清理资源对象
  37. }
  38. private boolean isSecure() {
  39. ...//手机设置是否处于安全状态
  40. }
  41. private void updateScreen(final Mode mode) {
  42. ...//根据参数(Lock/unLock),判断显示为LockScreen或者UnlockScreen界面
  43. }
  44. View createLockScreen() {
  45. ...//创建lockScreen
  46. }
  47. View createUnlockScreenFor(UnlockMode unlockMode) {
  48. ...//根据不同的Unlock Mode , 创建不同的UnlockScreen
  49. }
  50. private Mode getInitialMode() {
  51. ...//得到初始化的状态Mode (lock or unlock).
  52. }
  53. /** Given the current state of things, what should the unlock screen be? */
  54. private UnlockMode getUnlockMode() {
  55. ...//返回开锁的状态Unlock Mode
  56. }
  57. private void showTimeoutDialog() {
  58. ... //输入密码超过一定次数时,提示30s后在登录的对话框
  59. }
  60. private void showAlmostAtAccountLoginDialog() {
  61. ... //显示Google 账户登录对话框
  62. }
  63. }
public class LockPatternKeyguardView extends KeyguardViewBase {
     ...
     private View mLockScreen;
     private View mUnlockScreen;

     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 账户登录对话框
     }
 }

8、KeyguardViewBase类 抽象类 (自定义ViewGroup)

功能:为LockPatternKeyguardView提供了一组通用的方法 。需要值得注意的方法就是他对某些KeyEvent的监听,

当他消费监听到这些KeyEvent,我们的App就监听不到这些KeyEvent了 。常用的有KEYEVENT_VOLUME_UP/DOWN等。

[java]
view plain
copy
print
?

  1. public abstract class KeyguardViewBase extends FrameLayout {
  2. ...
  3. @Override
  4. public boolean dispatchKeyEvent(KeyEvent event) {
  5. ...
  6. if (interceptMediaKey(event)) {
  7. return true;
  8. }
  9. return super.dispatchKeyEvent(event);
  10. }
  11. private boolean interceptMediaKey(KeyEvent event) {
  12. final int keyCode = event.getKeyCode();
  13. if (event.getAction() == KeyEvent.ACTION_DOWN) {
  14. switch (keyCode) {
  15. ...//more keyevent
  16. case KeyEvent.KEYCODE_VOLUME_UP:
  17. case KeyEvent.KEYCODE_VOLUME_DOWN: {
  18. ...
  19. // Don‘t execute default volume behavior
  20. return true; //直接返回不在向下传递处理
  21. }
  22. }
  23. }
  24. return false;
  25. }
  26. }
public abstract class KeyguardViewBase extends FrameLayout {
	 ...
  @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        ...
        if (interceptMediaKey(event)) {
            return true;
        }
        return super.dispatchKeyEvent(event);
    }

    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 plain
copy
print
?

  1. public interface KeyguardViewProperties {
  2. //创建一个KeyguardViewBase实例 , 实际是指LockPatternKeyguardView实例
  3. KeyguardViewBase createKeyguardView(Context context,
  4. KeyguardUpdateMonitor updateMonitor,
  5. KeyguardWindowController controller);
  6. boolean isSecure();
  7. }
public interface KeyguardViewProperties {
	    //创建一个KeyguardViewBase实例 , 实际是指LockPatternKeyguardView实例
	    KeyguardViewBase createKeyguardView(Context context,
	            KeyguardUpdateMonitor updateMonitor,
	            KeyguardWindowController controller);

	    boolean isSecure();
	}

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

10、LockPatternKeyguardViewProperties

源代码释义如下:

[java]
view plain
copy
print
?

  1. public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
  2. ...
  3. //创建一个LockPatternKeyguardView对象
  4. public KeyguardViewBase createKeyguardView(Context context,
  5. KeyguardUpdateMonitor updateMonitor,
  6. KeyguardWindowController controller) {
  7. return new LockPatternKeyguardView(context, updateMonitor,
  8. mLockPatternUtils, controller);
  9. }
public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
    ...
    //创建一个LockPatternKeyguardView对象
    public KeyguardViewBase createKeyguardView(Context context,
            KeyguardUpdateMonitor updateMonitor,
            KeyguardWindowController controller) {
        return new LockPatternKeyguardView(context, updateMonitor,
                mLockPatternUtils, controller);
    }
}

//=============================================

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

//=============================================

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

12、KeyguardViewMediator核心类 ,该类是唯一实现了KeyguardViewCallback的类。

功能: 功能:该类提供了一些接口,由PhoneWindowManager)去访问控制Keyguard....

该类的初始化是在PolicyWindowManager的构造函数中创建的。如下:

[java]
view plain
copy
print
?

  1. public class PhoneWindowManager implements WindowManagerPolicy {
  2. ...
  3. /** {@inheritDoc} */ //由SystemServer调用
  4. public void init(Context context, IWindowManager windowManager,
  5. LocalPowerManager powerManager) {
  6. ...//初始化该实例
  7. mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
  8. }
  9. }
public class PhoneWindowManager implements WindowManagerPolicy {
	   ...
	  /** {@inheritDoc} */  //由SystemServer调用
	  public void init(Context context, IWindowManager windowManager,
	          LocalPowerManager powerManager) {
	      ...//初始化该实例
	      mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
	  }
  }

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

[java]
view plain
copy
print
?

  1. public class KeyguardViewMediator implements KeyguardViewCallback, KeyguardUpdateMonitor.SimStateCallback {
  2. private boolean mSystemReady; //启动成功 由SystemServer调用
  3. /**Used to keep the device awake while to ensure the keyguard finishes opening before
  4. * we sleep.*/ //在需要显示锁屏界面时,保持屏幕在某个时间段内为暗屏状态
  5. private PowerManager.WakeLock mShowKeyguardWakeLock;
  6. private KeyguardViewManager mKeyguardViewManager; //KeyguardViewManager实例
  7. /** * External apps (like the phone app) can tell us to disable the keygaurd.*/
  8. //是否允许其他App禁止锁屏 , 例如来电时 禁止锁屏
  9. private boolean mExternallyEnabled = true;
  10. //处于锁屏状态 , 即显示锁屏
  11. private boolean mShowing = false;
  12. // true if the keyguard is hidden by another window
  13. private boolean mHidden = false; //被其他窗口掩盖 , 例如来电时锁屏被掩盖
  14. private boolean mScreenOn = false; // 是否亮屏
  15. public KeyguardViewMediator(Context context, PhoneWindowManager callback,
  16. LocalPowerManager powerManager) {
  17. ...
  18. //构造相关实例对象
  19. mKeyguardViewProperties = new LockPatternKeyguardViewProperties(
  20. new LockPatternUtils(mContext), mUpdateMonitor);
  21. mKeyguardViewManager = new KeyguardViewManager(
  22. context, WindowManagerImpl.getDefault(), this,
  23. mKeyguardViewProperties, mUpdateMonitor);
  24. //解锁成功后发送的Intent
  25. mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);
  26. mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
  27. }
  28. /** Called to let us know the screen was turned off.
  29. * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
  30. * {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
  31. * {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
  32. */
  33. //屏幕变灰暗 , 原因有如下:以及对应的逻辑处理。
  34. // 1、OFF_BECAUSE_OF_USER : 用户按下POWER键 , 当前是否处于锁屏界面,若是(mShowing)则重置显示界面,否则重新显示锁屏界面
  35. // 2、OFF_BECAUSE_OF_TIMEOUT : 屏幕超时,常见情况就是一段时间没有操作屏幕,手机处于灰暗状态。 处理行为:
  36. // 发送Action值为DELAYED_KEYGUARD_ACTION的广播,因为该类注册了该Intent广播,接受到时会调用doKeyguard()方法锁屏
  37. // 3、OFF_BECAUSE_OF_PROX_SENSOR:接打电话时,距离感应太近导致暗屏,此时由于PowerManager那儿已经处理了暗屏,不需要做任何事
  38. // 最后,如果以上逻辑都不成立,调用 doKeyguard()方法显示屏幕
  39. public void onScreenTurnedOff(int why) {
  40. ...
  41. }
  42. /**
  43. * Let‘s us know the screen was turned on.
  44. */
  45. public void onScreenTurnedOn() {
  46. synchronized (this) {
  47. ...
  48. notifyScreenOnLocked(); //通知亮屏
  49. }
  50. }
  51. /** Enable the keyguard if the settings are appropriate. */
  52. private void doKeyguard() {
  53. synchronized (this) {
  54. ...
  55. showLocked();//显示锁屏界面
  56. }
  57. }
  58. //该方法的调用时机就是当按下POWER键时,系统会回调该方法 keyCode值一般为 26
  59. public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {
  60. //操作按键是否能唤醒屏幕
  61. if (isWakeKeyWhenKeyguardShowing(keyCode)) {
  62. // give the keyguard view manager a chance to adjust the state of the
  63. // keyguard based on the key that woke the device before poking
  64. // the wake lock
  65. wakeWhenReadyLocked(keyCode);//开始唤醒屏幕咯
  66. return true;
  67. } else {
  68. return false;
  69. }
  70. }
  71. /** {@inheritDoc} */ //在一定时间内保存屏幕为亮屏状态
  72. public void pokeWakelock(int holdMs) {
  73. ...
  74. }
  75. //表示成功得分完成了解锁操作
  76. public void keyguardDone(boolean authenticated, boolean wakeup) {
  77. synchronized (this) {
  78. //发送给Handler 进行异步处理
  79. Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
  80. msg.arg1 = wakeup ? 1 : 0;
  81. mHandler.sendMessage(msg);
  82. if (authenticated) {
  83. mUpdateMonitor.clearFailedAttempts(); //清除错误登录次数
  84. }
  85. ...
  86. }
  87. }
  88. //Handler对象 , 异步处理
  89. private Handler mHandler = new Handler() {
  90. @Override
  91. public void handleMessage(Message msg) {
  92. switch (msg.what) {
  93. ... //异步处理
  94. }
  95. }
  96. };
  97. //异步处理完成开锁成功
  98. private void handleKeyguardDone(boolean wakeup) {
  99. handleHide(); //释放该Keyguard对应的窗口
  100. mWakeLock.release();
  101. mContext.sendBroadcast(mUserPresentIntent); //解锁成功,发送Intent信息
  102. }
  103. //显示锁屏界面
  104. private void handleShow() {
  105. synchronized (KeyguardViewMediator.this) {
  106. ...
  107. mKeyguardViewManager.show();
  108. mShowing = true;
  109. ...
  110. }
  111. }
  112. private void handleHide() {
  113. synchronized (KeyguardViewMediator.this) {
  114. //去除锁屏界面对应的窗口
  115. mKeyguardViewManager.hide();
  116. mShowing = false;
  117. ...
  118. }
  119. }
  120. //设置状态栏enable状态 , 例如:能否被下拉等
  121. private void adjustStatusBarLocked() {
  122. ...
  123. // if the keyguard is shown, allow the status bar to open
  124. // only if the keyguard is insecure and is covered by another window
  125. boolean enable = !mShowing || (mHidden && !isSecure());
  126. mStatusBarManager.disable(enable ?StatusBarManager.DISABLE_NONE : StatusBarManager.DISABLE_EXPAND);
  127. }
  128. }
  129. }
public class KeyguardViewMediator implements KeyguardViewCallback, KeyguardUpdateMonitor.SimStateCallback {
    private boolean mSystemReady;  //启动成功 由SystemServer调用

    /**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);
        }
    }
}

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

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

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

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

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

随着看的越来越深入,慢慢的也在心里长生了涟漪。经过尝试、烧鸡验证,发现如下办法行的通,而且效果还比较好。风险应该

比较小吧。 希望正在在框架修改的同学,慎重行事。

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

方法去给系统调用,让系统觉得我们“锁屏”了,同样也不去真正的隐藏“锁屏”界面,提供一个空壳给系统调用。由于可能涉及

到其它问题,例如:能否下拉状态栏,按下POWER键后,屏幕很快休眠等。Come on ,我们需要统一做处理。

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

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

[java]
view plain
copy
print
?

  1. //该方法会显示锁屏界面,我们使其成为一个空壳子
  2. private void handleShow() {
  3. synchronized (KeyguardViewMediator.this) {
  4. if (DEBUG) Log.d(TAG, "handleShow");
  5. if (!mSystemReady) return;
  6. playSounds(true);
  7. //Begin : Modifid by qinjuning
  8. //mKeyguardViewManager.show(); //
  9. //mShowing = true; //
  10. //adjustUserActivityLocked(); //
  11. //adjustStatusBarLocked(); //取消对状态栏的控制
  12. //End
  13. try {
  14. ActivityManagerNative.getDefault().closeSystemDialogs("lock");
  15. } catch (RemoteException e) {
  16. }
  17. mShowKeyguardWakeLock.release();
  18. }
  19. }
//该方法会显示锁屏界面,我们使其成为一个空壳子
private void handleShow() {
    synchronized (KeyguardViewMediator.this) {
        if (DEBUG) Log.d(TAG, "handleShow");
        if (!mSystemReady) return;

        playSounds(true);
        //Begin : Modifid by qinjuning     

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

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

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

[java]
view plain
copy
print
?

  1. //真正的隐藏屏幕实现
  2. private void handleHide() {
  3. synchronized (KeyguardViewMediator.this) {
  4. if (DEBUG) Log.d(TAG, "handleHide");
  5. if (mWakeAndHandOff.isHeld()) {
  6. Log.w(TAG, "attempt to hide the keyguard while waking, ignored");
  7. return;
  8. }
  9. // only play "unlock" noises if not on a call (since the incall UI
  10. // disables the keyguard)
  11. if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
  12. playSounds(false);
  13. }
  14. //Begin : Modifid by qinjuning
  15. //mKeyguardViewManager.hide();
  16. //mShowing = false;
  17. //adjustUserActivityLocked();
  18. //adjustStatusBarLocked(); //取消对状态栏的控制
  19. //End
  20. }
  21. }
//真正的隐藏屏幕实现
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  

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

        //End
    }
}

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

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

[java]
view plain
copy
print
?

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

        // 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");
        }
    }
}

经过真机测试是通过的,但其他风险并不清楚。 这个方法只是提供了一个学习的途径吧。大家慎重行事。

上面Step 1、以及Step 2可以由如下方法代替:

将属性mExternallyEnabled 设置为 false, 接下来需要显示界面时都不会继续走下去,如下函数:

[java]
view plain
copy
print
?

  1. /**
  2. * Enable the keyguard if the settings are appropriate.
  3. */ //显示界面
  4. private void doKeyguard() {
  5. synchronized (this) {
  6. // if another app is disabling us, don‘t show
  7. if (!mExternallyEnabled) { //mExternallyEnabled 为false
  8. if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
  9. // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
  10. // for an occasional ugly flicker in this situation:
  11. // 1) receive a call with the screen on (no keyguard) or make a call
  12. // 2) screen times out
  13. // 3) user hits key to turn screen back on
  14. // instead, we reenable the keyguard when we know the screen is off and the call
  15. // ends (see the broadcast receiver below)
  16. // TODO: clean this up when we have better support at the window manager level
  17. // for apps that wish to be on top of the keyguard
  18. return;
  19. }
  20. ...
  21. }
  22. }
/**
 * 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");

            // 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;
        }
        ...
    }
}

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

出现,因为存在另一个判断依据:变量mNeedToReshowWhenReenabled , 其初始值为false,只有成功禁止锁屏之后才置为

true )。 因此,我们可以仿照这个方法,主动添加一个私有变量,禁止显示锁屏界面,即禁止doKeyguard()方法继续走下去。

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

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

封装好了。


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

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

KeyguardManager简介 》

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

和显示锁屏界面。

KeyguardManager.KeyguardLock的两个方法说明如下:

public void disableKeyguard ()

功能:取消锁屏界面显示,同时禁止显示锁屏界面。除非你显示调用了reenableKeyguard()方法使能显示锁屏界面。

public void reenableKeyguard ()

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

这两个方法最终都会调用到KeyguardViewMediator类的setKeyguardEnabled(boolean enable)方法。

参数说明: enable = false 对应于disableKeyguard()方法,

enable = true 对应于reenableKeyguard()方法。

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

[java]
view plain
copy
print
?

  1. /**
  2. * Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide
  3. * a way for external stuff to override normal keyguard behavior. For instance
  4. * the phone app disables the keyguard when it receives incoming calls.
  5. */
  6. public void setKeyguardEnabled(boolean enabled) {
  7. synchronized (this) {
  8. mExternallyEnabled = enabled; //保存值,该值会在doKeyguard()时用到,如果为false ,则不进行锁屏
  9. if (!enabled && mShowing) {
  10. if (mExitSecureCallback != null) {//该判断为false
  11. ...
  12. return ;
  13. }
  14. mNeedToReshowWhenReenabled = true; //置为真,以便下次调用
  15. hideLocked(); //已经显示了锁屏界面,则取消隐藏界面
  16. } else if (enabled && mNeedToReshowWhenReenabled) { //重新显示锁屏界面
  17. mNeedToReshowWhenReenabled = false;
  18. if (mExitSecureCallback != null) {//该判断为false
  19. } else {
  20. showLocked(); //显示隐藏界面
  21. ...
  22. }
  23. }
  24. }
  25. }
/**
 * 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 ,则不进行锁屏

        if (!enabled && mShowing) {
            if (mExitSecureCallback != null) {//该判断为false
                ...
                return ;
            }
            mNeedToReshowWhenReenabled = true;  //置为真,以便下次调用
            hideLocked(); //已经显示了锁屏界面,则取消隐藏界面
        } else if (enabled && mNeedToReshowWhenReenabled) { //重新显示锁屏界面
            mNeedToReshowWhenReenabled = false;

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

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

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

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

屏幕变暗以及屏幕点亮的广播

android.intent.action.SCREEN_ON --- 屏幕变亮

android.intent.action.SCREEN_OFF ---- 屏幕点暗


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

于是在监听到屏幕变暗/变亮时,通过KeyguardManager 类实现即可。对与用户而言,就相当于解除了锁屏界面了。

可能代码如下:

[java]
view plain
copy
print
?

  1. //屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定
  2. private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver(){
  3. @Override
  4. public void onReceive(Context context , Intent intent) {
  5. String action = intent.getAction() ;
  6. Log.i(TAG, intent.toString());
  7. if(action.equals("android.intent.action.SCREEN_OFF")
  8. || action.equals("android.intent.action.SCREEN_ON") ){
  9. mKeyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
  10. mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");
  11. mKeyguardLock.disableKeyguard();
  12. startActivity(zdLockIntent);
  13. }
  14. }
  15. };
//屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定
	private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver(){
		@Override
		public void onReceive(Context context , Intent intent) {
			String action = intent.getAction() ;

		    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);
			}
		}

	};

关于参考/设计一个好的解锁界面以及仿正点闹钟滑动解锁,请看我的这篇博客:

《 Android自定义锁屏实现----仿正点闹钟滑屏解锁

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

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

了框架去处理,具体对应类是DevicePolicyManager类,关于该类的具体使用请参见该博客:

《【Android设备管理】 利用DevicePolicyManager执行屏幕锁定 》 。

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

时间: 2024-10-07 20:51:45

Android框架浅析之锁屏(Keyguard)机制原理的相关文章

QQ音乐/酷狗音乐锁屏控制实现原理

我实现的效果 混乱的锁屏控制 Android自4.0版本, 也就是API level 14开始, 加入了锁屏控制的功能, 相关的类是RemoteControlClient, 这个类在API level 21中被标记为deprecated, 被新的类MediaSession所替代. 我们的音乐App中最开始使用的是原生锁屏控制API, 说实话这个API不好用, 遇到了一些小坑, 最要命的是不同品牌的手机, 锁屏界面长的还不一样, 就连我自己都没见过原生4.0的锁屏控制界面是什么样的. 国内的手机厂

Android 7.1.1 锁屏界面启动流程

前几天遇到一个低概率复现锁屏界面不显示,仅仅显示状态栏的问题,跟了下锁屏界面启动显示的流程,在这分享下,也方便以后自己查看.前面简介了下Zygote启动流程, Zygote进程启动后会首先创建一个SystemServer进程,SystemServer进程在调用startOtherServices同一时候也会调用WindowManagerService的systemReady()方法 //frameworks/base/services/java/com/android/server/System

【Android】如何实现Android程序在手机锁屏后继续运行

最近笔者在做一个功能,就是实现Android程序在锁屏后可以继续运行,笔者在网上查了一些资料,现在整理出来,希望能够对你有所帮助. 1.如何监听屏幕锁屏 监听屏幕锁屏可以通过如下方式来实现,直接通过代码来判定,或通过监听器来实现 1)通过代码来判定屏幕的锁屏状态 可以通过PowerManager的isScreenOn方法,代码如下: PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);

Android开发之实现锁屏功能

锁屏需要引入设备超级管理员.在文档Android开发文档的Administration中有详细的说明.Android设备管理系统功能和控制访问. 主要有一下几个步骤: 1  创建广播接收者,实现DeviceAdminReceiver package com.andy.lockscreen; import android.app.admin.DeviceAdminReceiver; /** * @author Zhang,Tianyou * @version 2014年11月20日 下午9:51:

Android wiki : 18.DevicePolicyManager锁屏和清除数据

DevicePolicyManager 类:设备策略管理器   安全设备 Android 2.2 SDK提供了一个可管理和操作设备的API叫DevicePolicyManager, 使用这个API你可以接管手机的应用权限,对手机做出很多大胆的操 作, 比如锁屏.恢复出厂设置.设置密码.强制清除密码,修改密码.设置屏幕灯光渐暗时间间隔等操作. 这个API让你的程序直接掌握了系统的命脉. 但是,同样的道理,别人也可以做类似的程序,各程序之间的权限是不起冲突的~ 首先,要想调用DevicePolicy

android 退出,不锁屏

Cocos2dxGLSurfaceView.java public boolean onKeyDown(final int pKeyCode, final KeyEvent pKeyEvent) { switch (pKeyCode) { case KeyEvent.KEYCODE_BACK: Cocos2dxActivity.clossGame(); return true; case KeyEvent.KEYCODE_MENU: this.queueEvent(new Runnable()

【腾讯Bugly干货分享】浅谈Android自定义锁屏页的发车姿势

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57875330c9da73584b025873 一.为什么需要自定义锁屏页 锁屏作为一种黑白屏时代就存在的手机功能,至今仍发挥着巨大作用,特别是触屏时代的到来,锁屏的功用被发挥到了极致.多少人曾经在无聊的时候每隔几分钟划开锁屏再关上,孜孜不倦,其酸爽程度不亚于捏气泡膜.确实,一款漂亮的锁屏能为手机增色不少,但锁屏存在的核心目的主要是三个:保护自己手机的隐私,防止误操作,在不关闭

Android中锁屏密码算法解析以及破解方案

一.前言 最近玩王者荣耀,下载了一个辅助样本,结果被锁机了,当然破解它很简单,这个后面会详细分析这个样本,但是因为这个样本引发出的欲望就是解析Android中锁屏密码算法,然后用一种高效的方式制作锁机恶意样本.现在的锁机样本原理强制性太过于复杂,没意义.所以本文就先来介绍一下android中的锁屏密码算法原理. 二.锁屏密码方式 我们知道Android中现结单支持的锁屏密码主要有两种: 一种是手势密码,也就是我们常见的九宫格密码图 一种是输入密码,这个也分为PIN密码和复杂字符密码,而PIN密码

Android 禁止屏幕休眠和锁屏的方法

Introduction 常常我们开 发程序的时候我们不需要系统唤醒系统锁屏功能,比如我们在做xxxNowTV或XXX播放器这样的程序,用户有时候在看电视或视频的时候不希望系统的锁屏 功能启动,既不想锁频,然而系统却在我们看电视或者视频的时候出来个锁屏的界面进行锁频拉,我们还要想继续看的话还要去解锁,这样好麻烦,不是我们想要 的,那我们该怎么做呢,其实很简单,我这里只讲其中的两种 一 :我们只要在程序中用代码实现.代码如下: [java] view plaincopy //方法一 getWind