Android 常駐進程保活自啓動方案總結

Android常駐進程就是進程一直存在,即使被系統外者其他軟件人爲殺死也能夠自啓,這種需求一般就是用來常駐接受推送消息時,如何保證進程不被殺死,實時收到推送的消息,與後臺保持着鏈接。那如何保持進程常駐呢,總結了如下方案:

##提高進程優先級以及等級
Android系統進程管理對進程分等級,當內存不足時按照等級排序從最低等級最先殺死用來回收內存,進程等級排名如下
1.前臺進程( FOREGROUND_APP)
前臺進程是目前正在屏幕上顯示的進程和一些系統進程,也就是和用戶正在交互的進程
2.可視進程(VISIBLE_APP )
可見進程指部分程序界面能夠被用戶看見,卻不在前臺與用戶交互的進程。
3.次要服務進程(SECONDARY_SERVER )
服務進程是通過 startService() 方法啓動的進程,但不屬於前臺進程和可見進程。例如,在後臺播放音樂或 者在後臺下載就是服務進程
4.後臺進程 (HIDDEN_APP)
後臺進程指的是目前對用戶不可見的進程。當點擊Home鍵讓qq界面消失的時候,這個時候它就轉換成了後臺進 程。
5.內容供應節點(CONTENT_PROVIDER)
6.空進程(EMPTY_APP)
空進程指的是在這些進程內部,沒有任何東西在運行。保留這種進程的的唯一目的是用作緩存,以縮短該應用下次在其中運行組件所需的啓動時間。
所以我們最基本的措施是先竟可能的提高等級,措施如下:
1、配置Android:priority:對於Service被系統回收,一般做法是通過提高優先級可以解決,在AndroidManifest.xml文件中對於intent-filter可以通過Android:priority = “1000”這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低。
2、生命週期中執行不可中斷任務,阻塞主線程:如果service正在調用onCreate,onStartCommand或者onDestory方法,那麼用於當前service的進程則變爲前臺進程以避免被killed。
3、bindService:如果客戶端已經連接到service
(bindService),那麼擁有Service的進程則擁有最高的優先級,可以認爲service是可見的。
4、startForeground(int, Notification):如果service可以使用startForeground(int, Notification)方法來將service設置爲前臺狀態,那麼系統就認爲是對用戶可見的,並不會在內存不足時killed。
Android7.1以下系統bug而生的黑科技-設置爲前臺進程卻不顯示Notification
具體做法就是先後啓動兩個service,並配置startForeground(int, Notification)相同的id,後一個service然後又stopself退出,消去了notification,從而使得第一個service成爲前臺進程等級。詳細原理解釋:http://blog.csdn.net/pw4work/article/details/52877980?utm_source=itdadao&utm_medium=referral

5、增強進程可見性:如果有其他的應用組件作爲Service,Activity等運行在相同的進程中,那麼將會增加該進程的重要性。例如見過一個比較狠的方式:啓動顯示出一個透明的小窗口Activity來來提高可見性

##進程自啓動方案
1、Service中onStartCommand方法return START_STICKY;
通過return START_STICKY;系統會在內容不足殺死進程之後,會重新啓動該進程。這種方式比較靠譜,作爲補充手段

2、監聽系統廣播來自啓動
通過監聽系統廣播如解鎖屏、網絡變化、安裝卸載應用等這些廣播時,判斷進程狀態,如果進程不存在,就重新啓動。

<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" > 
    <intent-filter>  
        <action android:name="android.intent.action.USER_PRESENT" /> 
        <action android:name="android.intent.action.PACKAGE_RESTARTED" /> 
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> 
    </intent-filter> 
    <intent-filter>  
                <action android:name="android.intent.action.PACKAGE_ADDED" />  
                <action android:name="android.intent.action.PACKAGE_REMOVED" />  
  
                <data android:scheme="package" />  
            </intent-filter> 
</receiver>
這種方式存在侷限於某些機型收不到廣播,或者廣播收到有要求,但大部分機型還是比較有用,作爲主力之一。

3、雙進程互相守護
同時開啓了兩個Service,分別是A和B,互相守護, 如果A守護B,當B掛掉,A就立刻把B啓動起來,所以A和B互相守護,無論誰被殺掉,對方就把它拉起來。具體通過bindService方式來互相綁定對方,分別在不同的進程中避免同時被殺死。
定義兩個Service,分別是LocalService和RemoteService,其中的RemoteService我們通過屬性配置android:process=”:remote”代碼如下:

 public class LocalService extends Service {

    private MyBinder mBinder;
    private PendingIntent mPintent;
    private MyServiceConnection mServiceConnection;

    @Override
    public void onCreate() {
        super.onCreate();
        if (mBinder == null) {
            mBinder = new MyBinder();
        }
        mServiceConnection = new MyServiceConnection();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        bindService(new Intent(this, RemoteService.class), myServiceConnection, Context.BIND_IMPORTANT);
        Notification notification = new Notification(R.drawable.ic_launcher, "", System.currentTimeMillis());
        mPintent = PendingIntent.getService(this, 0, intent, 0);
        notification.setLatestEventInfo(this, "", "自啓", pintent);
       
        startForeground(startId, notification);
        return START_STICKY;
    }

    class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
        
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            // 連接出現了異常斷開了,被殺掉了
            Toast.makeText(LocalService.this, "遠程服務Remote被幹掉", Toast.LENGTH_LONG).show();
            startService(new Intent(LocalService.this, RemoteService.class));
            bindService(new Intent(LocalService.this, RemoteService.class),
                    mServiceConnection, Context.BIND_IMPORTANT);
        }

    }

    class MyBinder extends Connection.Stub {

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

    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

}   
public class RemoteService extends Service {
    private  MyBinder mBinder;
    private PendingIntent mPintent;
    private MyServiceConnection mServiceConnection;
 
    @Override
    public void onCreate() {
        super.onCreate();
        if (myBinder == null) {
            mBinder = new MyBinder();
        }
        mServiceConnection = new MyServiceConnection();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.bindService(new Intent(this,LocalService.class), mServiceConnection, Context.BIND_IMPORTANT);
        Notification notification = new Notification(R.drawable.ic_launcher,
                "",
                System.currentTimeMillis());
        mPintent=PendingIntent.getService(this, 0, intent, 0);
        notification.setLatestEventInfo(this, "",
                "防止被殺掉!", pintent);
        startForeground(startId, notification);
        return START_STICKY;
    }

    class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
            
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Toast.makeText(RemoteCastielService.this, "本地服務Local被幹掉", Toast.LENGTH_LONG).show();
            startService(new Intent(RemoteService.this,LocalService.class));
           bindService(new Intent(RemoteService.this,LocalService.class), mServiceConnection, Context.BIND_IMPORTANT);
        }

    }

    class MyBinder extends Connection.Stub {

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

    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

}
擴展思路: 甚至可以是更多的進程之間串連方式,連成一條線互相監聽,A-B-C-D-E-A 這種環形監聽方式。

4、特定時間特定場景下檢查
直接通過設定定時任務來檢查進程是否存在,可以通過兩種方式來:
1)監聽系統時鐘廣播或AlarmManager週期性來檢查進程狀態。
2)5.0以後的Android系統,我們就可以使用JobScheduler,JobScheduler來可以調度特殊場景下啓動JobService來執行onStartJob,如設定wifi連接時候啊,JobService是Android5.0以後新增的一個服務,即可以通過JobScheduler來執行一些滿足特定條件但不緊急的後臺任務,那我們就可以設定這個任務就是定時檢查進程是否存在,後續詳細講解這種方式,詳細參考另一邊文章:http://blog.csdn.net/u010019468/article/details/72958859。

5、全家桶互拉方式
這種方案常見於百度、360等全家桶,這種互相喚醒的方式,可以參考,但不要過分哦。

6、後臺播放無聲音樂
這種方案應用比較多,且比較有效,且高版本息屏下也可以保活,循環播放會比較耗時,一般會間隔幾分鐘,但是若息屏下間隔事件要控制到小間隔,過長的話,會被回收,實際使用中可以採取lenght+xxx
這種保活的根本原因,就是把進程的優先級提高到很高了,可以查看oom_adj變化,說明下,該值越小說明優先級越大。
測試效果如下:把應用都退到後臺,無播放的情況如下圖1所示oom_adj值
無播放處於後臺
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700 解釋如下:
max是指系統最大值爲1001,優先級最低,當前值cur=700

後臺播放
此時cur=-10000 ,如上所說值越小,優先級越大

總結:上述方式沒有一個是萬能的報活方式,都會存在不適配,這種情況下就不要單獨依賴某種方式,而是綜合使用多種方式,甚至可以全部使用,來做個萬全之策,不論那種方式命中,都檢查進程狀態,而不要導致混亂。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章