保證Service不被Kill的解決方案
1、Service設置成START_STICKY(onStartCommand方法中),kill 後會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣
2、通過 startForeground將進程設置為前臺進程,做前臺服務,優先級和前臺應用一個級別?,除非在系統內存非常缺,否則此進程不會被 kill.具體實現方式為在service中創建一個notification,再調用void android.app.Service.startForeground(int id,Notificationnotification)方法運行在前臺即可。
3、雙進程Service:讓2個進程互相保護,其中一個Service被清理後,另外沒被清理的進程可以立即重啟進程
雙進程守護:
首先是一個AIDL接口,兩邊的Service都要通過繼承ServiceAidl.Stub來實現AIDL接口中的方法。接口聲明如下:
interface ServiceAidl { String getName(); }
然後是兩個Service,為了保持連接,內部寫一個內部類實現ServiceConnection的接口,當外部殺了其中一個進程的時候,會進入onDisConnection中,那麽此時要做的就是start和bind另一個進程,因為Service的啟動是可以多次的,所以這樣是沒問題的,代碼如下:
public class LocalService extendsService { private ServiceConnection conn; private MyService myService; @Override public IBinder onBind(Intent intent) { return myService; } @Override public void onCreate() { super.onCreate(); init(); } private void init() { if(conn == null) { conn = new MyServiceConnection(); } myService = new MyService(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(getApplicationContext(), "本地進程啟動", Toast.LENGTH_LONG).show(); Intent intents = new Intent(); intents.setClass(this, RemoteService.class); bindService(intents, conn, Context.BIND_IMPORTANT); return START_STICKY; } class MyService extends ServiceAidl.Stub { @Override public String getName() throws RemoteException { return null; } } class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("獲取連接"); } @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(LocalService.this, "遠程連接被幹掉了", Toast.LENGTH_SHORT).show(); LocalService.this.startService(new Intent(LocalService.this, RemoteService.class)); LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT); } } }
遠程服務類如下:
public class RemoteService extends Service { private MyBinder binder; private ServiceConnection conn; @Override public void onCreate() { super.onCreate(); // System.out.println("遠程進程開啟"); init(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(getApplicationContext(), "遠程進程啟動", Toast.LENGTH_LONG).show(); Intent intents = new Intent(); intents.setClass(this, LocalService.class); bindService(intents, conn, Context.BIND_IMPORTANT); return START_STICKY; } private void init() { if (conn == null) { conn = new MyConnection(); } binder = new MyBinder(); } @Override public IBinder onBind(Intent intent) { return binder; } static class MyBinder extends ServiceAidl.Stub { @Override public String getName() throws RemoteException { return "遠程連接"; } } class MyConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("獲取遠程連接"); } @Override public void onServiceDisconnected(ComponentName nme) { Toast.makeText(RemoteService.this, "本地連接被幹掉了", Toast.LENGTH_SHORT).show(); RemoteService.this.startService(new Intent(RemoteService.this, LocalService.class)); RemoteService.this.bindService(new Intent(RemoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT); } } }
布局文件裏要加上聲明
<service android:name=".LocalService" /> <service android:name=".RemoteService" android:process=":remote" />
實際情況我個人測試,在5.0以下的模擬器上是沒問題的,不管多次從系統的進程裏kill掉,也還是會重新啟動tos,但是5.0以上這種方法是無效的,5.0以上Android應該是意識到了這種雙進程守護的方式,因此修改了一下源碼,讓這種雙進程保活應用的方式無效。因此,針對5.0以上,我們采用另一種方案。
JobScheduler執行任務調度保活
JobScheduler這個類是21版本google新出來的api。
這個任務其實是在設備空閑期執行的,而且系統設計的這個api不會很耗電,本意是用來執行一些任務調度的,但是我們設想一下,如果用這個類來執行我們的開啟雙進程,那麽也是一定會在設備空閑期執行的,因此我們寫一個類繼承JobService,在onstart裏聲明創建JobScheduler對象,並設置多久執行一次和開機自啟動,這樣就能確保及時在息屏狀態,也能夠執行重啟進程,所以我們在JobService的onStopJob方法裏判斷我們的進程是否被回收了,如果被回收了就重啟進程,這樣子就可以實現5.0以上的進程保活了。具體代碼如下:
public class JobHandlerService extends JobService { private JobScheduler mJobScheduler; @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("服務被創建"); // startService(new Intent(this, LocalService.class)); // startService(new Intent(this, RemoteService.class)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder(startId++, new ComponentName(getPackageName(), JobHandlerService.class.getName())); builder.setPeriodic(5000); //每隔5秒運行一次 builder.setRequiresCharging(true); builder.setPersisted(true); //設置設備重啟後,是否重新執行任務 builder.setRequiresDeviceIdle(true); if (mJobScheduler.schedule(builder.build()) <= 0) { //If something goes wrong System.out.println("工作失敗"); } else { System.out.println("工作成功"); } } return START_STICKY; } @Override public boolean onStartJob(JobParameters params) { Toast.makeText(this, "服務啟動", Toast.LENGTH_SHORT).show(); // || isServiceRunning(this, "com.ph.myservice.RemoteService") == false System.out.println("開始工作"); // if (!isServiceRunning(getApplicationContext(), "com.ph.myservice") || !isServiceRunning(getApplicationContext(), "com.ph.myservice:remote")) { // startService(new Intent(this, LocalService.class)); // startService(new Intent(this, RemoteService.class)); // } /* boolean serviceRunning = isServiceRunning(getApplicationContext(), "com.ph.myservice"); System.out.println("進程一" + serviceRunning); boolean serviceRunning2 = isServiceRunning(getApplicationContext(), "com.ph.myservice:remote"); System.out.println("進程二" + serviceRunning2);*/ return false; } @Override public boolean onStopJob(JobParameters params) { if (!isServiceRunning(this, "com.ph.myservice.LocalService") || !isServiceRunning(this, "com.ph.myservice.RemoteService")) { startService(new Intent(this, LocalService.class)); startService(new Intent(this, RemoteService.class)); } return false; } // 服務是否運行 public boolean isServiceRunning(Context context, String serviceName) { boolean isRunning = false; ActivityManager am = (ActivityManager) this .getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo info : lists) {// 獲取運行服務再啟動 System.out.println(info.processName); if (info.processName.equals(serviceName)) { isRunning = true; } } return isRunning; } }
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { openJobService(); } else { openTwoService(); } } private void openTwoService() { startService(new Intent(this, LocalService.class)); startService(new Intent(this, RemoteService.class)); } private void openJobService() { Intent intent = new Intent(); intent.setClass(MainActivity.this, JobHandlerService.class); startService(intent); } }
經過我6.0系統的華為真機測試是沒有問題的,就算處於息屏狀態進程也還是活著的,不管過多久打開屏幕還是會tos,並且關機了開機也會吐司。
4、AlarmManager不斷啟動service。該方式原理是通過定時警報來不斷啟動service,這樣就算service被殺死,也能再啟動參考實現方式如下:
Intent intent =new Intent(mContext, MyService.class);
PendingIntent sender=PendingIntent
.getService(mContext, 0, intent, 0);
AlarmManager alarm=(AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis,5*1000,sender);
該方式基本可以保證在正常運行情況下,以及任務欄移除歷史任務後(小米、魅族手機除外),service不被殺死。但是360等軟件管家依然可以殺死。另外還有不斷啟動的邏輯處理麻煩。
5、監聽系統廣播判斷Service狀態(比如:手機重啟、網絡切換、開鎖屏)
6、QQ黑科技:在應用退到後臺後,另起一個只有 1 像素的頁面停留在桌面上,讓自己保持前臺狀態,保護自己不被後臺清理工具殺死
7、在已經root的設備下,修改相應的權限文件,將App偽裝成系統級的應用(Android4.0系列的一個漏洞,已經確認可行)
- Android系統中當前進程(Process)fork出來的子進程,被系統認為是兩個不同的進程。當父進程被殺死的時候,子進程仍然可以存活,並不受影響。鑒於目前提到的在Android-Service層做雙守護都會失敗,我們可以fork出c進程,多進程守護。死循環在那檢查是否還存在,具體的思路如下(Android5.0以下可行)
- 用C編寫守護進程(即子進程),守護進程做的事情就是循環檢查目標進程是否存在,不存在則啟動它。
- 在NDK環境中將1中編寫的C代碼編譯打包成可執行文件(BUILD_EXECUTABLE)。
- 主進程啟動時將守護進程放入私有目錄下,賦予可執行權限,啟動它即可。
8、聯系廠商,加入白名單
轉載連接
http://www.cnblogs.com/shen-hua/p/5836386.html
http://blog.csdn.net/pan861190079/article/details/72773549
保證Service不被Kill的解決方案