天天看點

Android Service保活的幾種方法總結

保活Service我們需要做什麼:

1.在應用被關閉後保活(最難)

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

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

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

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

首先介紹一下Service的等級:

一、前台程序

二、可見程序

三、服務程序

四、背景程序

五、空程序  ---關閉應用後,沒有清理緩存

是以為了提高優先級我們可以使用startForeground()方法将Service設定為前台程序。

一、在AndroidManifest中添加Service

<service android:name=".modle.StepService"
            android:process="istep.service"  //放入新程序
            >
            <intent-filter android:priority="1000">
                <!-- 系統啟動完成後會調用-->
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.DATE_CHANGED"/>
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.USER_PRESENT" />
                <action android:name="android.intent.action.ACTION_TIME_TICK" />
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
            </intent-filter>
</service>
 
 
<service android:name=".modle.GuardService"
            android:process=":GuardService">
            <intent-filter >
                <!-- 系統啟動完成後會調用-->
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.DATE_CHANGED"/>
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.USER_PRESENT" />
                <action android:name="android.intent.action.ACTION_TIME_TICK" />
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
            </intent-filter>
        </service>
           

二、雙程序保護

1.建立aidl實作跨程序通信(建立一個aidl)

Android Service保活的幾種方法總結
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

1、在AndroidManifest中添加Service和權限

<!--JobService權限-->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<receiver android:name=".modle.BootCompleteReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
</receiver>
           

2、JobService代碼

/**
 * 用于判斷Service是否被殺死
 * Created by db on 2018/1/11.
 */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)//5.0以後可用
public class JobWakeUpService extends JobService{
    private int JobWakeUpId = 1;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //開啟輪尋
        JobInfo.Builder mJobBulider = new JobInfo.Builder(
                JobWakeUpId,new ComponentName(this,JobWakeUpService.class));
        //設定輪尋時間
        mJobBulider.setPeriodic(2000);
        JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        mJobScheduler.schedule(mJobBulider.build());
        return START_STICKY;
    }
 
    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        //開啟定時任務 定時輪尋 判斷應用Service是否被殺死
        //如果被殺死則重新開機Service
        boolean messageServiceAlive = serviceAlive(StepService.class.getName());
        if(!messageServiceAlive){
            startService(new Intent(this,StepService.class));
        }
 
        return false;
    }
 
    @Override
    public boolean onStopJob(JobParameters jobParameters) {
 
        return false;
    }
 
    /**
     * 判斷某個服務是否正在運作的方法
     * @param serviceName
     *            是包名+服務的類名(例如:net.loonggg.testbackstage.TestService)
     * @return true代表正在運作,false代表服務沒有正在運作
     */
    private boolean serviceAlive(String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager)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>
           

2)廣播代碼

/**
 * 開機完成廣播
 */
 
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" />
           

(2)在建立Service以後調用方法

/**
     * 同步方法   得到休眠鎖
     * @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");
    }
           

(3)在onDestroy()方法中調用釋放鎖的方法(避免占用記憶體)

synchronized private void releaseLock()
    {
        if(mWakeLock!=null){
            if(mWakeLock.isHeld()) {
                mWakeLock.release();
                Log.v(TAG,"release lock");
            }
 
            mWakeLock=null;
        }
    }
           

PARTIAL_WAKE_LOCK    保持CPU運轉,螢幕和鍵盤燈有可能是關閉的。

SCREEN_DIM_WAKE_LOCK    保持CPU運轉,允許保持螢幕顯示但有可能是灰的,允許關閉鍵盤燈。

SCREEN_BRIGHT_WAKE_LOCK    保持CPU運轉,保持螢幕高亮顯示,允許關閉鍵盤燈。

FULL_WAKE_LOCK    保持CPU運轉,保持螢幕高亮顯示,鍵盤燈也保持亮度。

ACQUIRE_CAUSES_WAKEUP    不會喚醒裝置,強制螢幕馬上高亮顯示,鍵盤燈開啟。有一個例外,如果有notification彈出的話,會喚醒裝置。

ON_AFTER_RELEASE    Wake Lock被釋放後,維持螢幕亮度一小段時間,減少Wake Lock循環時的閃爍情況。

六、啟動所有Service(在Activity中)

/**
     * 開啟所有Service
     */
    private void startAllServices()
    {
        startService(new Intent(this, StepService.class));
        startService(new Intent(this, GuardService.class));
        if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP) {
            Log.d(TAG, "startAllServices: ");
            //版本必須大于5.0
            startService(new Intent(this, JobWakeUpService.class));
        }
    }
           

注意:該方法不能保證在所有機型上有效,而且除非在必要時,否則不建議寫這樣的流氓軟體。特别是谷歌在android7.0以後對管理加強,想要保活Service其實已經變得不太可能了,谷歌這樣做無疑是為了減少流氓軟體的數量,這樣做也是可取的。