Service
service:是一個(gè)后臺(tái)服務(wù),專門用來處理常駐后臺(tái)的工作的組件。
Service被強(qiáng)的原因:
- 內(nèi)存不足
- 三方軟件清理進(jìn)程
- 各大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)方案:
- 提高進(jìn)程的優(yōu)先級(jí),其實(shí)就是減小進(jìn)程的p->oomkilladj(越小越重要),如啟動(dòng)Service調(diào)用startForeground()盡量提高進(jìn)程的優(yōu)先級(jí)
- 當(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)程:
- 用戶正在交互的activity(onResume())
- 當(dāng)某個(gè)service綁定正在交互的activity
- 被主動(dòng)調(diào)用為前臺(tái)的service(setForeground())
- 組件在在執(zhí)行生命周期的回調(diào)(oncrete(),onStart(),OnDestroy)
- BroadcastReceiver正在執(zhí)行onReceive()
2、可見進(jìn)程: visible process
- 我們的activ處在onPause(),沒有進(jìn)入onStop()
- 綁定到前臺(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í)
- 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();
}
}
- app運(yùn)營(yíng)商和手機(jī)廠商可能有合作關(guān)系---白名單
- 雙進(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"/>
- 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;
}
}
- 監(jiān)聽QQ,微信,系統(tǒng)應(yīng)用,友盟,小米推送等等的廣播,然后把自己?jiǎn)?dòng)了。
- 利用賬號(hào)同步機(jī)制喚醒我們的進(jìn)程。AccountManager
- NDK來解決,Native進(jìn)程來實(shí)現(xiàn)雙進(jìn)程守護(hù)。