1.应用程序的优先级
为了提高用户的体验,安卓系统采用了一让应用“尽可能”保留在内存中的机制,这样有助于下次启动时减少加载应用的时间。
但是这种设计导致的问题就是内存常常处于吃紧的状态,为此安卓系统定义了5个进程等级,当内存不足的情况下按照5个优先级的不同来决定到底先杀哪个进程。
如下就是应用的进程等级:
前台进程;Foreground process
1)用户正在交互的Activity(onResume()) 2)当某个Service绑定正在交互的Activity。 3)被主动调用为前台Service(startForeground()) 4)组件正在执行生命周期的回调(onCreate()/onStart()/onDestroy()) 5)BroadcastReceiver 正在执行onReceive();
可见进程;Visible process,我们的Activity处在onPause()(没有进入onStop())
服务进程;Service process,简单的startService()启动。
- 后台进程;Background process,对用户没有直接影响的进程—-Activity出于onStop()的时候。
- 空进程; Empty process,不含有任何的活动的组件。(android设计的,为了第二次启动更快,采取的一个权衡)
2.如何提升应用的优先级
上面我们介绍了5个等级优先级,那么如何让你的应用长期驻留在内存中呢,我们可以让应用提高他所在的等级。
比如,在手机屏幕锁屏的时候,经常会将我们的APP杀死(为了省电)。QQ采取在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉(顺便同时把自己的核心服务再开启一次)。
开发步骤:
创建一个Activity:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //1.去掉布局 //setContentView(R.layout.activity_keep_alive); //2.设置界面只有一个像素 Window window = getWindow(); window.setGravity(Gravity.LEFT|Gravity.TOP); WindowManager.LayoutParams params=window.getAttributes(); params.width=1; params.height=1; window.setAttributes(params); //保存当前创建的Activity以便我们在屏幕解锁后杀掉 ActivityManager.getInstance().setKeepAliveActivity(this); Log.i("IT520","KeepAliveActivity onCreate()"); }
当前的Activity在启动的时候会产生黑屏,主要是我们没有设置好该界面的样式,接下来我们需要创建样式并应用在AndroidManifest.xml中:
<style name="keppalive_style"> <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>
启动服务在后台监听屏幕的锁屏解锁状态,服务里面创建了一个我们自定义的监听器(看第4步),可以帮助我们在锁屏的时候启动1px界面,并在解锁后关闭1px界面:
public class KeepAliveService extends Service { private ScreenListener mScreenListener; @Override public void onCreate() { super.onCreate(); mScreenListener = new ScreenListener(this.getApplicationContext()); mScreenListener.begin(new ScreenListener.ScreenStateListener() { @Override public void onScreenOn() {//解锁 //关闭界面 ActivityManager.getInstance().finishActivity(); } @Override public void onScreenOff() {//锁屏 //启动界面 ActivityManager.getInstance().startActivity(KeepAliveService.this); } @Override public void onUserPresent() { } }); } @Override public IBinder onBind(Intent intent) { throw null; } }
监听器内部实现了一个屏幕广播接收者,并在调用begin()时通过动态注册广播接收者来达到监听目的:
public class ScreenListener { private Context mContext; private ScreenBroadcastReceiver mScreenReceiver; private ScreenStateListener mScreenStateListener; public ScreenListener(Context context) { mContext = context; mScreenReceiver = new ScreenBroadcastReceiver(); } 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(); } } } public void begin(ScreenStateListener listener) { mScreenStateListener = listener; registerListener(); getScreenState(); } private void getScreenState() { PowerManager manager = (PowerManager) mContext .getSystemService(Context.POWER_SERVICE); if (manager.isScreenOn()) { if (mScreenStateListener != null) { mScreenStateListener.onScreenOn(); } } else { if (mScreenStateListener != null) { mScreenStateListener.onScreenOff(); } } } public void unregisterListener() { mContext.unregisterReceiver(mScreenReceiver); } 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 { public void onScreenOn(); public void onScreenOff(); public void onUserPresent(); }
}
3.其他可行的防应用进程杀死技术
所有的技术都不能百分百的方式应用进程被干掉,但可以大大提升应用的存活率。下面列举了一些其他可行的方案:
- app运营商和手机厂商可能有合作关系—白名单。
- 双进程守护—可以防止单个进程杀死,同时可以防止第三方的360清理掉。
- JobScheduler把任务加到系统调度队列中,当到达任务窗口期的时候就会执行,我们可以在这个任务里面启动我们的进程。这样可以做到将近杀不死的进程。
- 监听系统级广播,然后把自己启动了。
- NDK来解决,Native进程来实现双进程守护。
4.双进程服务在Java层的实现
技术的原理就是创建一个服务,每个服务拥有自己的一个进程,当一个进程中的服务被杀掉后,另一个进程服务重新启动原进程服务。
该技术能够达到的期望:
- 作为前台进程,提升了优先等级。
- 防止用户在设置界面杀死某个进程服务和停止应用。
- 防止360,鲁大师等手机优化软件杀死进程。
开发步骤:
创建一个本地服务和远程服务(另一个进程):
<service android:name=".LocalService" /> <service android:name=".RemoteService" android:process=":remoteprocess" />
在应用启动的时候启动两个服务:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService(new Intent(this,LocalService.class)); startService(new Intent(this,RemoteService.class)); } }
在服务创建的时候我们要做3件事如下,因为服务是跨进程的服务所以需要用到aidl。这2个服务的代码差不多,这里贴出其中一个:
- 在创建时绑定另一个进程的服务
- 在启动后设置当前服务为前台服务
- 在另一个进程服务被断开时重新启动服务。
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.i("IT520","RemoteService onCreate。。。");
Intent intent=new Intent(this,LocalService.class);
bindService(intent,new RemoteServiceConnection(),BIND_IMPORTANT);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("重要的远程服务。。。");
builder.setTicker("重要的远程服务。。。");
builder.setContentIntent(PendingIntent.getService(this,0,intent,0));
builder.setAutoCancel(true);
startForeground(startId,builder.build());
//如果系统不小心杀掉该服务,系统会稍后尝试重启
return START_STICKY;
}
private class ServiceAgent extends RemoteConnection.Stub{
@Override
public String getConnectionName() throws RemoteException {
return "RemoteService";
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ServiceAgent();
}
private class RemoteServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("IT520","LocalService 开始连接。。。");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("IT520","LocalService 断开连接。。。");
//LocalService已经被断开了 这里再次连接
Intent intent=new Intent(RemoteService.this,LocalService.class);
startService(intent);
bindService(intent,new RemoteServiceConnection(),BIND_IMPORTANT);
}
}
}
5.利用JobScheduler重启服务
上面的例子在用户设置界面中强制停止后就不会再启动了,不过我们可以通过系统5.0后提供的JobScheduler来重新启动服务。
开发步骤
创建JobService服务
/** * 该API只能应用在Android5.0之后,如果想在5.0之前实现可以使用AlarmManager(不太理想) */ @SuppressLint("NewApi") public class JobHandleService extends JobService{ private int kJobId = 0; ... @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("INFO", "jobService start"); scheduleJob(getJobInfo()); return START_NOT_STICKY; } @Override public boolean onStartJob(JobParameters params) { //主要的任务在这里写... return true; } @Override public boolean onStopJob(JobParameters params) { 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);//每10ms执行1次任务 这里真是项目可以将时间长度放大 return builder.build(); } /** * check whether service is work */ 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; }
}
在任务中监听之前创建的2个服务包。
@Override public boolean onStartJob(JobParameters params) { Log.i("INFO", "job start"); boolean isLocalServiceWork = isServiceWork(this, "com.a520it.keepalivebyservicedemo.LocalService"); boolean isRemoteServiceWork = isServiceWork(this, "com.a520it.keepalivebyservicedemo.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; }
配置服务
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <service android:name=".JobHandleService" android:permission="android.permission.BIND_JOB_SERVICE" />
示例图