天天看點

android 手機美化

 最近終于成功的擺脫了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界面(為了叙述友善,我們姑且稱為“解鎖界面),即我們通常所見到的界面,手機廠商一般定制

          該界面。界面如下所示:

android 手機美化

          該界面對應自定義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

                      界面顯示為:

android 手機美化

             ②、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

android 手機美化

             ④、GoogleAccount 開鎖界面 ,即Google賬戶開鎖界面。一般用于當使用者輸入密碼錯誤次數超過上限值時,系統會提示

       你輸入Google賬戶去開鎖。注意:開啟它需要你手動設定賬戶與同步,否則該界面是不會出來的。

                    對應的源檔案是: AccountUnlockScreen.java類    (自定義LinearLayout)

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

                    界面顯示為:

android 手機美化

    可以按照如下辦法選擇開啟哪一種開鎖界面: 設定—>位置和安全—>設定螢幕鎖定  ,具體選擇那種開鎖界面。

    顯示規則

         當然,這兩種界面的組合也是有很多變化的,總的規則如下:

            首先顯示LockScreen界面,接着判斷是否開啟了UnLockScreen界面,如果設定了UnLockScreen界面,則進入對應的

    UnLockScreen界面去解鎖,才算成功解鎖。但,存在一種特殊的情況,就是假如我們選擇了圖案  UnLockScreen界面,是不會

    顯示LockScreen界面,而隻會顯示UnLockScreen界面。

   二、鎖屏界面的實作

       我們知道, 任何一種界面都是由各種View/ViewGroup(當然包括自定義的)組成的,然後根據系統對應的狀态值的改變去更新

  這些View的顯示狀态,鎖屏界面自然也是如此。鎖屏界面的實作最頂層是采用了FrameLayout去控制的,當然内部也嵌套了很

  多層,内嵌層數的增多的一點好處就是我們可以分開而治,具體針對每層去做相應的更新。難處就是看代碼看的很蛋疼。

       當界面複雜時,我不得不提Google為開發人員提供的一款優秀工具了---Hierarchy Viewer ,通過它,我們很清晰的弄明白整

  個View樹的繼承層次,一個布局結構,當然,看源代碼也是必須的。

       關于Hierarchy Viewer的使用請參考該部落格:

       整個鎖屏界面的繼承層次如下(部分以及設定了圖案開鎖界面),更加完整的圖請使用Hierarchy Viewer 工具檢視。

android 手機美化

      上圖中比較重要的幾個視圖說明如下:

       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

     其源代碼釋義如下:

/** 

 * 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

       其源代碼釋義如下:

/** 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

         其源代碼釋義如下:

  * 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類 接口

         功能:提供通用 接口,判斷該界面是否需要顯示輸入法視窗。

 * 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功能了,提供了添加、删除鎖屏界面的功能。

          其源代碼釋義如下:     

 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卡狀态、電池電量;使用廣播監聽),根據這種狀态

           值的改變回調監聽了該狀态資訊的對象執行個體。

       其源代碼釋義如下:     

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界面的載體,控制顯示哪個界面。

             其源代碼釋義如下:     

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等。

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 接口

                 功能:提供了建立界面的通用方法。

  public interface KeyguardViewProperties {    

    //建立一個KeyguardViewBase執行個體 , 實際是指LockPatternKeyguardView執行個體  

    KeyguardViewBase createKeyguardView(Context context,  

            KeyguardUpdateMonitor updateMonitor,  

            KeyguardWindowController controller);  

    boolean isSecure();  

          其唯一實作類是是LockPatternKeyguardViewProperties類(稍後講到)。

  10、LockPatternKeyguardViewProperties類 

                源代碼釋義如下:

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的構造函數中建立的。如下:

public class PhoneWindowManager implements WindowManagerPolicy {  

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

  public void init(Context context, IWindowManager windowManager,  

          LocalPowerManager powerManager) {  

      ...//初始化該執行個體  

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

  }  

       參照源代碼,把一些重要的屬性和方法的大意給分析下:

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() {  

            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);//開始喚醒螢幕咯  

        } else {  

            return false;  

    /** {@inheritDoc} */  //在一定時間内儲存螢幕為亮屏狀态  

    public void pokeWakelock(int holdMs) {  

    //表示成功得分完成了解鎖操作  

    public void keyguardDone(boolean authenticated, boolean wakeup) {  

            //發送給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() {  

            //去除鎖屏界面對應的視窗  

            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、  取消  真正的去鎖屏實作

//該方法會顯示鎖屏界面,我們使其成為一個空殼子  

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、  取消  真正的去隐藏鎖屏實作       

//真正的隐藏螢幕實作  

private void handleHide() {  

        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();         //取消對狀态欄的控制  

    以上兩步行動後,存在一個Bug(問題),就是喚醒螢幕後,會在指定的時間内螢幕由亮變暗,我們還需要做如下修改

     Step 3、按下POWER鍵時, 解除螢幕由亮變暗的Bug

private void handleWakeWhenReady(int keyCode) {  

        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, 接下來需要顯示界面時都不會繼續走下去,如下函數:

 * 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  

      該方法的一個缺點就是,假如存在重新調用了setKeyguardEnabled()設定該值,一切都是白搭( 但從源碼看,這點不可能

 出現,因為存在另一個判斷依據:變量mNeedToReshowWhenReenabled , 其初始值為false,隻有成功禁止鎖屏之後才置為

 true )。 是以,我們可以仿照這個方法,主動添加一個私有變量,禁止顯示鎖屏界面,即禁止doKeyguard()方法繼續走下去。

     OK ,本文到此為止。講的比較抽象。 大家看代碼時多加了解才是王道 。

     顯然易見,手機廠商,基于架構隻需要修改LockScreen這個自定義ViewGroup即可,其他的一套Google已經為我們

   封裝好了。

      在架構層修改肯定不是最好的,對于第三方的App而言,實作不了該功能。還好,SDK為我們提供了接口類去處理隐藏鎖屏接口

的方法,該類是KeyguardManager類,關于該類的簡介請參考該部落格:

     我們可以通過KeyguardManager類執行個體獲得一個KeyguardManager.KeyguardLock對象,進而調用相應方法去取消鎖屏界面

 和顯示鎖屏界面。

    KeyguardManager.KeyguardLock的兩個方法說明如下:

          public void disableKeyguard ()

            功能:取消鎖屏界面顯示,同時禁止顯示鎖屏界面。除非你顯示調用了reenableKeyguard()方法使能顯示鎖屏界面。

            功能: 使能顯示鎖屏界面,如果你之前調用了disableKeyguard()方法取消鎖屏界面,那麼會馬上顯示鎖屏界面。 

     這兩個方法最終都會調用到KeyguardViewMediator類的setKeyguardEnabled(boolean enable)方法。

            參數說明: enable = false  對應于disableKeyguard()方法,

                             enable = true  對應于reenableKeyguard()方法。

          該方法原型為: 位于KeyguardViewMediator類中

 * 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) {  

        mExternallyEnabled = enabled;  //儲存值,該值會在doKeyguard()時用到,如果為false ,則不進行鎖屏  

        if (!enabled && mShowing) {  

            if (mExitSecureCallback != null) {//該判斷為false  

                ...   

                return ;  

            mNeedToReshowWhenReenabled = true;  //置為真,以便下次調用  

            hideLocked(); //已經顯示了鎖屏界面,則取消隐藏界面  

        } else if (enabled && mNeedToReshowWhenReenabled) { //重新顯示鎖屏界面  

            mNeedToReshowWhenReenabled = false;  

            } else {  

                showLocked(); //顯示隐藏界面  

                ...  

       使用這兩個方法時,記得加上如下權限:android.permission.DISABLE_KEYGUARD

   為了在亮屏時,達到取消顯示界面的效果,我們還需要知道 一下兩個廣播:

         螢幕變暗以及螢幕點亮的廣播

                    android.intent.action.SCREEN_ON  --- 螢幕變亮

                    android.intent.action.SCREEN_OFF ---- 螢幕點暗

  于是在監聽到螢幕變暗/變亮時,通過KeyguardManager 類實作即可。對與使用者而言,就相當于解除了鎖屏界面了。

   可能代碼如下:

//螢幕變暗/變亮的廣播 , 我們要調用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);  

};  

       關于參考/設計一個好的解鎖界面以及仿正點鬧鐘滑動解鎖,請看我的這篇部落格:

        PS:如果覺得本文對你有幫助,請給頂一下。

       最後,可能有些同學在做App時,可能想擷取系統的登入次數等,例如:登入失敗次數等 ; Android系統已經 為我們提供好

   了架構去處理,具體對應類是DevicePolicyManager類,關于該類的具體使用請參見該部落格:

        我也不再羅嗦了 ,大家認真學習吧 。 後面我會仔細分析下鎖屏架構的一些具體處理函數 。

繼續閱讀