Skip to content

Android(5): Set service foreground with notification

clarkehe edited this page Jun 24, 2016 · 5 revisions

Android的服务组件(Service)有前台(foreground)和非前台之分。要将服务设置为前台服务,只需要调用一个API就可以。

    /**
     * Make this service run in the foreground, supplying the ongoing
     * notification to be shown to the user while in this state.
     * By default services are background, meaning that if the system needs to
     * kill them to reclaim more memory (such as to display a large page in a
     * web browser), they can be killed without too much harm.  You can set this
     * flag if killing your service would be disruptive to the user, such as
     * if your service is persforming background music playback, so the user
     * would notice if their music stopped playing.
     * 
     * <p>If you need your application to run on platform versions prior to API
     * level 5, you can use the following model to call the the older setForeground()
     * or this modern method as appropriate:
     * 
     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
     *   foreground_compatibility}
     * 
     * @param id The identifier for this notification as per
     * {@link NotificationManager#notify(int, Notification)
     * NotificationManager.notify(int, Notification)}; must not be 0.
     * @param notification The Notification to be displayed.
     * 
     * @see #stopForeground(boolean)
     */
    public final void startForeground(int id, Notification notification) {
        try {
            mActivityManager.setServiceForeground(
                    new ComponentName(this, mClassName), mToken, id,
                    notification, true);
        } catch (RemoteException ex) {
        }
    }

将服务设置成前台后,在通知栏会有一个持续通知,同时服务所在进程的优化级也会提高。优化级高,被回收的可能性相对会小一点。在小MI手机上测试,将服务设置成前台后,服务进程的oom_adj:4 --> 1。oom_adj值越小,优先级越高。

问题:有时我们想通过将前服务设置为前台,从而提高进程的优先级,但又不想在通知栏有通知?

解决办法:对<=4.3的系统,设置一个空的Notification;对>=4.4的系统,则还要启动一个临时的服务,使用相同ID设置一个空的Notification,然后将临时服务停止。临时服务与真正的服务是一个进程。

    private final static int ID = 1;

    private void onSetServiceForeground(){
        Log.i(TAG, "setServiceForeground");

        startForeground(ID, new Notification());

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            this.startService(new Intent(this, InnerService.class));
        }
    }

    /**
     * 借助InnerService,让RecordService变成活动的前台服务,提高进程优先级,但不在通知栏显示.[黑科技,利用系统漏洞。]
     */
    public static class InnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            setServiceForeground();
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }

        private void setServiceForeground(){
            Log.i(TAG, "InnerService#setServiceForeground");
            startForeground(ID, new Notification());
        }

        @Override
        public void onDestroy() {
            Log.i(TAG, "InnerService#onDestroy");
            stopForeground(true);
            super.onDestroy();
        }
    }

**总结:**将服务设置为前台,提高进程优化级,最终目的是为了降低进程被回收的可能(进程保活)。系统在决定进程回收顺序时,考虑了两个因素:进程优化级和进程占用内存。因此,除了提高进程优化级,还是尽量降低内存的占用。
降低内存的占用,除了有针对性优化内存外,还有一个方法时分拆进程。将需要长时保活的功能模块放到一个单独进程中,越小越好。可参考:微信Android客户端后台保活经验分享

Clone this wiki locally