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方式
進程被殺死喚醒
待填