1. 程式人生 > >保證Service不被Kill的解決方案

保證Service不被Kill的解決方案

req som .html ppp 真機測試 其中 清理工具 reat 清理

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 extends
Service { 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以下可行)
  1. 用C編寫守護進程(即子進程),守護進程做的事情就是循環檢查目標進程是否存在,不存在則啟動它。
  2. 在NDK環境中將1中編寫的C代碼編譯打包成可執行文件(BUILD_EXECUTABLE)。
  3. 主進程啟動時將守護進程放入私有目錄下,賦予可執行權限,啟動它即可。

8、聯系廠商,加入白名單

轉載連接

http://www.cnblogs.com/shen-hua/p/5836386.html

http://blog.csdn.net/pan861190079/article/details/72773549

保證Service不被Kill的解決方案