Android進程與進程保活(涉及Notification)

Android進程與進程保活(涉及Notification)

由於內存限制,android系統會在內存不足時回收進程,
* 一、進程回收順序:*
優先級:前臺進程>可視進程>服務進程>後臺進程>內容供應根節點>空進程
oom_adj越大 越可能被回收
系統進程 <0 前臺進程0 可見進程1
進程級別參考:https://juejin.im/entry/58acf391ac502e007e9a0a11


1、Foreground process 前臺進程

下面幾種情況屬於前臺進程:
(1)Activity正在與用戶進程交互(Activity的onResume已經被調用)
(2)與正在和用戶交互的Activity綁定的Service
(3)Service運行在前臺——Service中調用了startForeground函數
(4)Service正在執行生命週期回調函數(onCreate,onStart,onDestory)
(5)BroadcastReceiver正在執行onReceive方法

2、Visible process 可視進程

下面幾種情況屬於可視進程:
(1)Activity沒有運行在前臺,但是用戶仍然可見(它的onPause方法被調用),例如:當前臺Activity啓動了一個Dialog,這樣Dialog運行在前臺,Activity仍然可見,屬於可視進程。
(2)與一個可視的Activity綁定的服務所在的進程

3、Service process 服務進程
運行服務的進程被startService()啓動,並且沒有進入上面1中(3)、(4)這兩種情況。例如,音樂播放、網絡下載數據

4、Background process 後臺進程
當Activity不可見的時候,它的進程屬於後臺進程(Activity的onStop方法被調用)

5、Empty process 空進程
沒有包含活動應用組件的進程爲空進程,也就是進程的應用組件已經運行完畢。


查看某個包的進程
Terminal 或者 cmd 進入 adb shell 模式
ps|grep 包名
第一個參數:u0_a124 當前用戶
第二個參數:進程ID
第三個參數: 進程的父進程ID
第四個參數:進程的虛擬內存大小
第五個參數:實際內存大小
最後一個參數:進程名

查看某個進程的進程優先級(oom_adj)
cat /proc/進程id/oom_adj
(注意 cat後要加空格)
permission denied
要root

檢查Service是否開啓在前臺
1 用 cat /proc/進程名/包名
查看開啓前後的進程優先級變化 優先級降低了並且爲0即爲前臺進程
2 或者
dumpsys activity services PackageName

查看 services 找到相應的services名
發現 isForeground=true 即爲前臺進程


進程保活常見方式

一、白色手段
開啓前臺Service,會在通知欄顯示
通過notification方式 如音樂播放

如果希望從前臺移除這個服務,只需要調用stopForeground(),一般情況我們只需要在onStartCommand裏面調用 startForeground,然後再onDestroy裏面調用stopForeground即可。

public class WhiteService extends Service {
    private static final String TAG = WhiteService.class.getSimpleName();
    private static final int NOTIFICATION_FLAG =0X11;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        // 在Android進行通知處理,首先需要重系統哪裏獲得通知管理器NotificationManager,它是一個系統Service。
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // 設置點擊通知跳轉的Intent
        Intent nfIntent = new Intent(this, MainActivity.class);
        // 設置 延遲Intent 
        // 最後一個參數可以爲PendingIntent.FLAG_CANCEL_CURRENT 或者 PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, nfIntent, 0);

        //構建一個Notification構造器
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext());

        builder.setContentIntent(pendingIntent)   // 設置點擊跳轉界面
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
                        R.mipmap.fight_total_money2x)) // 設置下拉列表中的圖標(大圖標)
                .setTicker("您有一個notification")// statusBar上的提示
                .setContentTitle("這是標題") // 設置下拉列表裏的標題
                .setSmallIcon(R.mipmap.fight_total_order2x) // 設置狀態欄內的小圖標24X24
                .setContentText("這是內容") // 設置詳細內容
                .setContentIntent(pendingIntent) // 設置點擊跳轉的界面
                .setWhen(System.currentTimeMillis()); // 設置該通知發生的時間
                .setDefaults(Notification.DEFAULT_VIBRATE) //默認震動方式
                .setPriority(Notification.PRIORITY_HIGH)   //優先級高

        Notification notification = builder.build(); // 獲取構建好的Notification

        notification.defaults = Notification.DEFAULT_SOUND; //設置爲默認的聲音
        notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明當通知被用戶點擊時,通知將被清除。
        notification.flags |= FLAG_ONGOING_EVENT; //將此通知放到通知欄的"Ongoing"即"正在運行"組中
        notification.flags |= FLAG_NO_CLEAR; //表明在點擊了通知欄中的"清除通知"後,此通知不清除,常與FLAG_ONGOING_EVENT一起使用


        manager.notify(NOTIFICATION_FLAG, notification);
        // 啓動前臺服務
        // 參數一:唯一的通知標識;參數二:通知消息。
        startForeground(NOTIFICATION_FLAG, notification);// 開始前臺服務

        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止前臺服務--參數:表示是否移除之前的通知
        stopForeground(true);
        Log.d(TAG, "onDestroy");
    }

}

開啓服務:

Intent intent = new Intent(MainActivity.this,WhiteService.class);
        startService(intent);// (服務與開啓者無聯繫的啓動形式  )

結束服務:

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                stopService(intent);
            }
        });

更新通知:
重新創建notification.builder和notification
notificationManager調用 notify方法
其實就是再創建一個notification 只是標誌和之前的那個一樣 這樣就會更新前面的通知了


二、灰色保活
也是開啓前臺Service,但是不會在通知欄顯示
adb shell 模式下:
dumpsys activity services PackageName
若Service有 isForeground=true 而通知欄卻沒有顯示 則是灰色保活方式

方式:
API < 18,啓動前臺Service時直接傳入空的 new Notification();
API >= 18,在需要提優先級的service A啓動一個InnerService,兩個服務同時startForeground,且綁定同樣的 ID。Stop 掉InnerService ,這樣通知欄圖標即被移除

public class GrayService extends Service {
    private static final String TAG = GrayService.class.getSimpleName();
    private final static int GRAY_SERVICE_ID = 0x12;

    public GrayService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //API 18以下,直接發送Notification並將其置爲前臺
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            startForeground(GRAY_SERVICE_ID, new Notification());
        } else {
            //API 18以上,發送Notification並將其置爲前臺後,啓動InnerService
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.fight_total_money2x);
            Notification notification = builder.build(); // 獲取構建好的Notification

            startForeground(GRAY_SERVICE_ID, notification);

            startService(new Intent(this, GrayInnerService.class));
        }
        Log.d(TAG, "GrayServiceOnCreate");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止前臺服務--參數:表示是否移除之前的通知
        stopForeground(true);
        Log.d(TAG, "onDestroy");
    }
}
public class GrayInnerService extends Service {
    private static final String TAG = GrayInnerService.class.getSimpleName();
    public static final int GRAY_INNER_SERVICE_ID=0x12;
    public GrayInnerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //發送與GrayService中ID相同的Notification,然後將其取消並取消自己的前臺顯示
        Notification.Builder builder = new Notification.Builder(this);
        builder.setSmallIcon(R.mipmap.fight_total_order2x);
        Notification notification = builder.build(); // 獲取構建好的Notification

        startForeground(GRAY_INNER_SERVICE_ID,notification);

        // 延遲0.1s 終止掉innerService 這樣 通知欄圖標會清除
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                stopForeground(true);
                NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                if (manager != null) {
                    manager.cancel(GRAY_INNER_SERVICE_ID);
                }else {
                    Log.e(TAG, "notification is null!");
                }
                stopSelf();
            }
        },100);
        Log.d(TAG, "GrayInnerServiceOnCreate");
    }
}

三、黑色手段
利用不同的app進程使用廣播來進行相互喚醒
(1)開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app
最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍視頻),CONNECTIVITY_ACTION(網絡切換)等三種廣播
(2)接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶
(3)app互相喚醒


四、白名單方式
系統給app加入白名單,系統不會殺死白名單中的app


五、開啓一個像素透明的Activity方式


進程被殺死喚醒
待填

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