Service防殺死

目錄

簡介

首先要弄明確一個問題絮姆,就是我們平時說的Service防殺死,其實防止是Service所在的進程被殺死,而不是Service這個組件着茸,因為Android 只殺死進程粤策,而不是組件豆巨。
當(dāng)我們說進程優(yōu)先級的時候是以 activity、service 這樣的組件來說的掐场,這些組件的優(yōu)先級是在進程的級別上往扔,不是組件級別上。只要一個組件的狀態(tài)發(fā)生變化熊户,就會影響進程的優(yōu)先級萍膛;比如:啟動一個前臺服務(wù),那么就會將該服務(wù)所在的整個進程變?yōu)榍芭_進程嚷堡。
弄清楚了這個問題后蝗罗,下面我們首先了解一下進程的優(yōu)先級。

進程優(yōu)先級

前臺進程 > 可見進程 > 服務(wù)進程 > 后臺進程 > 空進程蝌戒。

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

簡單的startService()啟動友鼻。

4.后臺進程傻昙;Background process

對用戶沒有直接影響的進程----Activity出于onStop()的時候闺骚。
android:process=":xxx"

5.空進程; Empty process

不含有任何的活動的組件妆档。(android設(shè)計的僻爽,為了第二次啟動更快)

防止進程被殺死

所謂進程防殺死,就是做到進程盡量不被系統(tǒng)殺死贾惦,并不能保證100%存活胸梆,因為受到內(nèi)存,手機廠商的限制等须板。上面提到進程優(yōu)先級乳绕,優(yōu)先級越高越不容易被殺死,所以要想防止進程被殺死逼纸,就要提高進程的優(yōu)先級洋措。

QQ的做法

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

  • 在首頁我們放置一個按鈕贺嫂,點擊按鈕啟動1個像素的Activity

    /** 啟動一個像素的Activity */
    public void start(View view){
      Intent intent = new Intent(MainActivity.this, LiveActivity.class);
      startActivity(intent);
      finish();
      Toast.makeText(this, "啟動完成", Toast.LENGTH_SHORT).show();
    }
    
  • 設(shè)置Activity為一個像素滓鸠,首先我們要將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>
    
  • 顯示一個像素

    @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);
    }
    

從效果圖中看出第喳,我們啟動Activity后糜俗,手機頁面不能滑動,當(dāng)點擊返回鍵的時候曲饱,又能滑動了悠抹,說明成功啟動了1px 的Activity,下面監(jiān)聽手機鎖屏事件扩淀。

封裝一個工具類楔敌,專門監(jiān)聽鎖屏事件

public class ScreenListener {

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

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

//監(jiān)聽鎖屏事件的廣播接受者
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)) {//開屏
            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)對應(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);
}

//動態(tài)注冊鎖屏廣播
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();
}
}

啟動一個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() {
            //開屏,銷毀1px Activity
            LiveActivityManager.getInstance(MyService.this).finishKeepLiveActivity();
        }

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

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

在應(yī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"/>

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

上面只是簡單的模擬了一下,但是實現(xiàn)上述功能還有一個前提就是MyService這個服務(wù)得一直存活胜臊,不然沒辦法監(jiān)聽處理了勺卢。

雙進程守護

雙進程守護,可以防止單個進程殺死象对,同時可以防止第三方的軟件清理掉黑忱。一個進程被殺死,另外一個進程又被他啟動。相互監(jiān)聽啟動杨何,因為殺進程是一個一個殺的。本質(zhì)是和殺進程時間賽跑沥邻。

  • 1.創(chuàng)建兩個Service危虱,實現(xiàn)互相監(jiān)聽

    <service android:name=".LocalService"/>
    <service
         android:name=".RemoteService"
         android:process=":remote"/> // 開啟一個新的進程
    

LocalService和RemoteService位于不同的進程中,實現(xiàn)互相的監(jiān)聽唐全,他們的代碼一樣埃跷,只不過是監(jiān)聽的對象不一樣,所以下面只貼出了一個文件的代碼邮利。

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) {
    //啟動的時候綁定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被殺死弥雹,重新啟動綁定
        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();
   //銷毀的時候要解綁
    unbindService(connection);
}
}

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

從效果圖中看出,我們關(guān)閉一個服務(wù)龄减,又會被立刻啟動起來项钮,通過右邊啟動的時間也可以看出來,如果想提高進程的優(yōu)先級希停,可以在onStartCommand()方法中調(diào)用 startForeground()將進程提升為前臺進程烁巫。這樣雖然能實現(xiàn)進程常駐,但是兩個服務(wù)一直在后臺運行宠能,是非常耗費資源的程拭,尤其是電量。那么有沒有更好一點的辦法呢棍潘?那就是下面要說的內(nèi)容了恃鞋。

Jobscheduler

Android 5.0系統(tǒng)以后,Google為了優(yōu)化Android系統(tǒng)亦歉,提高使用流暢度以及延長電池續(xù)航恤浪,加入了在應(yīng)用后臺/鎖屏?xí)r,系統(tǒng)會回收應(yīng)用肴楷,同時自動銷毀應(yīng)用拉起的Service的機制水由。同時為了滿足在特定條件下需要執(zhí)行某些任務(wù)的需求,google在全新一代操作系統(tǒng)上赛蔫,采取了Job (jobservice & JobInfo)的方式砂客,即每個需要后臺的業(yè)務(wù)處理為一個job泥张,通過系統(tǒng)管理job,來提高資源的利用率鞠值,從而提高性能媚创,節(jié)省電源。這樣又能滿足APP開發(fā)的要求彤恶,又能滿足系統(tǒng)性能的要求钞钙。

實現(xiàn)進程防殺死

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

具體實現(xiàn)

  • 派生JobService 子類术徊,定義需要執(zhí)行的任務(wù)
  • 從Context 中獲取JobScheduler 實例
  • 構(gòu)建JobInfo 實例本刽,指定 JobService任務(wù)實現(xiàn)類及其執(zhí)行條件
  • 通過JobScheduler 實例加入到任務(wù)隊列
@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)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赠涮,隨后出現(xiàn)的幾起案子盅安,更是在濱河造成了極大的恐慌,老刑警劉巖世囊,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件别瞭,死亡現(xiàn)場離奇詭異,居然都是意外死亡株憾,警方通過查閱死者的電腦和手機蝙寨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗤瞎,“玉大人墙歪,你說我怎么就攤上這事”雌妫” “怎么了虹菲?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掉瞳。 經(jīng)常有香客問我毕源,道長,這世上最難降的妖魔是什么陕习? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任霎褐,我火速辦了婚禮,結(jié)果婚禮上该镣,老公的妹妹穿的比我還像新娘追驴。我一直安慰自己沙绝,他們只是感情好陕赃,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娘纷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跋炕。 梳的紋絲不亂的頭發(fā)上赖晶,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音枣购,去河邊找鬼嬉探。 笑死擦耀,一個胖子當(dāng)著我的面吹牛棉圈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眷蜓,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼分瘾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吁系?” 一聲冷哼從身側(cè)響起德召,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汽纤,沒想到半個月后上岗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡蕴坪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年肴掷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片背传。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡呆瞻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出径玖,到底是詐尸還是另有隱情痴脾,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布梳星,位于F島的核電站赞赖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冤灾。R本人自食惡果不足惜薯定,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞳购。 院中可真熱鬧话侄,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至变丧,卻和暖如春芽狗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痒蓬。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工童擎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人攻晒。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓顾复,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鲁捏。 傳聞我的和親對象是個殘疾皇子芯砸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

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

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