說到進(jìn)程保活毙驯,忍不住吐槽一下市場上除了微信這樣的白名單大佬倒堕,其他的應(yīng)用很難在后臺(tái)保持長鏈接,北郏活只能是使用一些歪門邪道來延長進(jìn)程的持續(xù)時(shí)間垦巴。總的來說也就這兩種方法:
(1)提供進(jìn)程優(yōu)先級(jí)铭段,降低進(jìn)程被殺死的概率骤宣;(2)在進(jìn)程被殺死后,進(jìn)行拉活.
今天就先來說降低被殺死的概率方法:一像素Activity序愚,雙進(jìn)程守護(hù)Service憔披。
1像素Activity
1像素Activity的特點(diǎn): 需要設(shè)置該activity的style設(shè)置透明,在手機(jī)鎖屏?xí)rstart爸吮;在屏幕解鎖時(shí)finish芬膝,主要作用就是在App退到后臺(tái)之后且鎖屏的時(shí)候啟動(dòng)一個(gè)看不見的Activity,造成一個(gè)該App沒有回退到后臺(tái)的假象形娇,從而降低被殺的幾率锰霜。
代碼:
在onCreate方法里設(shè)定一像素的activity
window.setGravity(Gravity.START | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 0;
params.y = 0;
params.height = 1;
params.width = 1;
window.setAttributes(params);
在onReceive方法里進(jìn)行操作
public void onReceive(final Context context, Intent intent) {
Log.i("ScreenStateReceiver", "---屏幕鎖屏監(jiān)聽---");
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
//屏幕鎖定,開啟OnePixelActivity
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
//屏幕解鎖桐早,finish OnePixelActivity
}
}
說一下詳細(xì)步驟
1.需要注冊(cè)一個(gè)監(jiān)聽屏幕鎖屏和解屏的BroadcastReceiver
關(guān)于這一點(diǎn)有一個(gè)細(xì)節(jié)需要處理癣缅,為了放置用戶快速進(jìn)行鎖屏和解屏的切換操作,而導(dǎo)致OnePixelActivity頻繁開關(guān)友存。需要用一個(gè)Handler發(fā)送一個(gè)延遲消息處理最佳:
private Handler mHandler;
private boolean isScreenOn = true;
private PendingIntent pendingIntent;
private List<ScreenStateListener> screenStateListeners = null;
@Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
//標(biāo)記屏幕為鎖屏狀態(tài)
isScreenOn = false;
//開啟一像素的Activity
startOnePixelActivity(context);
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
//標(biāo)記屏幕為解屏狀態(tài)
isScreenOn = true;
if(pendingIntent!=null){
pendingIntent.cancel();
}
}
}
//開啟一像素的Activity
private void startOnePixelActivity(final Context context){
if(mHandler==null){
mHandler = new Handler(Looper.myLooper());
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//如果屏幕此時(shí)已經(jīng)打開,則不執(zhí)行
if(isScreenOn){
return;
}
if(pendingIntent!=null){
pendingIntent.cancel();
}
Intent startOnePixelActivity = new Intent(context, OnePixelActivity.class);
startOnePixelActivity.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//啟動(dòng)一像素包活activity
pendingIntent = PendingIntent.getActivity(context, 0, startOnePixelActivity, 0);
try {
pendingIntent.send();
} catch (Exception e) {
e.printStackTrace();
}
notifyScreenOff();
}
},1000);
}
2爬立、需要將該activity的 android:excludeFromRecents設(shè)置為true
3万哪、需要將該Activity設(shè)置為singleInstance啟動(dòng)模式
<activity android:name=".OnePixelActivity"
android:theme="@style/onePixelActivity"
android:launchMode="singleInstance"
android:excludeFromRecents="true"/>
為什么需要將此Activity的啟動(dòng)模式設(shè)置為singleInstance呢?原因是因?yàn)槿绻O(shè)置成其他模式奕巍,如果按照如下步驟操作的話會(huì)出現(xiàn)不友好的狀況:
1、啟動(dòng)App(比如進(jìn)入MainActivity),按home鍵讓app返回后臺(tái)
2的止、鎖定屏幕檩坚,此時(shí)注冊(cè)好的監(jiān)聽廣播會(huì)啟動(dòng)OnePixelActivity
3匾委、解鎖屏幕,此時(shí)廣播接受到此廣播后會(huì)finish 掉OnePixelActivity.
也就是說氓润,當(dāng)你的app在后臺(tái)的時(shí)候,你解鎖屏幕,創(chuàng)建銷毀OnePixelActvity的同時(shí)會(huì)把整個(gè)棧的activity彈出來,然會(huì)用戶就會(huì)莫名其妙的看到你的app自己打開了。
另外需要注意的是咖气,當(dāng)屏幕解鎖的時(shí)候,OnePixelActivity的onResume得到執(zhí)行崩溪,所以在該Activity的onResume方法執(zhí)行finish效果最好:
//OnePixelActivity的onResume
protected void onResume() {
super.onResume();
if (DeviceUtils.isScreenOn(this)) {//如果屏幕已經(jīng)打開
finish();
}
}
雙進(jìn)程守護(hù)
所謂雙進(jìn)程守護(hù)原理就是很簡單:兩個(gè)進(jìn)程的Service浅役,相互守護(hù);當(dāng)一個(gè)進(jìn)程的Service掛的時(shí)候伶唯,另一個(gè)進(jìn)程的Service負(fù)責(zé)重啟掛掉的Service.因?yàn)橄到y(tǒng)的進(jìn)程回收機(jī)制是一個(gè)個(gè)回收的,利用這個(gè)時(shí)間差來相互喚起,當(dāng)一個(gè)進(jìn)程被磨滅掉,另一個(gè)馬上重啟,缺點(diǎn)是現(xiàn)在大部分機(jī)型只要一鍵清理就玩完了觉既。文中KeepAliveService是跟我們要保活的App處于一個(gè)進(jìn)程的Service乳幸,RemoteService是另外一個(gè)進(jìn)程的Service;將RemoteService設(shè)置成單獨(dú)的進(jìn)程很方便奋救,在AndroidMainfest.xml里面配置 android:process即可。
<service android:name=".KeepAliveService"/>
//將RemoteService和KeepAliveServcie處于不同的進(jìn)程
<service android:name=".RemoteService" android:process=":remote"/>
守護(hù)進(jìn)程其實(shí)是一個(gè)雙向守護(hù)的過程反惕,比如KeepAliveService掛了尝艘,那么RemoteService負(fù)責(zé)將KeepAliveService重啟;同理姿染,如果RemoteService掛了的話背亥,KeepAliveService負(fù)責(zé)將RemoteService重啟秒际!那么二者是怎么知道彼此是否掛了呢?因?yàn)槭俏挥趦蓚€(gè)進(jìn)程狡汉,所以我們可以通過AIDL來確保兩個(gè)不同進(jìn)程的Service可以通信娄徊。
定義一個(gè)AIDL接口
interface GuardAidl {
void notifyAlive();
}
接著我們重寫KeepAliveSerivice的onBind方法,該方法返回一個(gè)IBinder給RemoteService使用:
//將keepAliveBinder 交給RemoteService
public IBinder onBind(Intent intent) {
return keepAliveBinder;
}
private GuardAidl.Stub keepAliveBinder = new GuardAidl.Stub(){
@Override
public void notifyAlive() throws RemoteException {
Log.i(null,"Hello RemoteService!");
}
};
通過KeepAliveService的onBind方法返回一個(gè)IBinder對(duì)象交給RemoteService使用盾戴,這樣RemoteService就可以通過keepAliveBinder對(duì)象跟KeepAliveServcie進(jìn)行通信寄锐!同樣的RemoteService也是一樣的代碼邏輯:
public class RemoteService extends Service {
public IBinder onBind(Intent intent) {
return remoteBinder;
}
private GuardAidl.Stub remoteBinder = new GuardAidl.Stub(){
@Override
public void notifyAlive() throws RemoteException {
Log.i(null,"Hello KeepAliveService!");
}
};
}
兩個(gè)Servcie相互綁定
現(xiàn)在兩個(gè)Service都重寫onBind返回了自己的IBinder,但是怎么將自己創(chuàng)建的IBinder 交給對(duì)象使用呢尖啡?答案是雙方調(diào)用bindSerice方法相互綁定對(duì)方橄仆,該方法是Service的一個(gè)方法,其簽名如下:
bindService(Intent service, ServiceConnection conn, int flags)
所以我們綁定的時(shí)候除了傳一個(gè)intent之外衅斩,還要傳一個(gè)ServiceConnection盆顾。看看KeepAliveService綁定RemoteService的代碼:
//KeepAliveService的onStartCommand方法
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("KeepAliveService", "---KeepAliveService 啟動(dòng)---");
//注冊(cè)鎖屏廣播
registerScreenStateReceiver();
//初始化播放器
initMediaPlayer();
//開啟前臺(tái)Service
startForeground(this);
//start HideNotifactionService
startHideNotificationService();
//綁定守護(hù)進(jìn)程
bindRemoteService();
return START_STICKY;
}
//綁定守護(hù)進(jìn)程
private void bindRemoteService(){
Intent intent = new Intent(this,RemoteService.class);
bindService(intent,connection,Context.BIND_ABOVE_CLIENT);
}
private ServiceConnection connection = new ServiceConnection() {
//調(diào)用此方法說明RemoteServcie已經(jīng)掛掉畏梆,需要重新啟動(dòng)
public void onServiceDisconnected(ComponentName name) {
Intent remoteService = new Intent(KeepAliveService.this,
RemoteService.class);
KeepAliveService.this.startService(remoteService);
Intent intent = new Intent(KeepAliveService.this, RemoteService.class);
//將KeepAliveService和RemoteService進(jìn)行綁定
KeepAliveService.this.bindService(intent, connection,
Context.BIND_ABOVE_CLIENT);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
//與RemoteService綁定成功
GuardAidl remoteBinder = GuardAidl.Stub.asInterface(service);
remoteBinder.notifyAlive();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
其中ServiceConnection有兩個(gè)方法onServiceDisconnected和onServiceConnected您宪,當(dāng)RemoteService被異常銷毀掛掉的時(shí)候,onServiceDisconnected會(huì)被調(diào)用奠涌。此時(shí)需要在該方法里面重新啟動(dòng)RemoteService;而onServiceConnected方法則是系統(tǒng)調(diào)用它來傳送在RemoteService的onBind()中返回的IBinder宪巨!同理,RemoteService綁定KeepAlivce的代碼跟上面雷同溜畅,再此就不在貼出來了捏卓,
源碼可以點(diǎn)擊此處查閱