進(jìn)程保活

Service

service:是一個(gè)后臺(tái)服務(wù),專門用來處理常駐后臺(tái)的工作的組件。

Service被強(qiáng)的原因:

  1. 內(nèi)存不足
  2. 三方軟件清理進(jìn)程
  3. 各大Rom廠商進(jìn)行清理工作

進(jìn)程被強(qiáng)的機(jī)制(LowmemoryKiller的工作機(jī)制)

LowmemoryKiller會(huì)在內(nèi)存不足的時(shí)候掃描所有的用戶進(jìn)程,找到不是太重要的進(jìn)程殺死。

static short lowmem_adj[6] = {
    0,
    1,
    6,
    12,
};
static int lowmem_adj_size = 4;

static int lowmem_minfree[6] = {
    3 * 512,    
    2 * 1024,    
    4 * 1024,   
    16 * 1024,   
};
static int lowmem_minfree_size = 4;
  • lowmem_adj中各項(xiàng)數(shù)值代表閾值的警戒級(jí)數(shù),lowmem_minfree代表對(duì)應(yīng)級(jí)數(shù)的剩余內(nèi)存
  • LowmemoryKiller就是根據(jù)當(dāng)前系統(tǒng)的可用內(nèi)存多少來獲取當(dāng)前的警戒級(jí)數(shù),如果進(jìn)程的oom_adj大于警戒級(jí)數(shù)并且占內(nèi)存最大,將會(huì)被優(yōu)先殺死
  • 具有相同omm_adj的進(jìn)程,則殺死占用內(nèi)存較多的

根據(jù)原理可以進(jìn)行改進(jìn)方案:

  1. 提高進(jìn)程的優(yōu)先級(jí),其實(shí)就是減小進(jìn)程的p->oomkilladj(越小越重要),如啟動(dòng)Service調(diào)用startForeground()盡量提高進(jìn)程的優(yōu)先級(jí)
  2. 當(dāng)應(yīng)用退到后臺(tái)適當(dāng)釋放資源然后降低APP的內(nèi)存占用量,因?yàn)樵趏om_adj相同的時(shí)候,會(huì)優(yōu)先干掉內(nèi)存消耗大的進(jìn)程
    3.對(duì)于要一直在后臺(tái)運(yùn)行的Service我們一定要輕

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

進(jìn)程的重要性:優(yōu)先級(jí)(越往后越容易被系統(tǒng)殺死)

1.前臺(tái)進(jìn)程:Foreground process

用戶當(dāng)前操作所必需的進(jìn)程。如果一個(gè)進(jìn)程滿足以下任一條件,即視為前臺(tái)進(jìn)程:

  1. 用戶正在交互的activity(onResume())
  2. 當(dāng)某個(gè)service綁定正在交互的activity
  3. 被主動(dòng)調(diào)用為前臺(tái)的service(setForeground())
  4. 組件在在執(zhí)行生命周期的回調(diào)(oncrete(),onStart(),OnDestroy)
  5. BroadcastReceiver正在執(zhí)行onReceive()
2、可見進(jìn)程: visible process
  1. 我們的activ處在onPause(),沒有進(jìn)入onStop()
  2. 綁定到前臺(tái)的service
3.服務(wù)進(jìn)程:service process

簡(jiǎn)單的startservice

4.后臺(tái)進(jìn)程: Backgroud process

對(duì)于用戶沒有直接影響的進(jìn)程

android:process:":"

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

不含任何活動(dòng)的組件(為了第二次啟動(dòng)更快,采用的一個(gè)權(quán)衡)

任何提升進(jìn)程的優(yōu)先級(jí)

  1. QQ采用在鎖屏的時(shí)候啟動(dòng)一個(gè)1像素的activity,當(dāng)用戶解鎖以后將這個(gè)Axtivity結(jié)束掉(順便同時(shí)把自己的核心服務(wù)在開啟一次)
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "KeepLiveActivity----onCreate!!!");
        
        Window window = getWindow();
        window.setGravity(Gravity.LEFT|Gravity.TOP);
        LayoutParams params = window.getAttributes();
        params.height = 1;
        params.width = 1;
        params.x = 0;
        params.y = 0;
        
        window.setAttributes(params);
        
        KeepLiveActivityManager.getInstance(this).setKeepLiveActivity(this);
    }

這就是1像素的布局,但是必須設(shè)置style,不然背景會(huì)是黑色的

<style name="KeepLiveStyle">
        <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>

開鎖管理類

public class KeepLiveActivityManager {
    private static KeepLiveActivityManager instance;
    private Context context;
    private WeakReference<Activity> activityInstance;

    public static KeepLiveActivityManager getInstance(Context context) {
        if(instance==null){
            instance = new KeepLiveActivityManager(context.getApplicationContext());
        }
        return instance;
    }
    
    private KeepLiveActivityManager(Context context) {
        this.context = context;
    }
    
    public void setKeepLiveActivity(Activity activity){
        activityInstance = new WeakReference<Activity>(activity);
    }

    public void startKeepLiveActivity() {
        Intent intent = new  Intent(context, KeepLiveActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }
    public void finishKeepLiveActivity() {
        if(activityInstance!=null&&activityInstance.get()!=null){
            Activity activity = activityInstance.get();
            activity.finish();
        }
    }
}

監(jiān)聽鎖屏

Intent intent = new Intent(this, MyService.class);
startService(intent);
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.begin(new ScreenStateListener() {
            
            @Override
            public void onUserPresent() {
            }
            
            @Override
            public void onScreenOn() {
                // 開屏---finish這個(gè)一個(gè)像素的Activity
                KeepLiveActivityManager.getInstance(MyService.this).finishKeepLiveActivity();
            }
            
            @Override
            public void onScreenOff() {
                // 鎖屏---啟動(dòng)一個(gè)像素的Activity
                KeepLiveActivityManager.getInstance(MyService.this).startKeepLiveActivity();
            }
        });
    }
}

權(quán)限設(shè)置和androidmainfast

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

<service android:name="com.ricky.keepliveprocess.onepixel.MyService"></service>

監(jiān)聽事件

public class ScreenListener {
    private Context mContext;
    private ScreenBroadcastReceiver mScreenReceiver;
    private ScreenStateListener mScreenStateListener;

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

    /**
     * screen狀態(tài)廣播接收者
     */
    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();
            }
        }
    }

    /**
     * 開始監(jiān)聽screen狀態(tài)
     * 
     * @param listener
     */
    public void begin(ScreenStateListener listener) {
        mScreenStateListener = listener;
        registerListener();
        getScreenState();
    }

    /**
     * 獲取screen狀態(tài)
     */
    @SuppressLint("NewApi")
    private void getScreenState() {
        PowerManager manager = (PowerManager) mContext
                .getSystemService(Context.POWER_SERVICE);
        if (manager.isInteractive()) {
            if (mScreenStateListener != null) {
                mScreenStateListener.onScreenOn();
            }
        } else {
            if (mScreenStateListener != null) {
                mScreenStateListener.onScreenOff();
            }
        }
    }

    /**
     * 停止screen狀態(tài)監(jiān)聽
     */
    public void unregisterListener() {
        mContext.unregisterReceiver(mScreenReceiver);
    }

    /**
     * 啟動(dòng)screen狀態(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);
    }

    public interface ScreenStateListener {// 返回給調(diào)用者屏幕狀態(tài)信息
        public void onScreenOn();

        public void onScreenOff();

        public void onUserPresent();
    }
}
  1. app運(yùn)營(yíng)商和手機(jī)廠商可能有合作關(guān)系---白名單
  2. 雙進(jìn)程守護(hù)---可以防止單個(gè)進(jìn)程殺死,同時(shí)可以防止第三方的360清理掉。
  • 一個(gè)進(jìn)程被殺死,另外一個(gè)進(jìn)程又被他啟動(dòng)。相互監(jiān)聽啟動(dòng)。
  • 殺進(jìn)程是一個(gè)一個(gè)殺的。本質(zhì)是和殺進(jìn)程時(shí)間賽跑。

使用IPC進(jìn)程間通信adil

package com.dn.keepliveprocess;
interface RemoteConnection{
    String getProcessName();
}

兩個(gè)進(jìn)程

  • 本地進(jìn)程
public class LocalService extends Service {

    public static final String TAG = "LocalService";
    private MyBinder binder;
    private MyServiceConnection conn;

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        if(binder ==null){
            binder = new MyBinder();
        }
        conn = new MyServiceConnection();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
    
            // 把service設(shè)置為前臺(tái)運(yùn)行,避免手機(jī)系統(tǒng)自動(dòng)殺掉改服務(wù)。
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                startForeground(Util.NOTIFICATION_ID, new Notification());
            } else {
                // API 18以上,發(fā)送Notification并將其置為前臺(tái)后,啟動(dòng)InnerService
                startForeground(Util.NOTIFICATION_ID, new Notification());
                startService(new Intent(this, InnerService.class));
            }

        return START_STICKY;
    }
    

    class MyBinder extends RemoteConnection.Stub{

        @Override
        public String getProcessName() throws RemoteException {
            return "LocalService";
        }
        
    }
    
    class MyServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "建立連接成功!");  
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "RemoteService服務(wù)被干掉了~~~~斷開連接!");
            Toast.makeText(LocalService.this, "斷開連接", 0).show();
            //啟動(dòng)被干掉的
            LocalService.this.startService(new Intent(LocalService.this, RemoteService.class));
            LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
        }   
    }
}

第二個(gè)進(jìn)程RemoteService基本上都是一樣的只是

@Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "LocalService服務(wù)被干掉了~~~~斷開連接!");
            //Toast.makeText(RemoteService.this, "斷開連接", 0).show();
            //啟動(dòng)被干掉的
            RemoteService.this.startService(new Intent(RemoteService.this, LocalService.class));
            RemoteService.this.bindService(new Intent(RemoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT);
        }

這個(gè)不同

其中有一個(gè)問題是出現(xiàn)通知欄,用戶會(huì)被發(fā)現(xiàn),使用可以開啟一個(gè)服務(wù)去關(guān)閉通知欄

public class InnerService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
   
        startForeground(Util.NOTIFICATION_ID, new Notification());
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                stopForeground(true);
                NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                manager.cancel(Util.NOTIFICATION_ID);
                stopSelf();
            }
        }, 100);
    }

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

使用調(diào)用

startService(new Intent(this, LocalService.class));
startService(new Intent(this, RemoteService.class));

設(shè)置權(quán)限和mainfast

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

<service android:name="com.dn.keepliveprocess.LocalService"/>
<service android:name="com.dn.keepliveprocess.InnerService"/>
<service 
    android:name="com.dn.keepliveprocess.RemoteService"
    android:process=":remoteprocess"/>
  1. JobScheduler

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

調(diào)用方法:

<service android:name="com.dn.keepliveprocess.JobHandleService" android:permission="android.permission.BIND_JOB_SERVICE"></service>

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
            startService(new Intent(this, JobHandleService.class));
}

JobScheduler類

public class JobHandleService extends JobService{
    private int kJobId = 0;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("INFO", "jobService create"); 
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("INFO", "jobService start");
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
    
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i("INFO", "job start");
        boolean isLocalServiceWork = isServiceWork(this, "com.dn.keepliveprocess.LocalService");
        boolean isRemoteServiceWork = isServiceWork(this, "com.dn.keepliveprocess.RemoteService");
        if(!isLocalServiceWork||
           !isRemoteServiceWork){
            this.startService(new Intent(this,LocalService.class));
            this.startService(new Intent(this,RemoteService.class));
            Toast.makeText(this, "process start", Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i("INFO", "job stop");
        scheduleJob(getJobInfo());
        return true;
    }

    /** Send job to the JobScheduler. */
    public void scheduleJob(JobInfo t) {
        Log.i("INFO", "Scheduling job");
        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);//間隔時(shí)間--周期
        return builder.build();
    }
    
    
    /** 
     * 判斷某個(gè)服務(wù)是否正在運(yùn)行的方法 
     *  
     * @param mContext 
     * @param serviceName 
     *            是包名+服務(wù)的類名(例如:net.loonggg.testbackstage.TestService) 
     * @return true代表正在運(yùn)行,false代表服務(wù)沒有正在運(yùn)行 
     */  
    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;  
    }  
}

  1. 監(jiān)聽QQ,微信,系統(tǒng)應(yīng)用,友盟,小米推送等等的廣播,然后把自己?jiǎn)?dòng)了。
  2. 利用賬號(hào)同步機(jī)制喚醒我們的進(jìn)程。AccountManager
  3. NDK來解決,Native進(jìn)程來實(shí)現(xiàn)雙進(jìn)程守護(hù)。
最后編輯于
?著作權(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)容