1. 程式人生 > 其它 >Android Service保活

Android Service保活

技術標籤:Android

保活Service我們需要做什麼:

1.在應用被關閉後保活

2.在內用佔用過大,系統自動釋放記憶體時保活(優先殺死佔用較高的Service)

3.重啟手機後自動開啟Service

4.手機息屏後不被釋放記憶體

5.手動清理記憶體時保活

Android程序的生命週期

與大家比較熟悉的Activity生命週期相比,Android程序的生命週期實質更為簡單,越核心的東西越簡單嘛,Android將一個程序分為五種不同的狀態:

一、前臺程序 Foreground process
二、可見程序 Visible process
三、服務程序 Service process
四、後臺程序 Background process

五、空程序 Empty process

後臺程序常駐的實現

程序提權

adj值越小的程序越不容易被殺死,相對普通程序來說能讓adj去到0顯然是最完美的,可是我們如何才能讓一個完全沒有可見元素的後臺程序擁有前臺程序的狀態呢?Android給了Service這樣一個功能:startForeground,它的作用就像其名字一樣,將我們的Service置為前臺,不過你需要傳送一個Notification:

public class DaemonService extends Service {
    @Override
    public void onCreate() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(250, builder.build());
        } else {
            startForeground(250, new Notification());
        }
    }
}

雙程序保護

1.建立aidl實現跨程序通訊(新建一個aidl)

interface ProcessConnection {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    //刪除不必要方法
 }

2.建立主服務

/**
 * 主程序 雙程序通訊
 * Created by db on 2018/1/11.
 */
 
public class StepService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new ProcessConnection.Stub() {};
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground(1,new Notification());
        //繫結建立連結
        bindService(new Intent(this,GuardService.class),
                mServiceConnection, Context.BIND_IMPORTANT);
        return START_STICKY;
    }
 
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //連結上
            Log.d("test","StepService:建立連結");
        }
 
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            //斷開連結
            startService(new Intent(StepService.this,GuardService.class));
            //重新繫結
            bindService(new Intent(StepService.this,GuardService.class),
                    mServiceConnection, Context.BIND_IMPORTANT);
        }
    };
 
}

3.建立守護服務

 /**
* 守護程序 雙程序通訊
* Created by db on 2018/1/11.
*/
 
public class GuardService extends Service{
 @Nullable
 @Override
 public IBinder onBind(Intent intent) {
 return new ProcessConnection.Stub() {};
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
 startForeground(1,new Notification());
 //繫結建立連結
 bindService(new Intent(this,StepService.class),
 mServiceConnection, Context.BIND_IMPORTANT);
 return START_STICKY;
 }
 
 private ServiceConnection mServiceConnection = new ServiceConnection() {
 @Override
 public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
 //連結上
 Log.d("test","GuardService:建立連結");
 }
 
 @Override
 public void onServiceDisconnected(ComponentName componentName) {
 //斷開連結
 startService(new Intent(GuardService.this,StepService.class));
 //重新繫結
 bindService(new Intent(GuardService.this,StepService.class),
 mServiceConnection, Context.BIND_IMPORTANT);
 }
 };
 
}
 

返回引數含義:

  • START_STICKY:在Service被關閉後,重新開啟Service
  • START_NOT_STICKY:服務被異常殺掉後,系統將會被設定為started狀態,系統不會重啟該服務,直到startService(Intent intent)方法再次被呼叫。
  • START_REDELIVER_INTENT:重傳Intent,使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務,並將Intent的值傳入。
  • START_STICKY_COMPATIBILITY:START_STICKY的相容版本,但不保證服務被kill後一定能重啟。

使用JobService來實現應用退出後重啟Service

什麼是JobService

JobService也是一個service,和普通的service不同的是,JobService是一個任務回撥類,通過JobScheduler設定任務給系統,系統來呼叫JobService中的方法,具體處理什麼任務需要我們自己在JobService中的回撥方法中實現。那麼關於任務的管理和程序的維護、排程當然是由系統來統一管理。

Google從Android SDK 21之後添加了JobScheduler來執行一些滿足特定條件但不緊急的後臺任務,我們可以利用JobScheduler來執行這些特殊的後臺任務時來減少電量的消耗。

使用JobService來實現APP程序防殺。
1.首先宣告許可權

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<service
            android:name=".MyJobDaemonService"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_JOB_SERVICE" />

2.自定義一個Service類,繼承自JobService

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobDaemonService extends JobService {
    private int kJobId = 0;
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyJobDaemonService", "jobService啟動");
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }
 
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i("MyJobDaemonService", "執行了onStartJob方法");
        boolean isLocalServiceWork = isServiceWork(this, "com.marswin89.marsdaemon.demo.Service1");
        boolean isRemoteServiceWork = isServiceWork(this, "com.marswin89.marsdaemon.demo.Service2");
        if(!isLocalServiceWork||
                !isRemoteServiceWork){
            this.startService(new Intent(this,Service1.class));
//            this.startService(new Intent(this,Service2.class));
//            Toast.makeText(this, "程序啟動", Toast.LENGTH_SHORT).show();
            Log.i("onStartJob", "啟動service1");
        }
        return true;
    }
 
    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i("MyJobDaemonService", "執行了onStopJob方法");
        scheduleJob(getJobInfo());
        return true;
    }
 
    //將任務作業傳送到作業排程中去
    public void scheduleJob(JobInfo t) {
        Log.i("MyJobDaemonService", "排程job");
        JobScheduler tm =
                (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        tm.schedule(t);
    }
 
    public JobInfo getJobInfo(){
        JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, MyJobDaemonService.class));
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE);
        builder.setPersisted(true);
        builder.setRequiresCharging(false);
        builder.setRequiresDeviceIdle(false);
        //間隔1000毫秒
        builder.setPeriodic(1000);
        return builder.build();
    }
 
    // 判斷服務是否正在執行
    public boolean isServiceWork(Context mContext, String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager) mContext
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(100);
        if (myList.size() <= 0) {
            return false;
        }
        for (int i = 0; i < myList.size(); i++) {
            String mName = myList.get(i).service.getClassName().toString();
            if (mName.equals(serviceName)) {
                isWork = true;
                break;
            }
        }
        return isWork;
    }
 
}

保證Service在開機後自動啟動

(1)註冊廣播

  <receiver android:name=".modle.mReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
  </receiver>
/**
 * 開機完成廣播
 */
 
public class mReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
        Intent mIntent = new Intent(context,StepService.class);
        context.startService(mIntent);
    }
}

保證息屏後不被釋放資源殺死(WakeLock的使用)

(1)新增許可權

    <uses-permission android:name="android.permission.WAKE_LOCK" />

需求:要在後臺跑一個Service執行輪詢,螢幕熄滅或鎖屏後,仍然需要保持Service一直處於輪詢狀態。

應用程式中如果要在待機前儲存資料狀態的話,要保證此過程中不會進入待機。可以在 onResume() 或者 onStart() 中申請 wakelock 鎖,即呼叫getLock()方法。

在 onPause() 或者 onDistroy() 中處理應用待機後再釋放掉 wakelock 鎖,此時呼叫releaseLock()方法

 /**
 * 同步方法 得到休眠鎖
 * @param context
 * @return
 */
 synchronized private void getLock(Context context){
 if(mWakeLock==null){
 PowerManager mgr=(PowerManager)context.getSystemService(Context.POWER_SERVICE);
 mWakeLock=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,StepService.class.getName());
 mWakeLock.setReferenceCounted(true);
 Calendar c=Calendar.getInstance();
 c.setTimeInMillis((System.currentTimeMillis()));
 int hour =c.get(Calendar.HOUR_OF_DAY);
 if(hour>=23||hour<=6){
 mWakeLock.acquire(5000);
 }else{
 mWakeLock.acquire(300000);
 }
 }
 Log.v(TAG,"get lock");
 }
synchronized private void releaseLock()
    {
        if(mWakeLock!=null){
            if(mWakeLock.isHeld()) {
                mWakeLock.release();
                Log.v(TAG,"release lock");
            }
 
            mWakeLock=null;
        }
    }