保活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)
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其實已經變得不太可能了,谷歌這樣做無疑是為了減少流氓軟體的數量,這樣做也是可取的。