在系统内存不足的情况下,系统会依据自身的一套进程回收机制来回收掉一些进程,以腾出内存来供给需要的app,这套进程回收机制就叫做Low Memory Killer。然而某些应用需要在后台常驻以满足一些需求,比如后台接收推送消息等。那么如何保持进程常驻呢,总结了如下方案:
1.利用Android前台Service的漏洞,之前看Tinker源码发现也是使用这种方式。
具体步骤:对于Api < 18,调用startForeground(id , new Notification()),开启一个空的Notification,再通知栏不会显示。
对于Api > 18,在需要提高优先级的Service中启动一个InnerService,两个服务同时调用startForeground,并且使用同一个id。然后stop掉InnerService。
代码如下:
public class KeepLiveService extends Service {
public static final int NOTIFICATION_ID=100;
public KeepLiveService() {
}
@Override
public IBinder onBind(Intent intent) {
}
@Override
public void onCreate() {
super.onCreate();
//API 18以下,直接发送Notification并将其置为前台
if (Build.VERSION.SDK_INT <Build.VERSION_CODES.JELLY_BEAN_MR2) {
startForeground(NOTIFICATION_ID, new Notification());
} else {
//API 18以上,发送Notification并将其置为前台后,启动InnerService
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
startService(new Intent(this, InnerService.class));
}
}
public static class InnerService extends Service{
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//发送与KeepLiveService中ID相同的Notification,然后将其取消并取消自己的前台显示
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
stopForeground(true);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
stopSelf();
}
},100);
}
}
}
2.开启一像素的Activity,据说这是手Q的进程保活方案。该方案的思路是在锁屏时候开启一个大小为1像素的Activity,并且透明无切换动画,在开屏时关掉这个Activity,由于是透明无切换动画,所以能不让用户察觉。
代码如下:
public class SinglePixelActivity extends Activity {
public static final String TAG = SinglePixelActivity.class.getSimpleName();
public static void actionToSinglePixelActivity(Context pContext) {
Intent intent = new Intent(pContext, SinglePixelActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
pContext.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singlepixel);
Window window = getWindow();
//放在左上角
window.setGravity(Gravity.START | Gravity.TOP);
WindowManager.LayoutParams attributes = window.getAttributes();
//宽高设计为1个像素
attributes.width = 1;
attributes.height = 1;
//起始坐标
attributes.x = 0;
attributes.y = 0;
window.setAttributes(attributes);
ScreenManager.getInstance(this).setActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
public class ScreenManager {
private Context mContext;
private WeakReference<Activity> mActivityWref;
public static ScreenManager gDefualt;
public static ScreenManager getInstance(Context pContext) {
if (gDefualt == null) {
gDefualt = new ScreenManager(pContext.getApplicationContext());
}
return gDefualt;
}
private ScreenManager(Context pContext) {
this.mContext = pContext;
}
public void setActivity(Activity pActivity) {
mActivityWref = new WeakReference<Activity>(pActivity);
}
public void startActivity() {
SinglePixelActivity.actionToSinglePixelActivity(mContext);
}
public void finishActivity() {
//结束掉SinglePixelActivity
if (mActivityWref != null) {
Activity activity = mActivityWref.get();
if (activity != null) {
activity.finish();
}
}
}
}
3.双进程互相守护
同时开启两个Service,分别是A和B,互相守护,如果B挂掉了,A就立刻把B启动起来,所以A和B互相守护,无论谁被杀掉,对方就把它拉起来。具体方式是通过bindServie方式来互相绑定对方,分别在不同的进程中避免同时被杀死。
首先定义两个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;
}
}