Service防殺死

目錄

簡(jiǎn)介

首先要弄明確一個(gè)問(wèn)題,就是我們平時(shí)說(shuō)的Service防殺死,其實(shí)防止是Service所在的進(jìn)程被殺死,而不是Service這個(gè)組件,因?yàn)锳ndroid 只殺死進(jìn)程,而不是組件。
當(dāng)我們說(shuō)進(jìn)程優(yōu)先級(jí)的時(shí)候是以 activity、service 這樣的組件來(lái)說(shuō)的,這些組件的優(yōu)先級(jí)是在進(jìn)程的級(jí)別上,不是組件級(jí)別上。只要一個(gè)組件的狀態(tài)發(fā)生變化,就會(huì)影響進(jìn)程的優(yōu)先級(jí);比如:?jiǎn)?dòng)一個(gè)前臺(tái)服務(wù),那么就會(huì)將該服務(wù)所在的整個(gè)進(jìn)程變?yōu)榍芭_(tái)進(jìn)程。
弄清楚了這個(gè)問(wèn)題后,下面我們首先了解一下進(jìn)程的優(yōu)先級(jí)。

進(jìn)程優(yōu)先級(jí)

前臺(tái)進(jìn)程 > 可見(jiàn)進(jìn)程 > 服務(wù)進(jìn)程 > 后臺(tái)進(jìn)程 > 空進(jìn)程。

1.前臺(tái)進(jìn)程;Foreground process
  • 用戶正在交互的Activity(onResume())
  • 當(dāng)某個(gè)Service綁定正在交互的Activity。
  • 被主動(dòng)調(diào)用為前臺(tái)Service(startForeground())
  • 組件正在執(zhí)行生命周期的回調(diào)(onCreate()/onStart()/onDestroy())
  • BroadcastReceiver 正在執(zhí)行onReceive();
2.可見(jiàn)進(jìn)程;Visible process
  • 我們的Activity處在onPause()(沒(méi)有進(jìn)入onStop())
  • 綁定到前臺(tái)Activity的Service。
3.服務(wù)進(jìn)程;Service process

簡(jiǎn)單的startService()啟動(dòng)。

4.后臺(tái)進(jìn)程;Background process

對(duì)用戶沒(méi)有直接影響的進(jìn)程----Activity出于onStop()的時(shí)候。
android:process=":xxx"

5.空進(jìn)程; Empty process

不含有任何的活動(dòng)的組件。(android設(shè)計(jì)的,為了第二次啟動(dòng)更快)

防止進(jìn)程被殺死

所謂進(jìn)程防殺死,就是做到進(jìn)程盡量不被系統(tǒng)殺死,并不能保證100%存活,因?yàn)槭艿絻?nèi)存,手機(jī)廠商的限制等。上面提到進(jìn)程優(yōu)先級(jí),優(yōu)先級(jí)越高越不容易被殺死,所以要想防止進(jìn)程被殺死,就要提高進(jìn)程的優(yōu)先級(jí)。

QQ的做法

QQ采取在鎖屏的時(shí)候啟動(dòng)一個(gè)1個(gè)像素的Activity,當(dāng)用戶解鎖以后將這個(gè)Activity結(jié)束掉,同時(shí)把自己的核心服務(wù)再開(kāi)啟一次。下面我們就簡(jiǎn)單模擬一下。

  • 在首頁(yè)我們放置一個(gè)按鈕,點(diǎn)擊按鈕啟動(dòng)1個(gè)像素的Activity

    /** 啟動(dòng)一個(gè)像素的Activity */
    public void start(View view){
      Intent intent = new Intent(MainActivity.this, LiveActivity.class);
      startActivity(intent);
      finish();
      Toast.makeText(this, "啟動(dòng)完成", Toast.LENGTH_SHORT).show();
    }
    
  • 設(shè)置Activity為一個(gè)像素,首先我們要將Activity的背景設(shè)置為透明,否則顯示的是黑色

     <style name="LiveStyle" parent="Theme.AppCompat.Light.NoActionBar">
      <item name="android:windowBackground">@android:color/transparent</item>
      <item name="android:windowFrame">@null</item>
      <item name="android:windowNoTitle">true</item>
      <item name="android:windowIsFloating">true</item>
      <item name="android:backgroundDimEnabled">false</item>
      <item name="android:windowContentOverlay">@null</item>
      <item name="android:windowIsTranslucent">true</item>
      <item name="android:windowAnimationStyle">@null</item>
      <item name="android:windowDisablePreview">true</item>
      <item name="android:windowNoDisplay">false</item>
    </style>
    
  • 顯示一個(gè)像素

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      Window window = getWindow();
      window.setGravity(Gravity.LEFT | Gravity.TOP);
      WindowManager.LayoutParams params = window.getAttributes();
      params.width = 1; // 1 px
      params.height = 1;
      params.x = 0;
      params.y = 0;
      window.setAttributes(params);
    }
    

從效果圖中看出,我們啟動(dòng)Activity后,手機(jī)頁(yè)面不能滑動(dòng),當(dāng)點(diǎn)擊返回鍵的時(shí)候,又能滑動(dòng)了,說(shuō)明成功啟動(dòng)了1px 的Activity,下面監(jiān)聽(tīng)手機(jī)鎖屏事件。

封裝一個(gè)工具類,專門監(jiān)聽(tīng)鎖屏事件

public class ScreenListener {

private Context mContext;
private ScreenBroadcastReceiver mScreenReceiver;
private ScreenStateListener mScreenStateListener;

public ScreenListener(Context context) {
    mContext = context;
    mScreenReceiver = new ScreenBroadcastReceiver();
}

//監(jiān)聽(tīng)鎖屏事件的廣播接受者
private class ScreenBroadcastReceiver extends BroadcastReceiver {
    private String action = null;
    @Override
    public void onReceive(Context context, Intent intent) {
        action = intent.getAction();
        if (Intent.ACTION_SCREEN_ON.equals(action)) {//開(kāi)屏
            mScreenStateListener.onScreenOn();
        } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { //鎖屏
            mScreenStateListener.onScreenOff();
        } else if (Intent.ACTION_USER_PRESENT.equals(action)) { //解鎖
            mScreenStateListener.onUserPresent();
        }
    }
}

//初始化
public void init(ScreenStateListener listener) {
    mScreenStateListener = listener;
    registerListener();
    getScreenState();
}

//根據(jù)鎖屏狀態(tài)回調(diào)對(duì)應(yīng)的方法
private void getScreenState() {
    PowerManager manager = (PowerManager) mContext
            .getSystemService(Context.POWER_SERVICE);
    if (manager.isScreenOn()) {
        if (mScreenStateListener != null) {
            mScreenStateListener.onScreenOn();
        }
    } else {
        if (mScreenStateListener != null) {
            mScreenStateListener.onScreenOff();
        }
    }
}

public void unregisterListener() {
    mContext.unregisterReceiver(mScreenReceiver);
}

//動(dòng)態(tài)注冊(cè)鎖屏廣播
private void registerListener() {
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_USER_PRESENT);
    mContext.registerReceiver(mScreenReceiver, filter);
}

//鎖屏事件回調(diào)接口
public interface ScreenStateListener {
     void onScreenOn();
     void onScreenOff();
     void onUserPresent();
}
}

啟動(dòng)一個(gè)Service專門管理Activity

public class MyService extends Service{

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    ScreenListener listener = new ScreenListener(this);
    listener.init(new ScreenListener.ScreenStateListener() {
        @Override
        public void onScreenOn() {
            //開(kāi)屏,銷毀1px Activity
            LiveActivityManager.getInstance(MyService.this).finishKeepLiveActivity();
        }

        @Override
        public void onScreenOff() {
            //鎖屏,啟動(dòng) 1px Activity
            LiveActivityManager.getInstance(MyService.this).startKeepLiveActivity();
        }

        @Override
        public void onUserPresent() {  //解鎖    }
    });
}
}

在應(yīng)用啟動(dòng)的時(shí)候啟動(dòng)該服務(wù)

Intent intent = new Intent(this, MyService.class);
startService(intent);

添加權(quán)限

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

通過(guò)日志的輸出可以看出,在鎖屏的時(shí)候調(diào)用了onCreate(),在亮屏的時(shí)候調(diào)用了onDestroy();

上面只是簡(jiǎn)單的模擬了一下,但是實(shí)現(xiàn)上述功能還有一個(gè)前提就是MyService這個(gè)服務(wù)得一直存活,不然沒(méi)辦法監(jiān)聽(tīng)處理了。

雙進(jìn)程守護(hù)

雙進(jìn)程守護(hù),可以防止單個(gè)進(jìn)程殺死,同時(shí)可以防止第三方的軟件清理掉。一個(gè)進(jìn)程被殺死,另外一個(gè)進(jìn)程又被他啟動(dòng)。相互監(jiān)聽(tīng)啟動(dòng),因?yàn)闅⑦M(jìn)程是一個(gè)一個(gè)殺的。本質(zhì)是和殺進(jìn)程時(shí)間賽跑。

  • 1.創(chuàng)建兩個(gè)Service,實(shí)現(xiàn)互相監(jiān)聽(tīng)

    <service android:name=".LocalService"/>
    <service
         android:name=".RemoteService"
         android:process=":remote"/> // 開(kāi)啟一個(gè)新的進(jìn)程
    

LocalService和RemoteService位于不同的進(jìn)程中,實(shí)現(xiàn)互相的監(jiān)聽(tīng),他們的代碼一樣,只不過(guò)是監(jiān)聽(tīng)的對(duì)象不一樣,所以下面只貼出了一個(gè)文件的代碼。

LocalService.java

public class LocalService extends Service {

private MyConnection connection;
private MyBinder binder;

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return binder;
}

@Override
public void onCreate() {
    super.onCreate();
    connection = new MyConnection();
    binder = new MyBinder();
}

@Override
public int onStartCommand(Intent intent,int flags, int startId) {
    //啟動(dòng)的時(shí)候綁定RemoteService
    LocalService.this.bindService(new Intent(LocalService.this,RemoteService.class),connection,
            Context.BIND_IMPORTANT);
    return START_STICKY;
}

// 綁定RemoteService建立的鏈接
class MyConnection implements ServiceConnection{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.e("service","綁定RemoteService");
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        //RemoteService被殺死,重新啟動(dòng)綁定
        Intent service = new Intent(LocalService.this,RemoteService.class);
        LocalService.this.startService(service);
        LocalService.this.bindService(service,connection, Context.BIND_IMPORTANT);
    }
}

 //LocalService和RemoteService通信的Binder
class MyBinder extends RemoteInterface.Stub{

    @Override
    public String getServicName() throws RemoteException {
        return "LocalService";
    }
}

@Override
public void onDestroy() {
    super.onDestroy();
   //銷毀的時(shí)候要解綁
    unbindService(connection);
}
}

從上述代碼中可以看出,在LocalService一啟動(dòng)的時(shí)候(onStartCommand),去綁定RemoteService,對(duì)應(yīng)的連接為connection,那么當(dāng)RemoteService被解綁的時(shí)候就會(huì)調(diào)用onServiceDisconnected方法,在該方法只能再次啟動(dòng)和綁定RemoteService。同理在RemoteService中也是一樣的邏輯,這樣就能實(shí)現(xiàn)兩個(gè)服務(wù)互相監(jiān)聽(tīng),一個(gè)被殺死,另一個(gè)立馬再次啟動(dòng)它。

從效果圖中看出,我們關(guān)閉一個(gè)服務(wù),又會(huì)被立刻啟動(dòng)起來(lái),通過(guò)右邊啟動(dòng)的時(shí)間也可以看出來(lái),如果想提高進(jìn)程的優(yōu)先級(jí),可以在onStartCommand()方法中調(diào)用 startForeground()將進(jìn)程提升為前臺(tái)進(jìn)程。這樣雖然能實(shí)現(xiàn)進(jìn)程常駐,但是兩個(gè)服務(wù)一直在后臺(tái)運(yùn)行,是非常耗費(fèi)資源的,尤其是電量。那么有沒(méi)有更好一點(diǎn)的辦法呢?那就是下面要說(shuō)的內(nèi)容了。

Jobscheduler

Android 5.0系統(tǒng)以后,Google為了優(yōu)化Android系統(tǒng),提高使用流暢度以及延長(zhǎng)電池續(xù)航,加入了在應(yīng)用后臺(tái)/鎖屏?xí)r,系統(tǒng)會(huì)回收應(yīng)用,同時(shí)自動(dòng)銷毀應(yīng)用拉起的Service的機(jī)制。同時(shí)為了滿足在特定條件下需要執(zhí)行某些任務(wù)的需求,google在全新一代操作系統(tǒng)上,采取了Job (jobservice & JobInfo)的方式,即每個(gè)需要后臺(tái)的業(yè)務(wù)處理為一個(gè)job,通過(guò)系統(tǒng)管理job,來(lái)提高資源的利用率,從而提高性能,節(jié)省電源。這樣又能滿足APP開(kāi)發(fā)的要求,又能滿足系統(tǒng)性能的要求。

實(shí)現(xiàn)進(jìn)程防殺死

把任務(wù)加到系統(tǒng)調(diào)度隊(duì)列中,當(dāng)?shù)竭_(dá)任務(wù)窗口期的時(shí)候就會(huì)執(zhí)行,我們可以在這個(gè)任務(wù)里面啟動(dòng)我們的進(jìn)程。這樣可以做到將近殺不死的進(jìn)程。

具體實(shí)現(xiàn)

  • 派生JobService 子類,定義需要執(zhí)行的任務(wù)
  • 從Context 中獲取JobScheduler 實(shí)例
  • 構(gòu)建JobInfo 實(shí)例,指定 JobService任務(wù)實(shí)現(xiàn)類及其執(zhí)行條件
  • 通過(guò)JobScheduler 實(shí)例加入到任務(wù)隊(duì)列
@SuppressLint("NewApi")
public class JobHandleService extends JobService{
private int kJobId = 0;
@Override
public void onCreate() {
    super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    scheduleJob(getJobInfo());
    return START_NOT_STICKY;
}

@Override
public boolean onStartJob(JobParameters params) {
    boolean isLocalServiceWork = isServiceWork(this, "com.gfd.demo.LocalService");
    boolean isRemoteServiceWork = isServiceWork(this, "com.gfd.demo.RemoteService");
    if(!isLocalServiceWork||
       !isRemoteServiceWork){
        this.startService(new Intent(this,LocalService.class));
        this.startService(new Intent(this,RemoteService.class));
    }
    return true;
}

@Override
public boolean onStopJob(JobParameters params) {
    scheduleJob(getJobInfo());
    return true;
}

public void scheduleJob(JobInfo t) {
    JobScheduler tm =
            (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
    tm.schedule(t);
}

public JobInfo getJobInfo(){
    JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, JobHandleService.class));
    builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
    builder.setPersisted(true);
    builder.setRequiresCharging(false);
    builder.setRequiresDeviceIdle(false);
    builder.setPeriodic(10);
}

//判斷Service是否在存活
public boolean isServiceWork(Context mContext, String serviceName) {  
    boolean isWork = false;  
    ActivityManager myAM = (ActivityManager) mContext  
            .getSystemService(Context.ACTIVITY_SERVICE);  
    List<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;  
}  
}

vip視頻

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 什么是進(jìn)程 進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單...
    晨起清風(fēng)閱讀 1,832評(píng)論 0 5
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,523評(píng)論 25 708
  • 【Android Service】 Service 簡(jiǎn)介(★★★) 很多情況下,一些與用戶很少需要產(chǎn)生交互的應(yīng)用程...
    Rtia閱讀 3,173評(píng)論 1 21
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,102評(píng)論 0 8
  • 01 那是一個(gè)陽(yáng)光燦爛的日子,三尺講臺(tái)上隱約可見(jiàn)樹(shù)影婆娑,斑駁了少女十五歲的雨季。 一個(gè)身穿白色運(yùn)動(dòng)套裝的身軀像光...
    愛(ài)上世界的張大路閱讀 1,022評(píng)論 5 14