天天看點

Android鎖屏實踐+保活寫在前面開始保活篇尾聲

本菜開源的一個自己寫的Demo,希望能給Androider們有所幫助,水準有限,見諒見諒… https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的優秀架構于一體,全部拆離不含任何額外的庫導入) https://github.com/zhiaixinyang/MyFirstApp (Retrofit+RxJava+MVP)

寫在前面

剛剛入職的時候,因為我在面試的時候去Service掌握并不好,組長就根據我所要加入的音樂組的項目給我講了音樂背景Service相關的内容,并且以鎖屏為例講到了AIDL相關的内容。

因為AIDL涉及到不少的内容,是以,以我現在所掌握的了解的深度并不能夠把這個東西梳理清楚。

是以這次的學習記錄是針對鎖屏這一子產品。技術組本來要把鎖屏子產品重新構架一番,抽成公共元件,但是最近這幾周由于要上線新的産品,是以就被放下了,不過不影響了解鎖屏的原理,抽離隻是思想上的東西,本質不會變。而且還可以在被抽離出來之後,再進行學習記錄封裝的思想,倆不耽誤。

開始

思路

整體的思路還是比較清晰的:首先寫一個用于展示鎖屏内容的Activity,然後監聽螢幕熄滅和亮起的事件,接到事件後start我們的鎖屏Activity。當然裡邊還有一些體驗上的細節要處理,比如:既然要讓我們的鎖屏顯示,那麼就要把系統的鎖屏關閉。再比如:如果有密碼鎖,我們是否需要讓鎖屏先于密碼鎖展示。等等。

監聽螢幕狀态

這個很簡單,我們隻需要寫一個BroadcastReceiver,除了onReceive()方法,比如下面的方式:

@Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null) {
            return;
        }
        //清掉系統鎖屏(還有其他方式)
        KeyguardManager keyguardManager = (KeyguardManager) context
                    .getSystemService(Context.KEYGUARD_SERVICE);
        KeyguardManager.KeyguardLock keyguardLock= keyguardManager 
                    .newKeyguardLock(context.getPackageName());
        keyguardManager.disableKeyguard();    
        //接收螢幕事件      
        if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
            LockScreenActivity.start(context);
        } else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
            LockScreenActivity.start(context);
        }
    }

           

我們startActivity的時候,一般要把這個鎖屏Activity放到一個全新的task中,以及一些Flags。

android:taskAffinity="xxxxxx"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

           

Tips:

監聽螢幕的廣播,必須要動态注冊。是以我們正常要啟動一個服務去動态的registerReceiver()。既然提到服務,必然會涉及到保活,關于保活的問題将會在保活篇簡單記錄部分代碼。

//注冊廣播接受者
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(執行個體化自己的接受者, filter);

           

最上層顯示Activity

可以在密碼鎖上面顯示,當然肯定是不可能繞過密碼鎖的。

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //添加<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>  權限
        //去掉系統鎖屏頁
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
        //使Activity在鎖屏時仍然能夠顯示
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
        setContentView(R.layout.activity_lock_screen);

    }

           

屏蔽實體鍵

屏蔽傳回鍵,菜單鍵等操作。

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        int key = event.getKeyCode();
        switch (key) {
            case KeyEvent.KEYCODE_BACK: {
                return true;
            }
            case KeyEvent.KEYCODE_MENU:{
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }


           

當然我們的鎖屏Activity肯定是需要滑動消失。

一般是自定義一個滑動控件,

可以參考我的GitHub鎖屏篇

保活篇

思想

公司一直使用的方案是:在正常啟動用于動态注冊鎖屏廣播的的Service上再開啟一個另一個Service,但是這個Service運作在其他程序中。并且這倆個Service在

onServiceDisconnected()

以及

onDestroy()

方法中進行互相的start。

具體代碼

本程序的Service:

public class LocalCoreService extends Service {
    //省略AIDL部分
    private ServiceConnection mConnection;
    private Handler mHandler;

    public static void starter(@NonNull Context context) {
        Intent intent = new Intent(context, LocalCoreService.class);
        context.startService(intent);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //省略AIDL部分
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mConnection = new LocalConnection();
        mHandler = new Handler();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        bindRemoteService();
        KeyguardService.starter(this);
        return START_STICKY;
    }

    protected class LocalConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    RemoteCoreService.starter(LocalCoreService.this);
                }
            }, 1000);
        }
    }

    private void restartKeyguardService() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                KeyguardService.starter(LocalCoreService.this);
            }
        }, 1000);
    }

    private void bindRemoteService() {
        bindService(new Intent(LocalCoreService.this, RemoteCoreService.class), mConnection, Context.BIND_IMPORTANT);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //解除對遠端service的綁定
        unbindService(mConnection);
        RemoteCoreService.starter(this);
    }
}

           

另程序Service:

public class RemoteCoreService extends Service {
    //省略AIDL操作部分
    private ServiceConnection mConnections;
    private Handler mHandler;

    public static void starter(@NonNull Context context) {
        Intent intent = new Intent(context, RemoteCoreService.class);
        context.startService(intent);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //省略AIDL操作部分
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBinder = new RemoteBinder();
        mConnections = new Connections();
        mHandler = new Handler();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LocalCoreService.starter(RemoteCoreService.this);
        bindLocalService();
        KeyguardService.starter(this);
        return START_STICKY;
    }

    /**
     * 1s 後重新開機鎖屏service
     */
    private void restartKeyguardService() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                KeyguardService.starter(RemoteCoreService.this);
            }
        }, 1000);
    }

    private class Connections implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    LocalCoreService.starter(RemoteCoreService.this);
                }
            }, 1000);
        }
    }

    protected void bindLocalService() {
        bindService(new Intent(this, LocalCoreService.class), mConnections, Context.BIND_IMPORTANT);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //解綁之前綁定的服務
        unbindService(mConnections);
        //通知local service 開啟
        LocalCoreService.starter(this);
    }
}
           

尾聲

以上的操作也僅僅是盡可能的保證Service存活,保證鎖屏能夠出來,但是國内手機商各種定制很難保證這些效果能夠順利的出來。

就行在我的手機上,網易雲音樂的鎖屏就沒有我們App的穩定性高;但反過來在其他品牌的手機上或許是另一種情況。這種事誰又能說得準呢?

最後希望各位看官可以star我的GitHub,三叩九拜,滿地打滾求star:

2018年7月2号,我正式開始了自己的Android工作,為了能夠讓自己能夠好好完成工作,并且能夠快速得到技術提升。是以打算以公衆号的方式去敦促自己學習,我會把自己日常的學習筆記釋出到公衆号上,如果可以,共同進步!~
Android鎖屏實踐+保活寫在前面開始保活篇尾聲
個人公衆号

繼續閱讀