如何让我们的Android应用进程保活?

来源:互联网 发布:task host windows 编辑:程序博客网 时间:2024/05/01 21:28

一、线程 进程 应用

  1. 默认下,同一个应用的所有组件都运行在同一个进程中。当然也可以在manifest清单文件中设置组件运行的进程。

    组件元素 activityservicereceiverprovider,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者设置进程同名与其他一些组件共享一个进程。

  2. Android会在某些时刻决定关闭一个进程,比如内存剩余较小并且其他进程更迫切需要内存时,某些进程会被关闭时,且进程中的组件们都被销毁.如果重新需要这些组件工作时,进程又会被创建出来。

  3. 系统不会为每个组件的实例分别创建线程。所有运行于一个进程的组件都在主线程(UI线程)中被实例化,并且系统对每个组件的调用都在这个线程中派发,且只能在UI线程中管理的你的界面。

  4. 可以 线程 进程 应用 打个比方:

    线程(Thread):流水线 、 进程(process): 车间 、 应用:工厂

  5. 为什么使用多线程?

    线程如同一条流水线,当我们执行一些比如网络连接或数据库请求这样的耗时操作,会造成该线程阻塞。而我们又需要在主线程(UI线程)中对整个界面进行响应。所以为了不阻塞UI线程,提高应用程序的性能,需要使用多线程来操作。

  6. 为什么使用多进程?

    在Android系统中,每个进程都有一个内存限制。如果一个应用可以多有个进程,那么这个应用可以有更多的内存来运行,使我们的应用内存限制变大,优化程序的速度。

二、进程的等级(进程的生命周期)

一. 进程按照优先级分为不同的等级:

  1. 前台进程(Foreground process):

    A. 拥有用户正在交互的 Activity( onResume()状态)B. 正在与bound绑定服务交互的 ActivityC. 正在“前台”运行的 Service(startForeground()被调用)D. 生命周期函数正在被执行的 Service(onCreate()、onStart() 或 onDestroy())E. 正执行 onReceive() 方法的 BroadcastReceiver    (该进程优先级别最高,杀死前台进程需要用户的响应。)
  2. 可见进程(Visible process):

    该进程并不是在最前端,并没有得到焦点,但是我们却能看到它们。

    A. 拥有不在前台、但仍对用户可见的 Activity(例如弹出一个对话框的Activity)B. 绑定到可见进程(或前台进程)中的Activity 的 Service
  3. 服务进程(Service process):

    A. 正在运行的通过 startService() 启动的,且不属于上述两个更高进程状态的Service
  4. 后台进程(Background process):

    A. 不可见状态的Activity进程
  5. 空进程(Empty process):

    A. 没有运行任何应用组件的进程,保留这个进程主要是为了缓存的需要,待下次相关组件运行时直接从内存中获取数据,缩短对数据获取的时间。
  6. 进程等级判断原则:当存在多种等级的进程状态时,优先考虑优先级较高的进程。

三、进程回收机制:Low Memory Killer

  1. 在Android系统,当用户退出应用程序之后,应用程序的进程还是会存在于系统中,这样方便于程序的再次启动。

  2. 但随着打开的应用程序数量的增加,系统内存会变得不足,就需要杀掉一部分应用程序的进程以释放内存空间。

  3. 至于是否需要杀死哪些进程需要被杀死,是通过Low Memory Killer机制来进行判定的。

  4. Low Memory Killer是通过进程的oom_adj与占用内存的大小决定要杀死的进程,oom_adj越小越不容易被杀死。

  5. 有一组系统内存临界值和与之一一对应的一组oom_adj值,当系统剩余内存位于内存临界值中的一个范围内时,如果一个进程的oom_adj值大于/等于这个临界值对应的oom_adj值,该进程就会被杀掉。

四、防止进程被杀

  1. 轻量化进程:按照Low Memory Killer的杀死进程的规则,应尽量让Service在后台做较少的事情,且及时释放内存资源。

  2. 利用service提升进程权限: 调用 startForeground方法将service置为“前台进程”,不过这样我们需要发送一个Notification通知。

    如何取消通知:    1. 在Android 4.3之前,可以通过构造一个空的Notification使通知栏不会显示我们发送的Notification。    2. 在Android 4.3之后,谷歌不再允许构造空的Notification。但有一个奇葩的方法:        可以将两个同进程的Service都通过startForeground设置为前台进程,但它们使用的是同一个ID的Notification通知,这样只会产生一个Notification。然后其中一个Service取消前台通知,那么该通知会被关闭。这样剩下的Service还是一个前台Service,且通知栏没有通知。
  3. 在application标签中加入 android:persistent=“true”

    在androidmanifest.xml中的application标签中加入android:persistent="true" 属性后能够达到保证该应用程序所在进程不会被LMK杀死,异常出现后也可以自启。但前提是应用程序必须是系统应用,即应用程序不能采用通常的安装方式。必须将应用程序的apk包直接放到/system/app目录下。而且必须重启系统后才能生效。

五、被杀后重启

  1. 利用Service机制重启:

    安卓系统设计Service时,可以通过其onStartCommand方法中返回不同的值告知系统,让系统在Service因为内存不足被杀掉后可以在资源不紧张时重启。

    START_NOT_STICKY如果系统在onStartCommand()方法返回之后杀死这个服务,那么直到接受到新的Intent对象,这个服务才会被重新创建。START_STICKY如果系统在onStartCommand()返回后杀死了这个服务,系统就会重新创建这个服务并且调用onStartCommand()方法,但是它不会重新传递最后的Intent对象,系统会用一个null的Intent对象来调用onStartCommand()方法。在这个情况下,除非有一些被发送的Intent对象在等待启动服务。这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来。START_REDELIVER_INTENT如果系统在onStartCommand()方法返回后,系统就会重新创建了这个服务,并且用发送给这个服务的最后的Intent对象调用了onStartCommand()方法。任意等待中的Intent对象会依次被发送。这适用于那些应该立即恢复正在执行的工作的服务,如下载文件。

    虽然在这种情况可以让Service被系统重启的,但不能立即被重启。而且在某些定制ROM上失效。

  2. 双进程守护
    设计AB两个不同进程,A进程里面轮询检查B进程是否存活,没存活的话将其拉起,同样B进程里面轮询检查A进程是否存活,没存活的话也将其拉起,而后台逻辑可以随便放在某个进程里执行即可。

    <service         android:name=".services.FirService"         android:process=":fir" /><service        android:name=".services.SecService"        android:process=":sec" />

    使用两个进程分别装载两个Service,两个Service相互轮询唤醒:

    public class FirService extends Service {    public final static String TAG = "com.example.servicedemo.FirService";    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.e(TAG, "onStartCommand");        thread.start();        return START_STICKY;    }    Thread thread = new Thread(new Runnable() {        @Override        public void run() {            Timer timer = new Timer();            TimerTask task = new TimerTask() {                @Override                public void run() {                    Log.e(TAG, "FirService Run: "+System.currentTimeMillis());                    boolean b = Util.isServiceWorked(FirService.this, "com.example.servicedemo.SecService");                    if(!b) {                        Intent service = new Intent(FirService.this, SecService.class);                        startService(service);                        Log.e(TAG, "Start SecService");                    }                }            };            timer.schedule(task, 0, 1000);        }    });    @Override    public IBinder onBind(Intent arg0) {        return null;    }}--------------------------------------public class SecService extends Service {    public final static String TAG = "com.example.servicedemo.SecService";    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.e(TAG, "onStartCommand");        thread.start();        return START_REDELIVER_INTENT;    }    Thread thread = new Thread(new Runnable() {        @Override        public void run() {            Timer timer = new Timer();            TimerTask task = new TimerTask() {                @Override                public void run() {                    Log.e(TAG, "SecService Run: " + System.currentTimeMillis());                    boolean b = Util.isServiceWorked(SecService.this, "com.example.servicedemo.FirService");                    if(!b) {                        Intent service = new Intent(ServiceTwo.this, FirService.class);                        startService(service);                    }                }            };            timer.schedule(task, 0, 1000);        }    });    @Override    public IBinder onBind(Intent arg0) {        return null;    }}-----------------------------------public class Util {    public static boolean isServiceWorked(Context context, String serviceName) {        ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);        ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager.getRunningServices(Integer.MAX_VALUE);        for (int i = 0; i < runningService.size(); i++) {            if (runningService.get(i).service.getClassName().toString().equals(serviceName)) {                return true;            }        }        return false;    }}
  3. 利用静态广播接收器重启进程:
    使用静态注册的广播接收器BroadcastReceiver来监听一些系统广播/其他应用发出的广播在onReceive中重启进程。

    无法接受到系统?:1. 在Android 3.1之后,处于stopped状态的应用无法接收到系统广播。2. 如果一个应用在安装后从来没有启动过,或者已经被用户强制停止了,那么这个应用就处于停止状态(stopped state)。3. 如果想使处于stopped状态的应用也接收到广播,需要在intent中增加FLAG_INCLUDE_STOPPED_PACKAGES这个Flag。要注意的是,用户无法自定义系统广播。4. 有些广播只有静态接收器可以接收得到。5. 深度定制ROM使应用重回STOPPED状态
  4. 与系统Service捆绑 : NotificationListenerService

    NotificationListenerService是通过系统调起的服务,当有应用发起通知的时候,系统会将通知的动作和信息回调给NotificationListenerService。

    <service  android:name=".services.NotificationService "    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"    android:process=":service">    <intent-filter>        <action android:name="android.service.notification.NotificationListenerService" />    </intent-filter></service>-------------------public class NotificationService extends NotificationListenerService {  @Override  public void onNotificationPosted(StatusBarNotification sbn) {  }  @Override  public void onNotificationRemoved(StatusBarNotification sbn) {  }}

    当系统发现某应用产生通知或者用户删除某通知,都会回调该服务的上述这两个函数,函数的参数StatusBarNotification包含着该通知的具体信息。.

  5. 其他:全家桶/SDK唤醒、手机厂商的白名单定制

六、Android应用进程保活的权衡

  1. 进程保活要回到用户体验。有的进程保活不能做到性能优化,反而在不断重启费电、亦或如幽灵般重复出现在用户面前。在某些应用场景里,有的进程保活起到了应有的作用,比如音乐播放时的前台Service…..

  2. 流氓的进程保活只会搞坏Android 生态环境,伤害 Android 平台开发者等人的利益。当然,Google也不会让这么流氓方式胡作非为。


参考:

http://blog.csdn.net/aigestudio/article/details/51348408
http://www.jianshu.com/p/63aafe3c12af
http://www.cnblogs.com/angeldevil/archive/2013/05/21/3090872.html

0 0
原创粉丝点击