Android O后台服务限制总结

我们看Android O的新特性的时候会看到这样一个介绍:

多个 Android 应用和服务可以同时运行。 例如,用户可以在一个窗口中玩游戏,同时在另一个窗口中浏览网页,并使用第三个应用播放音乐。

同时运行的应用越多,对系统造成的负担越大。 如果还有应用或服务在后台运行,这会对系统造成更大负担,进而可能导致用户体验下降;例如,音乐应用可能会突然关闭。

为了降低发生这些问题的几率,Android 8.0 对应用在用户不与其直接交互时可以执行的操作施加了限制。

也就是系统不允许后台应用创建后台服务。

系统可以区分 前台后台 应用。 (用于服务限制目的的后台定义与内存管理使用的定义不同;一个应用按照内存管理的定义可能处于后台,但按照能够启动服务的定义又处于前台。)如果满足以下任意条件,应用将被视为处于前台:

  • 具有可见 Activity(不管该 Activity 已启动还是已暂停)。
  • 具有前台服务。
  • 另一个前台应用已关联到该应用(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)。 例如,如果另一个应用绑定到该应用的服务,那么该应用处于前台:

    • IME
    • 壁纸服务
    • 通知侦听器
    • 语音或文本服务

如果以上条件均不满足,应用将被视为处于后台。

这些规则不会对绑定服务产生任何影响。 如果您的应用定义了绑定服务,则不管应用是否处于前台,其他组件都可以绑定到该服务。

好了从官网复制的内容就到这里了,接下来说说处理办法,和现象效果。官网介绍的办法就是把我们的后台服务转变为前台服务,为我们提供了新的启动服务方法:startForegroundService,使用这个方法启动的服务必须在5秒内使用startForeground方法以显示新服务的用户可见通知,否则的话将抛出异常:Context.startForegroundService() did not then call Service.startForeground()。

首先我们定义自己的service:

public class MyService extends Service {

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

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("zhangdi","MyService onCreate");
        sendNotification();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("zhangdi","MyService onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i("zhangdi","MyService onDestroy");
        super.onDestroy();
    }

    private void sendNotification() {

        if (Build.VERSION.SDK_INT>=26) {
            Intent intent = new Intent(this, StopActivity.class);
            //创建NotificationChannel并与NotificationManager、Notification关联,否则系统会
            //提示Failed to post notification on channel “null”
            String channelId = "channel1";
//            NotificationChannel channel = new NotificationChannel(channelId, "通知", NotificationManager.IMPORTANCE_LOW);
//            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//            manager.createNotificationChannel(channel);
            Notification nf = new Notification.Builder(this, channelId)
                    .setContentTitle("通知")
                    .setContentText("一个服务正在运行")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(PendingIntent.getActivities(this, 100, new Intent[]{intent}, PendingIntent.FLAG_CANCEL_CURRENT))
                    .build();
            startForeground(10, nf);
        }
    }
}

然后在MainActivity中启动这个服务:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT>=26) {
            startForegroundService(new Intent(this, MyService.class));
        }

        //startService(new Intent(this, MyService.class));
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("zhangdi","activity onStop");
    }

    @Override
    protected void onDestroy() {
        Log.i("zhangdi","activity onDestroy");
        super.onDestroy();
    }
}

 运行效果图:

这个效果图大家都看懂了吧?就是启动应用在MainActivity中启动服务,然后退出应用,我们会看到通知栏会出现一个通知提醒我们应用还有一个服务在运行,而且我们点击这个通知还能跳到系统的页面中去强制停止服务,感觉是不是有点吊吊的。但是,是不是哪有点不对?看上面我在service中写的代码,我明明自己定义了一个notification啊,现在怎么弹出来的是系统的?也没有报错了啊,怎么肥事?好吧,细心的朋友应该发现了在我们应用启动的时候快速的弹出了一个系统吐司,也没看清是什么,但是不要紧我们再重新运行,截图看看到底提示了什么?

系统吐司截图:

它提示的是:Failed to post notification on channel "null",好吧系统在这里告诉我们为什么我们的通知没能发送成功了,是缺少channel值,可是channel是什么呢?我也不知道,所以我去百度查了,查的结果是:这个channel指的是NotificationChannel,官网对它的解释是:A representation of settings that apply to a collection of similarly themed notifications.百度翻译为:应用于相似主题集合的设置的表示形式。是不是没太看懂?我又向下看了看类里提供的方法,是一些为notification设置声音、震动、提示灯的方法,那我是不是可以把它理解为是一个细化notification设置系统提示形式的类。其实我在上边service的代码里也设置了这个属性,只是注释掉了,为了看看没有的效果,现在只需要解开注释就能看到正确的效果了。

 最后注意一下NotificationChannel的设置只是在Android8.0才开始的,低版本里并没有这个设置,还是我们原来notification怎么使用就怎么使用。

还有个问题我在写demo的时候也使用了原来的startService方法来启动服务,我发现,这个方法也还是可以启动服务的,如果我们没有把它转为前台服务,在我们的应用退出后,这个后台服务还会运行一段时间,之后会被系统停止,但是不会抛出异常:Context.startForegroundService() did not then call Service.startForeground(),也就是说系统不会告诉你,你的服务已经被我停止了,你的一些后台操作也进行不下去了。

我总结的就这些了,有哪不对的地方,希望看的朋友能够严厉指出,毕竟谁也不希望自己一直错下去不是。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值