由于一些需求的原因需要让自己App长时间在后台。虽然这样的做法是很Orz的,但是由于项目需求有时候不得不这样做。QQ、微信之所以没被第三方应用直接给kill掉,从市场原因腾讯的软件已经深入人心,很多厂家或是软件早就把这些软件加入白名单中!
但是作为App的开发人员来说肯定是强烈建议不要这么做,不仅仅从用户角度考虑,作为Android开发者也有责任去维护Android的生态环境。就是因为越来越多的这样的占用系统内存的进程越来越多才使得android手机越来越卡,android碎片化的原因越来越多的第三方App可以通过清理内存的方式将后台进程给直接Kill掉来增加run可用内存。
但是通过技术层面如何解决这个不被kill掉进程的问题呢?
- Service设置成START_STICKY,kill 后会被重启(等待5秒左右),重传Intent,保持与重启前一样。(PM1中提供参考)
- 通过 startForeground将进程设置为前台进程,做前台服务,优先级和前台应用一个级别,除非在系统内存非常缺,否则此进程不会被 kill
- 双进程Service:让2个进程互相保护,其中一个Service被清理后,另外没被清理的进程可以立即重启进程
- QQ黑科技:在应用退到后台后,另起一个只有 1 像素的页面停留在桌面上,让自己保持前台状态,保护自己不被后台清理工具杀死
- 在已经root的设备下,修改相应的权限文件,将App伪装成系统级的应用(Android4.0系列的一个漏洞,已经确认可行)
- Android系统中当前进程(Process)fork出来的子进程,被系统认为是两个不同的进程。当父进程被杀死的时候,子进程仍然可以存活,并不受影响。鉴于目前提到的在Android-Service层做双守护都会失败,我们可以fork出c进程,多进程守护。死循环在那检查是否还存在,具体的思路如下(Android5.0以下可行)
- 用C编写守护进程(即子进程),守护进程做的事情就是循环检查目标进程是否存在,不存在则启动它。
- 在NDK环境中将1中编写的C代码编译打包成可执行文件(BUILD_EXECUTABLE)。
- 主进程启动时将守护进程放入私有目录下,赋予可执行权限,启动它即可。
- 联系厂商,加入白名单
PM1:
for (RunningServiceInfo service :manager.getRunningServices(Integer.MAX_VALUE)) {
if("com.XXX.XXX.XXXService".equals(service.service.getClassName())){
isServiceRunning = true;
}
}
if (!isServiceRunning ) {
Intent i = new Intent(context, com.XXX.XXX.XXXService.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(i);
}
Notification notification = new Notification(R.drawable.icon, "服务开启", System.currentTimeMillis());notification.flags|= Notification.FLAG_NO_CLEAR; notification.flags=Notification.FLAG_ONGOING_EVENT;Intent notificationIntent = new Intent(this, MainActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);notification.setLatestEventInfo(this, "service", "防止服务被任务管理器所杀", pendingIntent);startForeground(ONGOING_NOTIFICATION, notification);后来一次 做自定义Notification的时候,通知栏没有显示通知,查看后发现 service 也没被kill 。所以就进一步去研究了下 最后发现 只用两行代码就能保持服务不会被kill,并且不会有通知栏通知代码如下:
|
1
2
|
Notification notification = new Notification();startForeground(1, notification); |
实例代码如下:
public class TestService extends Service { private static final Class[] mStartForegroundSignature = new Class[] { int.class, Notification.class }; private static final Class[] mStopForegroundSignature = new Class[] { boolean.class }; private NotificationManager mNM; private Method mStartForeground; private Method mStopForeground; private Object[] mStartForegroundArgs = new Object[2]; private Object[] mStopForegroundArgs = new Object[1]; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); try { mStartForeground = TestService.class.getMethod("startForeground", mStartForegroundSignature); mStopForeground = TestService.class.getMethod("stopForeground", mStopForegroundSignature); } catch (NoSuchMethodException e) { mStartForeground = mStopForeground = null; } // 我们并不需要为 notification.flags 设置 FLAG_ONGOING_EVENT,因为 // 前台服务的 notification.flags 总是默认包含了那个标志位 Notification notification =new Notification(); // 注意使用 startForeground ,id 为 0 将不会显示 notification startForegroundCompat(1, notification); } @Override public void onDestroy() { super.onDestroy(); stopForegroundCompat(1); } // 以兼容性方式开始前台服务 private void startForegroundCompat(int id, Notification n) { if (mStartForeground != null) { mStartForegroundArgs[0] = id; mStartForegroundArgs[1] = n; try { mStartForeground.invoke(this, mStartForegroundArgs); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return; } mNM.notify(id, n); } // 以兼容性方式停止前台服务 private void stopForegroundCompat(int id) { if (mStopForeground != null) { mStopForegroundArgs[0] = Boolean.TRUE; try { mStopForeground.invoke(this, mStopForegroundArgs); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return; } // 在 setForeground 之前调用 cancel,因为我们有可能在取消前台服务之后 // 的那一瞬间被kill掉。这个时候 notification 便永远不会从通知一栏移除 mNM.cancel(id); }}通过查询资料有人提到这个program:: droidwolf/NativeSubprocess · GitHub
创建 linux 子进程的 so 库,当初用在 service 免杀上,经测试,在大部分机子上有用。NativeSubprocess 是一个可以让你在 android 程序中创建 linux 子进程并执行你的 java 代码的 so 库。由于市面上典型的内存清理工具值清理 apk 包关联的进程,而不会处理 linux 原生进程。回头试试看这样是否好使。
参考文章:http://www.jb51.net/article/73976.htm
最后的最后还是要说:请不要做流氓软件!!让本来就碎片化的Android手机能运行的更顺畅。