android四大组件之Service

主要用于执行某些需要在后台长期运行的任务,并且是不受界面影响的,必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态

默认是在主进程的主线程中执行,因此若耗时操作,仍需要开启子线程执行
但是进程是有优先级的 给这个Service所在的进程(默认即为主进程) 设置为服务进程或者前台进程,这样能更好实现主进程的保活

若要自建一个进程中执行,则可以通过android:process属性进行设置 (Activity 等其他组件也是一样)
若名称以:(冒号)开头 则是该应用私有的 别的进程不能访问
若是符合包名的小写字母 则是相当于开启了一个新的进程或者可以说是应用
是会执行application的onCreate操作的

常用系统服务
后台service由系统启动时被SystemService服务启动
通过当前活动.getSystemService(name)并存于相应类的变量中 这些变量再调用一些方法
比如 WIFI连接查看 系统音量 短信 电量等等


实现:
主要有两种启动模式:
这里写图片描述


第一种:

生命周期见图一左侧

在Activity中:

 Intent intent = new Intent(MainActivity.this,XXXService.class);
        startService(intent);// (服务与开启者无联系的启动形式  )

创建Service服务类继承Service,实现下面的方法1,2,3,5

(若不是通过 new service 创建的 则要自己去 manifest 的application标签添加 service标签)

第二种:服务会与组件binding(绑定)

生命周期见图一右侧

创建服务类继承 Service 实现下面的方法1,3,4,5 注意是1,3,4,5!!!

(若不是通过 new service 创建的 则要自己去 manifest 的application标签添加 service标签)

方法1 : onCreate:
如果多次执行了Context的startService或者bindService方法启动Service,Service方法的onCreate方法只会在第一次创建Service的时候调用一次,以后均不会再次调用我们可以在onCreate方法中完成一些Service初始化相关的操作

方法2 :onStartCommand(Intent intent,int flag, int startId)
如果多次执行了Context的startService方法,那么Service的onStartCommand方法也会相应的多次调用

android在应用crash了之后,会默认重启service
可以通过onStartCommand的设置返回参数进行控制
START_NOT_STICKY
表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,如果想重新实例化该Service,就必须重新调用startService来启动
START_STICKY
表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,这时onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息
START_STICKY_COMPATIBILITY
这个其实是用来兼容api5 一下的,这个的作用和START_STICK一样,但是这个返回值不能保证系统一定会重新创建service
START_REDELIVER_INTENT
Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。

itent
intent就是startService(Intent intent)中的intent

flags
表示启动服务的方式:START_FLAG_REDELIVERY, 或者START_FLAG_RETRY

START_FLAG_REDELIVERY:你实现onStartCommand()来安排异步工作或者在另一个线程中工作,需要使用START_FLAG_REDELIVERY来 让系统重新发送一个intent。这样如果你的服务在处理它的时候被Kill掉, Intent不会丢失.

START_FLAG_RETRY:表示服务之前被设为START_STICKY,则会被传入这个标记。

startId
onStartCommand的启动次数,第一次通过startService启动为是1,不断startService启动依次累加,一般配合stopSelf(startId)使用可以看IntentService中使用(不确定,待测试)


方法3 :onBind
必须重写的方法
通过startService使用Service时,我们在重写onBind方法时,只需要将其返回值设为null即可。bindService时,用于返回binder对象


方法4 : onUnbind
解除绑定时调用unbindService(connect)会调用该方法,该方法调用结束后会解除绑定,并且调用onDestroy()方法

    @Override
    public boolean onUnbind(Intent intent) {
        // TODO Auto-generated method stub
        //松绑Service,会触发onDestroy()
        if(mediaPlayer.isPlaying()){
            mediaPlayer.stop();
        }
        return super.onUnbind(intent);
    }

方法5 onDestroy
在service销毁做相关释放操作,
应该在onDestroy()
而不是 stopService()

@Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        //先停止 再释放
        if(mediaPlayer.isPlaying()){
            mediaPlayer.stop();
        }
        mediaPlayer.release();

    }

至此,第一种方式完成,第二种方式还需要下面的步骤:

再在该服务类中声明一个类继承Binder{写各种方法的功能实现 oncreate() onBind()之类的}

 class MyMusicBinder extends Binder{
        //返回Service对象
        MusicService getService(){
            return MusicService.this;
        }
    }

在Activity中创建一个connector类 (连接服务的Client)监听Service状态的变化

private class MusicConnector implements ServiceConnection {
        //成功绑定时调用 即bindService()执行成功同时返回非空Ibinder对象
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
			//写成功绑定时要执行的内容
        }

        //不成功绑定时调用
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
	          //写不成功绑定时要执行的内容
            Log.i("binding is fail", "binding is fail");
        }
    }

在相应事件(点击事件或者onCreate)等:

  Intent intent = new Intent();
  intent.setClass(MainActivity.this, MusicService.class);
   MusicConnector connect_1= new MusicConnector();

在相应事件(点击事件等)中,进行服务与组件的绑定

bindService(intent,connect_1,flag);//(服务会与组件绑定)

service拿对象 得bindService才能拿
要在Service启动后 根据其他情况才做一些操作,得通过广播发信息过去,或者EventBus

flag 指定为0 绑定时不自动创建,指定为 BIND_AUTO_CREATE 绑定时自动创建


在相应事件(点击事件等)中,进行服务与组件的解除绑定

unbindService(connect_1);//解除绑定

会调用 onUnbind()
当组件销毁(关闭) 也会自动解除绑定 并 销毁 onDestroy()


至此,第二种绑定方式也完成!


服务停止:

stopService(intent);
or
stopSelf();

会调用onDestroy()


注意点:
1 记得在在各个位置调用stopService 各个界面 各个该结束的地方
2 crash了 默认是会重启服务的 要根据自己的需求设置 START_STICK参数

Service进程启动过程(startService())原理分析:https://blog.csdn.net/luoshengyang/article/details/6677029


IntentService

Service的子类 主要特点是: service内部 默认提供了一个线程,并且只能处理一个线程,并且当任务结束会自动关闭service
使用:
1 自定义MyIntentService 继承 IntentService

2 重写方法:

@Override
    protected void onHandleIntent(@Nullable Intent intent) {

在方法内部做操作即可

但是如果要绑定服务的话,还是要重写onBind 返回一个IBinder


JobIntentService

1 不具备实时性质 不适合实时更新UI
2 无法保证它会立即执行作业*
3 没有主动关闭的接口,只适用于短耗时的后台任务,如果需要在后台执行长期的任务或者循环任务,还是得开启前台服务

基本使用
1 要有WAKE_LOCK permission

2

        <service
            android:name=".service.XXJobService"
            android:permission="android.permission.BIND_JOB_SERVICE" />

1 提供enqueueWork静态方法给外部调用

companion object {
        fun enqueueWork(context: Context, work: Intent) {
            JobIntentService.enqueueWork(context, SSDPDiscoveryJobService::class.java, JOB_ID, work)
        }
}

2 重写onHandleWork 进行任务的处理
是会使用AsyncTask来处理work的,所以不需要考虑主线程阻塞的问题

override fun onHandleWork(intent: Intent) {
        // We have received work to do.  The system or framework is already
        // holding a wake lock for us at this point, so we can just go.
        val aa = intent.getStringExtra("lable")

3 重写onDestroy方法
当onHandleWork执行完 则会自动调用onDestroy 没有主动stop的接口 只能等onHandleWork执行完 因此不适合循环 长期任务

override fun onDestroy() {

问题:启动不了JobIntentService
**原因:**之前的服务开启了 没有关闭(没有调用onDestroy)


问题 Stopping service due to app idle
Background start not allowed: service Intent
java.lang.IllegalStateException: Not allowed to start service Intent
原因:
Android8.0以上 电池优化策略 应用在后台经过 idle timeout后 会禁用后台服务 stopService 并禁止启动服务
解决:
1 设置为前台服务
2 用JobIntentService
3 用JobService+JobScheduler 参考:https://blog.csdn.net/weixin_37577039/article/details/80041624

利用service进行背景音乐播放的事例参考博客:
http://blog.csdn.net/weixin_37577039/article/details/78775931

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值