精华内容
下载资源
问答
  • Android四大组件之ServiceService的生命周期被启动(startService)的服务的生命周期:被绑定(bindService)的服务的生命周期:被启动又被绑定的服务的生命周期:实例布局创建Service类启动ServiceDemo下载 android开发...
  • 今天小编就为大家分享一篇关于Android四大组件之Service详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • 主要介绍了Android四大组件之Service(服务)的用法,结合实例形式详细分析了Service的基本概念,类型,用法与相关注意事项,需要的朋友可以参考下
  • android四大组件之Service

    千次阅读 2017-11-09 23:29:37
    Service 主要用于在后台处理一些耗时的逻辑,或者去执行某些需要在后台长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态 常用系统服务 后台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

    展开全文
  • Android四大组件之Service的介绍

    千次阅读 2016-08-26 16:22:11
    ServiceAndroid四大组件之一,在Android开发中起到非常重要的作用。Service(服务)是一个没有用户界面的在后台运行执行“耗时操作”的应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景...

    Service的基本认识

    Service是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件.Service可由其他应用组件启动,而且即使用户切换到其他应用,Service仍将在后台继续运行.Service主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务.必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态.
    Service和Activity很相似,但是区别在于:Service一直在后台运行,没有用户界面,所以不会到前台,如果Service被启动起来,就和Activity一样,具有自己的声明周期.另外,需要注意的是,Service和Thread不是一个意思,不要被Service的后台概念所迷惑.实际上Service并不会自动开启线程,所有的代码都是默认运行在主线程中的.因此,我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则可能造成ANR的问题.

    Service的形式

    Service基本上分为两种形式:

    • Started(启动的)
    当应用组件(如 Activity)通过调用 startService() 启动Service时,Service即处于“启动”状态.一旦启动,Service即可在后台无限期运行,即使启动Service的组件已被销毁也不受影响.通常,一个开启的Service执行单一操作并且不会给调用者返回结果.例如,它可能通过网络下载或上传文件.操作完成后,Service会自行停止运行.一个比较形象的比喻startService,//这种方式,就是说:“起来,快去干活,干完活就自己滚蛋,不用回来了”。
    • Bound(绑定的)
    当应用组件通过调用 bindService() 绑定到Service时,Service即处于“绑定”状态.一个绑定的Service提供客户端/服务器接口允许组件和Service交互,甚至跨进程操作使用进行间通信(IPC).仅当与另一个应用组件绑定时,绑定服务才会运行.多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁.一个形象的比喻bindService,//这种方式,就是说:“起来,快去干活,有事电话联系”。

    Service的使用

    先使用startService()启动Service。

    • 新建一个MyService继承Service,并重写一些所需方法
    package com.servicedemo;
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;
    
    /**
     * Created by YK on 2016/8/26.
     */
    public class MyService extends Service {
        //当其他组件调用bindService()方法请求绑定Service时,该方法被回调。(默认为null)
        @Override
        public IBinder onBind(Intent intent) {
            Log.e("kaka", "onBind");
            return null;
        }
        //当Service第一次创建时,回调该方法(只一次)。
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e("kaka", "onCreate");
        }
        //当其他组件调用startService()方法请求启动Service时,该方法被回调(每次)。
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.e("kaka", "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
        //当Service被销毁时回调,在该方法中应清除一些占用的资源。
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.e("kaka", "onDestroy");
        }
    }
    • Service是Android四大基本组件之一,那么就必须在AndroidManifest.xml中注册
    <span style="color:#2f2f2f;"> <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            </span><span style="color:#ff0000;"><service android:name=".MyService" /></span><span style="color:#2f2f2f;">
        </application></span>
    • 在MainActivity中加入启动Service和停止Service的方法
    <span style="color:#2f2f2f;">package com.servicedemo;
    
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private Button mStartService, mStopService;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            mStartService.setOnClickListener(this);
            mStopService.setOnClickListener(this);
        }
    
        private void initView() {
            mStartService = (Button) findViewById(R.id.start);
            mStopService = (Button) findViewById(R.id.stop);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.start:
                    </span><span style="color:#ff0000;">Intent startIntent = new Intent(this, MyService.class);
                    startService(startIntent);</span><span style="color:#2f2f2f;">
                    break;
                case R.id.stop:
                    </span><span style="color:#ff0000;">Intent stopIntent = new Intent(this, MyService.class);
                    stopService(stopIntent);</span><span style="color:#2f2f2f;">
                    break;
                default:
                    break;
            }
        }
    }</span>

    运行结果及说明:

    点击启动服务按钮,运行结果:


    在上面基础之上点击启动服务按钮两次:

    按返回键后再次点击启动服务按钮:

    在上面基础上再点两次启动服务按钮:

    点击停止服务:


    重新进入应用,点击启动服务按钮:

    结论: Service只有在第一次启动的时候才会调用onCreate()方法,此后再次点击Start Service按钮只会执行onStartCommand()方法,并不会再启动一个服务。
    如何让Activity与Service产生“羁绊"呢?这里就需要使用之前MyService中没有使用的方法onBind()了,这个方法就是用于与Activity建立关联的。先修改MyService的代码,新增了一个MyBinder类继承自Binder类 ,MyBinder中添加了一个doSomething()方法用于执行你要执行的后台任务。

    使用bindService()方法启动Service。

    • MyService代码如下:
    package com.servicedemo;
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.util.Log;
    
    /**
     * Created by YK on 2016/8/26.
     */
    public class MyService extends Service {
        //当其他组件调用bindService()方法请求绑定Service时,该方法被回调。(默认为null)
        @Override
        public IBinder onBind(Intent intent) {
            Log.e("kaka", "onBind");
            return new MyBinder();
        }
    
        //当Service第一次创建时,回调该方法(只一次)。
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e("kaka", "onCreate");
        }
    
        //当其他组件调用startService()方法请求启动Service时,该方法被回调(每次)。
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.e("kaka", "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.e("kaka", "onUnbind");
            return super.onUnbind(intent);
        }
    
        //当Service被销毁时回调,在该方法中应清除一些占用的资源。
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.e("kaka", "onDestroy");
        }
    
        class MyBinder extends Binder {
            public void doSomething(){
                Log.e("kaka","doSomething");
            }
        }
    }
    
    • 修改MainActivity中的代码,让MainActivity和MyService之间建立关联:
    <span style="color:#2f2f2f;">package com.servicedemo;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private Button mStartService, mStopService;
        private MyService.MyBinder myBinder;
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("kaka", "onServiceConnected");
                myBinder = (MyService.MyBinder) service;
                myBinder.doSomething();
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e("kaka", "onServiceDisconnected");
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            mStartService.setOnClickListener(this);
            mStopService.setOnClickListener(this);
        }
    
        private void initView() {
            mStartService = (Button) findViewById(R.id.start);
            mStopService = (Button) findViewById(R.id.stop);
        }
    
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.start:
                    //这里传入BIND_AUTO_CREATE,表示在Activity和Service关联后自动创建Service,
                    //</span><span style="color:#ff0000;">这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。</span><span style="color:#2f2f2f;">
                    Intent startIntent = new Intent(this, MyService.class);
                    bindService(startIntent, conn, BIND_AUTO_CREATE);
                    break;
                case R.id.stop:
                    //如果没有绑定服务就执行unbindService是会报错, java.lang.IllegalArgumentException: Service not registered:
                    unbindService(conn);
                    break;
                default:
                    break;
            }
        }
    }
    </span>

    运行结果及说明:

    点击绑定服务:

    再次点击绑定服务:不会打印任何log
    没有点击解绑的情况下按返回键退出打印log:

    可以看出会提示没有解绑,但是仍然会执行onUnbind()和onDestroy()方法。
    解决的办法就是:
    @Override
        protected void onDestroy(){
            super.onDestroy();
            unbindService(conn);
        }
    点击绑定服务后再点击解绑:

    我们可以知道,多次点击绑定服务按钮,并不会重复执行ServiceConnection中的onServiceConnected()方法。

    如果击了Start Service按钮,又点击了Bind Service按钮又是什么情况呢?

    这时无论单独点击停止按钮还是解绑服务按钮,Service都不会被销毁,必须要将两个按钮都点击一下,Service才会被销毁,也就是调用onDestroy()方法。所以,一个Service必须要在既没有和任何Activity关联又处于停止状态的时候才会被销毁。

    Service生命周期总结

    不同的启动方式,Service回调的生命周期方法也不一样。

    1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStartCommand将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。


    2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate和onBind方法都只会调用一次,同时onStartCommand方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。


    3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStartCommand便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

    4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

    注意:1、使用startService的时候,一般在onStartCommand()中写主要的逻辑代码,但是Service运行在主线程中,所以Service本身不能做耗时操作。如果做耗时操作或访问网络可以使用异步任务、子线程、Loader等来处理。

    特别注意:
    1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);


    2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;

    3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;
    4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。


    5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart仍然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。

    Service和Thread的区别

    都是后台运行,那么Service和Thread的选择就会迷惑不少人,什么时候用Service?什么时候用Thread?
    区别:
    • Service是运行在主线程里的,而Thread是开辟新的线程。(即如果在Service里编写了非常耗时的代码,程序必定会出现ANR的)
    • Service后台是指它的运行完全不依赖UI,即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以运行。(耗时的操作可以在Service中创建一个子线程去处理即可)
    既然都要创建子线程,为什么不在Activity中创建而是到Service中?
    这是因为Activity很难对Thread进行控制,当Activity被销毁后,就无法获取到Thread的实例了,而且在1Activity中创建的线程,在2Activity中是无法对其进行操作的。但Service不受这些约束,只要进程还在,Activity即使被销毁了,也能对子线程进行控制。

    前台Service

    Service基本都是后台运行的,既然后台运行有时候希望它能一直运行,比如时钟,或者需要实时运行的服务。但是Service的系统优先级还是比较低的,当系统出现了内存不足的情况,还是有可能回收掉正在后台运行的Service,Service如果要防止尽可能不被系统杀掉,需要设置为在前台运行。就像QQ一样,会在系统状态栏一直显示一个图标信息。
    由于设置前台运行service的方法在2.0之前和2.0之后有所变化。所以需要根据不同的版本进行区分;或者完全使用反射机制来处理,这样只要有相应的方法就可以使用,否则使用其他版本的方法。修改MyService中的代码为:

    待续。。。

    IntentService

    待续。。。

    拥有service的进程具有较高的优先级

      官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。

    1.  如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。
    2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.
    3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。
    4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
    5. 如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。

    参考资料:

    官方文档:https://developer.android.com/reference/android/app/Service.html#

    Android创建前台运行的Service:http://blog.csdn.net/ameyume/article/details/9150755


    展开全文
  • Android 四大组件之Service的Demo
  • 重温Android 四大组件之 Service 目录 一、Service是什么? 1.Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作且没有关联任何界面2.一个Service可以完成下面这些工作: 3.服务的特点: 二 、...

    重温Android 四大组件之 Service

    目录

    一、Service是什么?

    1.Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作且没有关联任何界面

    2.一个Service可以完成下面这些工作:

    3.服务的特点:

    4. 源码

    二 、Service的分类

    1.Local Service(本地服务)

    2.Remote Service(远程服务)

    三、 定义Service

    1.定义一个类继承于Service类

    2.在AndroidManifest.xml中配置Service

    四、启动与停止Service

    1.方式一:一般启动

    context.startService(Intent intent)contstopService(lntent intent)

    2.方式二:绑定启动

    bindService(Intent intent,ServiceConnection connection)unbindService(ServiceConnection connection)

     

    五、代码示例

    1. 本地服务

    (1) activity_main.xml 中定义两组按钮

    (2) 自定义Service 类, 并实现onBind 方法, 以及重写其它生命周期相关方法(onCreate / onStartCommand/ onUnbind/ onDestroy)

    (3) AndroidManifest 中声明service

    (4) Activity 中处理Button 点击事件 

    (5) 启动或停止服务的 日志输出:

    (6) 绑定或解绑服务的 日志输出:



    一、Service是什么?


    1.Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作且没有关联任何界面

    2.一个Service可以完成下面这些工作:

           访问网络
           播放音乐
           文件1O操作
           大数据量的数据库操作

    3.服务的特点:

    Service在后台运行,不用与用户进行交互即使应用退出,服务也不会停止.
    在默认情况下,Service运行在应用程序进程的主线程(UI线程)中,如果需要在Service中处理一些网络连接等耗时的操作,那么应该将
    这些任务放在分线程中处理,避兔阻塞用户界面

    4. 源码

    Service 类是一个抽象类, 继承了ContextWrapper,  即Context 类里定义的方法也可以用到 (Activity 也是ContextWrapper 的子类)

    public abstract class Service extends ContextWrapper implements ComponentCallbacks2,
            ContentCaptureManager.ContentCaptureClient {

    二 、Service的分类

    1.Local Service(本地服务)

    Service对象与Serive的启动者在同个进程中运行,两者的通信是进程内通信


    2.Remote Service(远程服务)

    Service对象与Service的启动者不在同一个进程中运行,这时存在一个进程间通信的问题,Android专门为此设计了AlDL来实现进程间通信

    三、 定义Service

    1.定义一个类继承于Service类

    public class MyLocalService extends Service {

    2.在AndroidManifest.xml中配置Service

    <application 
               ...>
        <service android:name=".MyLocalService"/>
    </application>

    四、启动与停止Service


    1.方式一:一般启动

    context.startService(Intent intent)
    context.stopService(lntent intent)


    2.方式二:绑定启动

    bindService(Intent intent,ServiceConnection connection)
    unbindService(ServiceConnection connection)


    区别:看Service启动后是否与启动者有关联?
    Service对象经历的生命周期是否相同?

     

    五、代码示例

    1. 本地服务

    实例如图:

    (1) activity_main.xml 中定义两组按钮

    分别为 启动服务 、停止服务  和 绑定服务、解绑服务, 界面很简单

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:textSize="30sp"
            android:text="1. 测试启动本地服务" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:orientation="horizontal">
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="启动服务"
                android:onClick="startService"
                android:textSize="20sp"/>
    
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="停止服务"
                android:onClick="stopService"
                android:textSize="20sp"/>
        </LinearLayout>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:textSize="30sp"
            android:text="2. 测试绑定本地服务" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:orientation="horizontal">
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="绑定服务"
                android:onClick="bindService"
                android:textSize="20sp"/>
    
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="解绑服务"
                android:onClick="unbindService"
                android:textSize="20sp"/>
        </LinearLayout>
    
    </LinearLayout>

    (2) 自定义Service 类, 并实现onBind 方法, 以及重写其它生命周期相关方法(onCreate / onStartCommand/ onUnbind/ onDestroy)

    package com.example.servicetest1;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.util.Log;
    
    import androidx.annotation.Nullable;
    
    public class MyLocalService extends Service {
        private final static String TAG = "MyLocalService";
    
        @Override
        public void onCreate() {
            super.onCreate();
            log("onCreate");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            log("onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            log("onDestroy");
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            log("onBind");
            return new Binder();
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            log("onUnbind");
            return super.onUnbind(intent);
        }
    
        private void log(String msg){
            Log.d(TAG, msg);
        }
    }
    

    (3) AndroidManifest 中声明service

        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service android:name=".MyLocalService"/>
        </application>

    (4) Activity 中处理Button 点击事件 

    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
        private final static String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void startService(View view) {
            log("startService ");
            Intent intent = new Intent(this, MyLocalService.class);
            startService(intent);
        }
    
        public void stopService(View view) {
            log("stopService ");
            Intent intent = new Intent(this, MyLocalService.class);
            stopService(intent);
        }
    
        private void log(String msg) {
            Log.d(TAG, msg);
        }
    
        private ServiceConnection mConnection = null;
    
        public void bindService(View view) {
            log("bindService ");
            Intent intent = new Intent(this, MyLocalService.class);
            if (mConnection == null) {
                mConnection = new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        log("onServiceConnected ");
                    }
    
                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        log("onServiceDisconnected ");
                    }
                };
            }
    
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        }
    
        public void unbindService(View view) {
            log("unbindService ");
            if (mConnection != null) {
                unbindService(mConnection);
                mConnection = null;
                Toast.makeText(this, "unbind service", Toast.LENGTH_SHORT).show();
            }
        }
    
        @Override
        protected void onDestroy() { // 似乎是多余的
            super.onDestroy();
            log("onDestroy ");
            if (mConnection != null) {
                log("onDestroy - need unbind service");
                unbindService(mConnection);
                mConnection = null;
            }
        }
    }

    (5) 启动或停止服务的 日志输出:

    //第一次点击启动
    2020-11-26 12:42:27.305  D/MainActivity: startService 
    2020-11-26 12:42:27.312  D/MyLocalService: onCreate
    2020-11-26 12:42:27.312  D/MyLocalService: onStartCommand
    //第二次点击启动
    2020-11-26 12:42:31.241  D/MainActivity: startService 
    2020-11-26 12:42:31.244  D/MyLocalService: onStartCommand
    
    //点击停止
    2020-11-26 12:42:34.118  D/MainActivity: stopService 
    2020-11-26 12:42:34.122  D/MyLocalService: onDestroy
    

    可以看到, 在启动时, Service 会调用 onCreate / onStartCommand,   而已经启动情况下,再调启动,仅会执行onStartCommand;

    停止时, Service 会调用 onDestroy

    (6) 绑定或解绑服务的 日志输出:

    //点击绑定
    2020-11-26 12:47:46.372  D/MainActivity: bindService 
    2020-11-26 12:47:46.379  D/MyLocalService: onCreate
    2020-11-26 12:47:46.380  D/MyLocalService: onBind
    2020-11-26 12:47:46.383  D/MainActivity: onServiceConnected
    
    //点击解绑
    2020-11-26 12:51:48.959  D/MainActivity: unbindService 
    2020-11-26 12:51:48.984  D/MyLocalService: onUnbind
    2020-11-26 12:51:48.985  D/MyLocalService: onDestroy
    
    //绑定的时候,退出Activity
    2020-11-26 12:53:12.285  I/ViewRootImpl@665f8ac[MainActivity]: dispatchDetachedFromWindow
    2020-11-26 12:53:12.304  D/MyLocalService: onUnbind
    2020-11-26 12:53:12.304  D/MyLocalService: onDestroy

    在绑定的时候, 会调用 onCreate /onBind,   客户端会回调 onServiceConnected

    解绑的试试,会先调用 onUnBind,  再调用 onDestroy.

    此外,在绑定的时候,退出Activity, 似乎会自动解绑服务?? (不需要在Activity onDestroy 时, 手动调用解绑服务??, 不会报 leak service connection ??)

    展开全文
  • 2. 进程Service是一种不提供用户交互页面但是可以在后台长时间运行的组件,可以通过在AndroidManifest.xml设置Serviceandroid:process=":remote"属性,让Service运行另一个进程中,也就是说,虽然你是在当前应用...

    1. 简介

    这篇文章会从Service的一些小知识点,延伸到Android中几种常用进程间通信方法。

    2. 进程

    Service是一种不提供用户交互页面但是可以在后台长时间运行的组件,可以通过在AndroidManifest.xml设置Service的android:process=":remote"属性,让Service运行另一个进程中,也就是说,虽然你是在当前应用启动的这个Service,但是这个Service和这个应用并不是同一个进程。

    四大组件都支持android:process=":remote"这个属性。

    因为Service可以运行在不同的进程,这里说一下Android中几种进程的优先级,当系统内存不足时候,系统会从优先级低的进程开始回收,下面根据优先级由高到低列出Android中几种进程。

    前台进程,当前用户操作所需要的进程

    用户正在交互的Activity(Activity执行了onResume方法)

    与正在交互的Activity绑定的Service

    设置为前台权限的Service(Service调用startForeground()方法)

    正在执行某些生命周期回调的Service,onCreate()、onStart()、onDestroy()

    正在执行onReceive()的BroadcastReceiver

    这种进程基本不会被回收,只有当内存不足以支持前台进程同时运行时候,系统才回回收它们,主要关注前三个。

    可见进程,没有与用户交互所必须的组件,但是在屏幕上仍然可见其内容的进程

    调用了onPause()方法但仍对用户可见的Activity

    与上面这种Activity绑定的Service

    服务进程,使用startService()启动的Service且不属于上面两种类别进程的进程,虽然这个进程与用户交互没有直接关系,但是一般会在后台执行一些耗时操作,所以,只有当内存不足以维持所有前台进程和可见进程同时运行,系统才回回收这个类别的进程。

    后台进程,对用户不可见的Activity进程,已调用了onStop()方法的Activity

    空进程,不包含任何活动应用组件的进程,保留这种进程唯一目的是作为缓存,缩短引用组件下次启动时间。通常系统会最优先回收这类进程。

    此外,一个进程的级别可能会因为其他进程对它的依赖而有所提高,即进程A服务于进程B(B依赖A),那么A的进程级别至少是和B一样高的。

    3. Service配置

    和其他组件(Activity/ContentProvider/BroadcastReceiver)一样,Service需要在Androidmanifest.xml中声明。

    ...

    ...

    Service是运行在主线程中的,如果有什么耗时的操作,建议新建子线程去处理,避免阻塞主线程,降低ANR的风险。

    在另外一篇文章中Intent以及IntentFilter详解提到过,为了确保应用的安全,不要为Service设置intent-filter,因为如果使用隐式Intent去启动Service时候,手机里面那么多应用,并不能确定哪一个Service响应了这个Intent,所以在项目中尽量使用显式Intent去启动Service。在Android 5.0(API LEVEL 21)版本后的,如果传入隐式Intent去调用bindService()方法,系统会抛出异常。

    可以通过设置android:exported=false来确保这个Service仅能在本应用中使用。

    4. 服务启动方式

    服务可以由其他组件启动,而且如果用户切换到其他应用,这个服务可能会继续在后台执行。到目前为止,Android中Service总共有三种启动方式。

    Scheduled,可定时执行的Service,是Android 5.0(API LEVEL 21)版本中新添加的一个Service,名为JobService,继承Service类,使用JobScheduler类调度它并且设置JobService运行的一些配置。具体文档可以参考JobScheduler,如果你的应用最低支持版本是21,官方建议使用JobService。

    Started,通过startService()启动的Service。通过这种方式启动的Service会独立的运行在后台,即使启动它的组件已经销毁了。例如Activity A使用startService()启动了Service B,过了会儿,Activity A执行onDestroy()被销毁了,如果Service B任务没有执行完毕,它仍然会在后台执行。这种启动方式启动的Service需要主动调用StopService()停止服务。

    Bound,通过bindService()启动的Service。通过这种方式启动Service时候,会返回一个客户端交互接口,用户可以通过这个接口与服务进行交互,如果这个服务是在另一个进程中,那么就实现了进程间通信,也就是Messenger和AIDL,这个会是下篇文章的重点。多个组件可以同时绑定同一个Service,如果所有的组件都调用unbindService()解绑后,Service会被销毁。

    startService和bindService可以同时使用

    5. 主要方法

    Service是一个抽象类,使用需要我们去实现它的抽象方法onBind(),Service有且仅有这一个抽象方法,还有一些其他的生命周期回调方法需要复写帮助我们实现具体的功能。

    onCreate(),在创建服务时候,可以在这个方法中执行一些的初始化操作,它在onStartCommand()和onBind()之前被调用。如果服务已经存在,调用startService()启动服务时候这个方法不会调用,只会调用onStartCommand()方法。

    onStartCommand(),其他组件通过startService()启动服务时候会回调这个方法,这个方法执行后,服务会启动被在后台运行,需要调用stopSelf()或者stopService()停止服务。

    onBind(),其他组件通过bindService()绑定服务时候会回调的方法,这是Service的一个抽象方法,如果客户端需要与服务交互,需要在这个方法中返回一个IBinder实现类实例化对象,如果不想其他客户端与服务绑定,直接返回null。

    onDestroy(),当服务不在还是用且即将被销毁时,会回调这个方法,可以在这个方法中做一些释放资源操作,这是服务生命周期的最后一个回调。

    如果组件仅通过startService()启动服务,不论服务是否已经启动,都会回调onStartCommand()方法,而且服务会一直运行,需要调用stopSelf和stopService方法关闭服务。

    如果组件仅通过bindService()绑定服务,则服务只有在与组件绑定时候运行,一旦所有的客户端全部取消绑定unbindService,系统才会销毁该服务。

    多次启动同一个服务,只有在服务初次启动时候会回调onCreate方法,但是每次都会回调onStartCommand,可以利用这个向服务传递一些信息。

    onStartCommand()的回调是在UI主线程,如果有什么耗时的操作,建议新启线程去处理。

    6. 启动和关闭服务

    启动服务:

    JobScheduler.schedule()

    startService(Intent)

    bindService(Intent service, ServiceConnection conn, int flags)

    关闭服务:

    JobScheduler.cancel()或者JobScheduler.cancelAll(),对应JobScheduler.schedule()

    Service自身的stopSelf()方法,组件的stopService(Intent)方法,对应startService启动方法

    unbindService(ServiceConnection conn),对应bindService

    示例:

    // 启动服务

    Intent intent = new Intent(this, DemoService.class);

    startService(intent);

    // 停止服务

    stopService(intent)

    // 绑定服务

    ServiceConnection mConnection = ServiceConnection() { ... };

    Intent intent = new Intent(this, DemoService.class);

    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    // 解除绑定

    unbindService(mConnection);

    绑定服务bindService()第三个参数数值:

    0,如果不想设置任何值,就设置成0

    Context.BIND_AUTO_CREATE,绑定服务时候,如果服务尚未创建,服务会自动创建,在API LEVEL 14以前的版本不支持这个标志,使用Context.BIND_WAIVE_PRIORITY可以达到同样效果

    Context.BIND_DEBUG_UNBIND,通常用于Debug,在unbindService时候,会将服务信息保存并打印出来,这个标记很容易造成内存泄漏。

    Context.BIND_NOT_FOREGROUND,不会将被绑定的服务提升到前台优先级,但是这个服务也至少会和客户端在内存中优先级是相同的。

    Context.BIND_ABOVE_CLIENT,设置服务的进程优先级高于客户端的优先级,只有当需要服务晚于客户端被销毁这种情况才这样设置。

    Context.BIND_ALLOW_OOM_MANAGEMENT,保持服务受默认的服务管理器管理,当内存不足时候,会销毁服务

    Context.BIND_WAIVE_PRIORITY,不会影响服务的进程优先级,像通用的应用进程一样将服务放在一个LRU表中

    Context.BIND_IMPORTANT,标识服务对客户端是非常重要的,会将服务提升至前台进程优先级,通常情况下,即时客户端是前台优先级,服务最多也只能被提升至可见进程优先级,

    BIND_ADJUST_WITH_ACTIVITY,如果客户端是Activity,服务优先级的提高取决于Activity的进程优先级,使用这个标识后,会无视其他标识。

    7. onStartCommand()返回值

    onStartCommand()方法有一个int的返回值,这个返回值标识服务关闭后系统的后续操作。

    返回值有以下几种:

    Service.START_STICKY,启动后的服务被杀死,系统会自动重建服务并调用on onStartCommand(),但是不会传入最后一个Intent(Service可能多次执行onStartCommand),会传入一个空的Intent,使用这个标记要注意对Intent的判空处理。这个标记适用于太依靠外界数据Intent,在特定的时间,有明确的启动和关闭的服务,例如后台运行的音乐播放。

    Service.START_NOT_STICKY,启动后的服务被杀死,系统不会自动重新创建服务。这个标记是最安全的,适用于依赖外界数据Intent的服务,需要完全执行的服务。

    Service.START_REDELIVER_INTENT,启动后的服务被杀死,系统会重新创建服务并调用onStartCommand(),同时会传入最后一个Intent。这个标记适用于可恢复继续执行的任务,比如说下载文件。

    Service.START_STICKY_COMPATIBILITY,启动后的服务被杀死,不能保证系统一定会重新创建Service。

    8. Service生命周期

    Service生命周期(从创建到销毁)跟它被启动的方式有关系,这里只介绍startService和bindService两种启动方法时候Service的生命周期。

    startService启动方式,其他组件用这种方式启动服务,服务会在后台一直运行,只有服务调用本身的stopSelf()方法或者其他组件调用stopService()才能停止服务。

    bindService启动方式,其他组件用这种方法绑定服务,服务通过IBinder与客户端通信,客户端通过unbindService接触对服务的绑定,当没有客户端绑定到服务,服务会被系统销毁。

    这两种生命周期不是独立的,组件可以同时用startService启动服务同时用bindService绑定服务,例如跨页面的音乐播放器,就可以在多个页面同时绑定同一个服务,这种情况下需要调用stopService()或者服务本身的stopSelf()并且没有客户端绑定到服务,服务才会被销毁。

    1460000011325764

    图-1Service生命周期图

    左图是使用startService()所创建的服务的生命周期,右图是使用bindService()所创建的服务的生命周期。

    9. 在前台运行服务

    服务可以通过startForeground来使服务变成前台优先级。

    public final void startForeground(int id, Notification notification) {

    try {

    mActivityManager.setServiceForeground(

    new ComponentName(this, mClassName), mToken, id,

    notification, true);

    } catch (RemoteException ex) {

    }

    }

    第一个参数用于标识你应用中唯一的通知标识id,不能设为0,最终会传入NotificationManager.notify(int id, Notification notification)取消通知需要用到,第二个参数是通知具体内容。

    前台服务需要在状态栏中添加通知,例如,将音乐播放器的服务设置为前台服务,状态栏通知显示正在播放的歌曲,并允许其他组件与其交互。

    // 设置Notification属性

    Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),System.currentTimeMillis());

    Intent notificationIntent = new Intent(this, ExampleActivity.class);

    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

    notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);

    startForeground(ONGOING_NOTIFICATION_ID, notification);

    要将服务从前台移除,需要调用stopForeground(boolean removeNotification),参数是一个布尔值,用来标识服务从前台服务移除时候,是否需要移除状态栏的通知。如果服务在前台运行时候被停止,状态栏的通知也会被移除。

    10. 与服务通信

    10.1 广播

    不多说,万能的通信。

    10.2 本地数据共享

    不多说,万能的通信,例如ContentProvider/SharePreference等等。

    10.3 startService()

    使用这个方法启动的服务,再次调用startService()传入Intent即可与服务通信,因为这种方式启动的服务在完整的生命周期内onCreate()只会执行一次,而onStartCommand()会执行多次,我们再次调用startService()时候,可以在oonStartCommand()去处理。

    10.4 bindService()

    使用这种方法启动的服务,组件有三种与服务通信的方式。

    Service中实现IBinder

    Messenger(AIDL的简化版)

    AIDL

    下一篇文章具体介绍Messenger、AIDL,因为它们是属于Android进程间通信。

    如果一个服务Service只需要在本应用的进程中使用,不提供给其他进程,推荐使用第一种方法。

    使用示例:

    Service:

    /**

    * 本地服务

    *

    * 和启动应用属于同一进程

    */

    public class LocalService extends Service {

    /**

    * 自定的IBinder

    */

    private final IBinder mBinder = new LocalBinder();

    @Override

    public IBinder onBind(Intent intent) {

    return mBinder;

    }

    /**

    * 提供给客户端的方法

    *

    * @return

    */

    public String getServiceInfo() {

    return this.getPackageName() + " " + this.getClass().getSimpleName();

    }

    /**

    * 自定义的IBinder

    */

    public class LocalBinder extends Binder {

    public LocalService getService() {

    return LocalService.this;

    }

    }

    }

    Activity:

    /**

    * 绑定本地服务的组件

    *

    * Created by KyoWang.

    */

    public class BindLocalServiceActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mShowServiceNameBtn;

    private LocalService mService;

    private boolean mBound = false;

    public ServiceConnection mConnection = new ServiceConnection() {

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

    LocalService.LocalBinder binder = (LocalService.LocalBinder) service;

    mService = binder.getService();

    mBound = true;

    }

    @Override

    public void onServiceDisconnected(ComponentName name) {

    mBound = false;

    }

    };

    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.a_bind_local_service);

    mShowServiceNameBtn = (Button) findViewById(R.id.bind_local_service_btn);

    mShowServiceNameBtn.setOnClickListener(this);

    }

    @Override

    protected void onStart() {

    super.onStart();

    Intent intent = new Intent(this, LocalService.class);

    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    }

    @Override

    protected void onStop() {

    super.onStop();

    if(mBound) {

    unbindService(mConnection);

    mBound = false;

    }

    }

    @Override

    public void onClick(View v) {

    int id = v.getId();

    if(id == R.id.bind_local_service_btn) {

    if(mBound) {

    String info = mService.getServiceInfo();

    Toast.makeText(BindLocalServiceActivity.this, info, Toast.LENGTH_SHORT).show();

    }

    }

    }

    }

    11. 服务长存后台

    关于网上通用的提升服务优先级以保证服务长存后台,即保证服务不轻易被系统杀死的方法有以下几种。

    设置android:persistent="true",这是application的一个属性,官方都不建议使用。

    Whether or not the application should remain running at all times,

    "true" if it should, and "false" if not.

    The default value is "false".

    Applications should not normally set this flag;

    persistence mode is intended only for certain system applications.

    设置android:priority优先级,这个并不是Service的属性。这个属性是在intent-filter中设置的。官方解释,这个属性只对活动和广播有用,而且这个是接受Intent的优先级,并不是在内存中的优先级,呵呵。

    android:priority

    The priority that should be given to the parent component with regard to

    handling intents of the type described by the filter.

    This attribute has meaning for both activities and broadcast receivers。

    在Service的onDestroy中发送广播,然后重启服务,就目前我知道的,会出现Service的onDestroy不调用的情况。

    startForeground,这个上面提到过,是通过Notification提升优先级。

    设置onStartCommand()返回值,让服务被杀死后,系统重新创建服务,上面提到过。

    五个里面就两个能稍微有点用,所以啊,网络谣传害死人。

    12. IntentService

    敲黑板时间,重点来了,官方强力推荐。

    前面提到两点。

    因为Service中几个方法的回调都是在主线程中,如果使用Service执行特别耗时的操作,建议单独新建线程去操作,避免阻塞主线程(UI线程)

    启动服务和停止服务是成对出现的,需要手动停止服务

    IntentService完美的帮我们解决了这个问题,在内部帮我们新建的线程,不需要我们手动新建,执行完毕任务后会自动关闭。IntentService也是一个抽象类,里面有一个onHandleIntent(Intent intent)抽象方法,这个方法是在非UI线程调用的,在这里执行耗时的操作。

    IntentService使用非UI线程逐一处理所有的启动需求,它在内部使用Handler,将所有的请求放入队列中,依次处理,关于Handler可以看这篇文章,也就是说IntentService不能同时处理多个请求,如果不要求服务同时处理多个请求,可以考虑使用IntentService。

    IntentService在内部使用HandlerThread配合Handler来处理耗时操作。

    private final class ServiceHandler extends Handler {

    public ServiceHandler(Looper looper) {

    super(looper);

    }

    @Override

    public void handleMessage(Message msg) {

    onHandleIntent((Intent)msg.obj);

    stopSelf(msg.arg1);

    }

    }

    public int onStartCommand(Intent intent, int flags, int startId) {

    onStart(intent, startId);

    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;

    }

    public void onStart(Intent intent, int startId) {

    Message msg = mServiceHandler.obtainMessage();

    msg.arg1 = startId;

    msg.obj = intent;

    mServiceHandler.sendMessage(msg);

    }

    注意msg.arg1它是请求的唯一标识,每发送一个请求,会生成一个唯一标识,然后将请求放入Handler处理队列中,从源代码里面可以看见,在执行完毕onHandleIntent方法后,会执行stopSelf来关闭本身,同时IntentService中onBind()方法默认返回null,这说明启动IntetService的方式最好是用startService方法,这样在服务执行完毕后才会自动停止;如果使用bindService来启动服务,还是需要调用unbindService来解绑服务的,也需要复写onBind()方法。

    小盆宇:在ServiceHandler类的handleMessage方法中,执行onHandleIntent后紧接着执行stopSelf(int startId),把服务就给停止了,那第一个请求执行完毕服务就停止了,后续的请求怎么会执行?

    注意stopSelf(int startID)方法作用是在其参数startId跟最后启动该service时生成的id相等时才会执行停止服务,当有多个请求时候,如果发现当前请求的startId不是最后一个请求的id,那么不会停止服务,所以只有当最后一个请求执行完毕后,才会停止服务。

    展开全文
  • 启动和停止方法主要是通过Intent来实现以上一篇中的ServiceTest项目为例来启动和停止MyService这个服务首先修改activity_main.xml中的代码,如下所示:android:orientation="vertical"android:layout_width="match_...
  • package ...import com.example.servicetest.service.MyService;import com.example.servicetest.service.MyService.MyBinder;import android.os.Binder;import android.os.Bundle;import andr...
  • 很多时候,面试官会问你,Service中能否进行耗时操作?我们稍后就会揭晓这个答案。作用:服务于需要长期运行的任务,比如资源下载、音视频播放等。生命周期生命周期图示由图中可以直观的看出几点。启动方式存在方式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,435
精华内容 9,774
关键字:

android四大组件之service