1. 程式人生 > >對Android程序守護、鬧鐘後臺被殺死的研究

對Android程序守護、鬧鐘後臺被殺死的研究

最近公司要求要做一個提醒功能,一說到提醒,那肯定就和鬧鐘差不多的意思,那麼肯定就要用到AlarmManager。

但是,我們知道,android系統很坑爹,不同的廠商對rom的定製,導致對程序的管理都不太相同,但是如何做到在各個手機上都能一直保持後臺執行呢?。

為了解決這個問題,特地去研究了各種保持程序不被殺死的方法。

下面對幾種常見的用法進行了分析,並且給出了我自己發現的一個保持程序執行的方法。


方法1:在原生的Android系統上使用AlarmManager

方法2:通過AIDL實現雙程序守護機制

方法3:MarsDaemon第三方庫,實現程序常駐

方法4:通過AppWiget,android桌面小元件保持程序的執行


下面是具體分析

方法1:在原生的Android系統上使用AlarmManager

“原生”這個詞就對這個方法的限定很大了。我嘗試了很多次,在原生的作業系統中,不需要特殊的去呼叫service處理。直接在某個Activity中通過AlarmManager的set和setRepeating方法設定定時後,就去殺了程序,測試結果顯示,鬧鐘還是可以繼續響的。但是這種不去特殊處理的,在第三方的rom基本都是不行的,相信大家每人敢用,因此知道就可以了。

方法2:通過AIDL實現雙程序守護機制

網上有很多關於AIDL實現雙程序守護機制的文章,內容都是差不多,關於這種方法,都是通過在MainFest檔案中指定某個Service android:process=":remote",這樣就可以使這個service單開一個程序來執行。在主程序中有一個MainService,一旦RemoteService所在程序被殺死,MainService就會立刻去重新啟動它,同樣的,當MainService被殺死了,RemoteService就會去啟動MainService,兩個程序的兩個Service互相監控來實現程序不銷燬。

大致方法為:

1、建立一個IMyAIDLInterface.aidl檔案

// IMyAidlInterface.aidl
package com.xiaoqi.alarmmanagerdemo;

interface IMyAidlInterface {
    String getServiceName();
}

2、建立一個LocalService:
public class LocalService extends Service{
	MyBinder binder;
	MyConn conn;
	@Nullable
	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		binder = new MyBinder();
		conn = new MyConn();
	}

	class MyBinder extends IMyAidlInterface.Stub {
		@Override
		public String getServiceName() throws RemoteException {
			return LocalService.class.getSimpleName();
		}
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Toast.makeText(LocalService.this, " 本地服務活了", Toast.LENGTH_SHORT).show();
		this.bindService(new Intent(LocalService.this,RomoteService.class),conn, Context.BIND_IMPORTANT);
		return START_STICKY;
	}

	class MyConn implements ServiceConnection{
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.i("xiaoqi", "繫結上了遠端服務");
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.i("xiaoqi", "遠端服務被幹掉了");
			Toast.makeText(LocalService.this, "遠端服務掛了", Toast.LENGTH_SHORT).show();
			//開啟遠端服務
			LocalService.this.startService(new Intent(LocalService.this,RomoteService.class));
			//繫結遠端服務
			LocalService.this.bindService(new Intent(LocalService.this,RomoteService.class),conn,Context.BIND_IMPORTANT);
		}
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		//開啟遠端服務
		LocalService.this.startService(new Intent(LocalService.this,RomoteService.class));
		//繫結遠端服務
		LocalService.this.bindService(new Intent(LocalService.this,RomoteService.class),conn,Context.BIND_IMPORTANT);

	}
}
3、建立一個RemoteService:
public class RomoteService extends Service{
	MyConn conn;
	MyBinder binder;

	@Nullable
	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		conn = new MyConn();
		binder = new MyBinder();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {

		Toast.makeText(this, " 遠端服務活了", Toast.LENGTH_SHORT).show();
		this.bindService(new Intent(this, LocalService.class), conn, Context.BIND_IMPORTANT);

		return START_STICKY;
	}

	class MyBinder extends IMyAidlInterface.Stub {
		@Override
		public String getServiceName() throws RemoteException {
			return RomoteService.class.getSimpleName();
		}
	}

	class MyConn implements ServiceConnection {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.i("xiaoqi", "繫結本地服務成功");
			// Toast.makeText(RomoteService.this, "繫結本地服務成功", Toast.LENGTH_SHORT).show();

		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.i("xiaoqi", "本地服務被幹掉了");
			Toast.makeText(RomoteService.this, "本地服務掛了", Toast.LENGTH_SHORT).show();

			//開啟本地服務
			RomoteService.this.startService(new Intent(RomoteService.this, LocalService.class));
			//繫結本地服務
			RomoteService.this.bindService(new Intent(RomoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT);
		}

	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		//開啟本地服務
		RomoteService.this.startService(new Intent(RomoteService.this, LocalService.class));
		//繫結本地服務
		RomoteService.this.bindService(new Intent(RomoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT);

	}
}
4、在AndroidMainfest檔案中註冊:
<service android:name="com.xiaoqi.alarmmanagerdemo.LocalService">
</service>
<service android:name="com.xiaoqi.alarmmanagerdemo.RomoteService" android:process=":romoteservice">
</service>
使用方法:
Intent service = new Intent(this,LocalService.class);
startService(service);
Intent remoteService = new Intent(this,RomoteService.class);
startService(remoteService);

這樣就可以了,但是經過測試發現,我們在應用管理中,會發現確實有兩個程序,我們單獨的去關閉一個,另一個馬上就會把它開啟,但是如果我們之間去殺程序,發現只有在vivo手機中,確實可以保持不被殺死,但是在其他手機中,整個後臺程序還是被殺死了。說明這個方法也不是很可靠的。

方法3:MarsDaemon第三方庫,實現程序常駐

守護程序也有第三方庫,相信很多人都沒想到吧
這個庫的使用也非常簡單,底層通過jni來實現了程序守護,這邊就不給出使用方法了,直接在github上看就行了。

但是我實際使用發現,在華為機器上依然不能程序保持執行,只要一清理,後臺的鬧鐘就沒有效果了。但是在某些機型上還是可以用的,可靠性比通過AIDL的雙程序守護效果好,可是依然不能保證執行。

方法4:通過AppWiget,android桌面小元件保持程序的執行

嘗試了網上的很多方法,都是程序一清理,所以程式都被停止了,尤其在華為手機上,360都沒法保持一直執行,因此我覺得這個想讓後臺服務永久執行的想法越來越不可靠。

我在應用商店上下載了一個排行第一的鬧鐘軟體,但是經過多次測試,結果都是一樣,想要後臺進行,基本是不可能。QQ是通過一個畫素點,一直顯示在最前,這種黑科技來保持程序一直在,於是我想到了,如果我們新增一個桌面元件來,這樣這個元件也是App的一部分, 但它卻一直執行在那,這樣是否就可以讓程序殺死了,程式還是能執行呢?

於是我測試了一下,寫了一個很簡單AppWidget元件,在AppWifgetProvider中,寫了一個簡單的鬧鐘程式,並且讓AppWidget中的TextView的數字一直自增1。寫完之後我測了一下,發現這個方法是可行的。即使是在華為手機上,我把程序清理了,鬧鐘還是會響,AppWidget上的數字也一直在更新。

關於AppWidget的寫法網上也很多,這邊就不具體給出了。在測試這個方法的時候,我發現剛剛下載的鬧鐘軟體也有桌面小元件,我添加了之後,再進行鬧鐘測試,居然發現,在程序殺死後,鬧鐘居然可以繼續執行,即使是鎖屏狀態,很明顯,這個軟體使用的方法和我想到的是一樣的。

通過AppWidget來保持程序中程式碼的執行,這個應該還其他部落格中還沒有被提到,這個方法相比其他的方法而言,已經是非常可靠的了。但是這個侷限也挺大,就是必須通過一個AppWidget來實現。

關於程序守護已經分析完了,如果有什麼更好的方法,歡迎大家分析。