美文网首页
Service详解(基础篇)

Service详解(基础篇)

作者: Geekholt | 来源:发表于2019-09-30 15:31 被阅读0次

如需转载请评论或简信,并注明出处,未经允许不得转载

目录

前言

Service是Android四大组件之一,但是由于实际开发中可能没有Activity使用的频繁,有的同学对Service的一些使用和某些细节可能不是特别清楚。没关系,这篇文章将带你更加全面的了解Service

为什么要使用Service

从字面意思上看来,Service是服务的意思,我们很容易联想到它是一个帮助我们在后台完成一些任务的组件,那么它和Thread有什么区别呢?

Thread的定义

Thread:程序执行的最小单元,它是分配CPU的基本单位

Thread的生命周期

  1. 新建,new Thread()

  2. 就绪,排队等候cpu资源

  3. 运行,run()

  4. 死亡,执行完毕或被杀死

  5. 阻塞,由于某种原因,正在运行的线程让出cpu,并暂停执行

Thread的特点(重要)

Thread子线程独立于ActivityActivity很难对线程进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作

当我们需要使用Thread连续不停地每隔一段时间就要连接服务器一次做某种同步,那么这个Thread就需要在Activity退出后也运行,同时需要对这个Thread进行控制,这时候我们要怎么办?

没错!使用Service就可以解决了

什么是Service?

  1. Service既不是一个线程,Service通常运行在当成宿主进程的主线程中,所以在Service中进行一些耗时操作就需要在Service内部开启线程去操作,否则会引发ANR异常
  2. 也不是一个单独的进程,除非在清单文件中声明时指定进程名,否则Service所在进程就是application所在进程
  3. Service可以不需要UI就在后台运行,不用管开启它的页面是否被销毁,只要进程还在就可以在后台运行
  4. Service是Android中的四大组件,使用它一定要在AndroidManifest.xml中声明

Service的启动方式与生命周期

Service有三种启动方式

  1. startService

  2. bindService(类似于C/S结构)

  3. startService&bindService同时调用

service生命周期

startService

startService步骤

  1. 定义一个类继承Service
  2. Manifest.xml文件中配置该Service
  3. 使用ContextstartSerice(intent)方法启动该Service
  4. 不再使用时,调用stopService(intent)方法停止该服务
private Intent mStartIntent;

public void btnStartService(View view) {
    mStartIntent = new Intent(this, MyService.class);
    startService(mStartIntent);
}

public void btnStopService(View view) {
    if (mStartIntent != null) {
        stopService(mStartIntent);
    }
}

startService特点

  1. startService方式启动Service不会调用到onBind()

  2. startService可以多次调用,但是只有第一次调用才会执行onCreate(),之后每次调用都会直接执行onStartCommand()onStartCommand()执行次数与startService调用次数一致

  3. 不管调用多少次startService,只需要调用一次stopService就结束

  4. 如果startService后没有调用stopSelf或者stopService,则Service一直存活并运行在后台,直到进程被杀死

  5. Service中onStartCommand方法返回值

bindService

bindService步骤

  1. 定义一个类继承Service
  2. Manifest.xml文件中配置该Service
  3. 创建Binder子类,返回给客户端即Activity使用,提供数据交换的接口
  4. ServiceonBind()中返回创建的Binder子类对象
  5. Activity中定义用于接收BinderServiceConnection
  6. onServiceConnected()中拿到Service对象后,就可以调用Service内部的公有方法
  7. 使用ContextbindSerice(intent,ServiceConnection,flags)方法启动该Service
  8. Activity被销毁,或者主动掉调用unbindService(),Service的生命周期结束
    //创建Binder子类,返回给客户端即Activity使用,提供数据交换的接口
    public class MyBinder extends Binder {
        //声明一个方法,getService。(提供给客户端调用)
        MyService getService() {
            return MyService.this;
        }
    }

        //在Service的onBind()中返回创建的Binder子类对象
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return new MyBinder();
    }
        
        //在Activity中定义用于接收Binder的ServiceConnection
    mConn = new ServiceConnection() {
        /**
         * 与服务端交互的接口方法,绑定服务的时候被回调,单这个方法获得绑定Service传递过来的IBinder对象
         * 通过IBinder对象,实现宿主和Service交互
         * */
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.i(TAG, "绑定成功调用:onServiceConnected");
            //获取Binder
            MyService.MyBinder binder = (MyService.MyBinder) iBinder;
            //拿到Service对象后,就可以调用Service内部的公有方法
            mService = binder.getService();
        }

        /**
         * 正常情况下是不被调用的,他的调用时机是当Service服务被意外销毁时
         * 进程crash或被kill
         * */
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i(TAG, "服务被销毁:onServiceDisconnected");
        }
    };

        //bindService
    public void btnBindService(View view) {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConn, Service.BIND_AUTO_CREATE);
    }

        //unbindService
    public void btnUnBindService(View view) {
        if (mService != null) {
            mService = null;
            unbindService(mConn);
        }
    }


bindService特点

  1. 由于bindService是异步执行的,所以需要额外构建一个ServiceConnection对象用与接收bindService的状态,同时还要指定bindService的类型
  2. bindService()也可以调用多次,与startService()不同,多次调用bindService()onBind()onServiceConnected()只执行一次
  3. 主动调用unbindService()并不会回到onServiceDisconnected(),只有当Service服务被意外销毁时(如进程crash或者内存资源不足被kill),这个方法才被自动调用。可以用adb shell kill pidkill service来测试
  4. 通过bindService()方式启动ServiceActivity可以拿到Service对象,所以可以调用Service内部的公有方法

startService&bindService

针对同一个Service,我们既可以是启动服务的状态(即启动后无限运行下去直到进程被杀死),也可以绑定服务状态(即绑定对象销毁,服务也销毁),那么就可能会出现启动服务和绑定服务的先后顺序问题

  1. startService(),后bindService()

启动服务状态不会转化为绑定服务状态,即使调用unbindService()后,服务依然会以启动服务状态在后台运行,直到收到stopService()指令

  1. bindService(),后startService()

绑定服务状态转化为启动服务状态,即使之前绑定的Activity销毁了,服务还是会继续运行下去,直到收到stopService()指令

小结

  1. 启动服务状态的优先级会高于绑定服务端状态,因为绑定服务是要依托于我们的Activity的,而启动服务只需要依靠Service本身以及内存的一些情况

  2. 不管先后顺序如何,Android系统仅仅会为一个Service创建一个Service对象,所有操作的都是同一个Service对象

  3. 当同时调用startService()bindService()后,需要分别调用stopService()unbindService()Service才会走onDestroy()

一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁

异步Service — IntentService

  1. Service通常运行在当成宿主进程的主线程中,所以在Service中进行一些耗时操作就需要在Service内部开启线程去操作,否则会引发ANR异常
  2. 通过startService形式开启Service时,如果不主动调用stopServiceService将在后台一直运行

为了解决这个两个问题,Android系统帮我们封装了IntentService类。下面我们通过源码来看看IntentService是怎么做的

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //子类重写onHandleIntent执行异步操作
            onHandleIntent((Intent)msg.obj);
            //执行完成后
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }
  
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //HanderThread是Thread类的子类,内部封装了Looper,方便我们在子线程中使用Handler
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        //把HandlerThread持有的looper对象转交给mServiceHandler
        //即 mServiceHandler就可以执行异步任务了
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    //onStartCommand之后执行
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //发送消息
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

IntentService内部有一个工作线程HanderThread,(有关HanderThread的知识可以看HandlerThread使用及源码解析
),并通过这个HandlerThread中的looper创建了一个Handler对象,然后将Intent以Message的方式从主线程发送到子线程,这样onHandleIntent()就在子线程中执行,可以执行耗时操作

相同点:

  1. 继承自Service,与Service的启动方式是一样的,具有Service各种特性

  2. IntentService也可以多次启动,但是每次启动会将任务添加到工作队列中,每次只会执行一个工作线程

不同点:

  1. 在使用时将不再需要实现onStartCommand(),同时需要实现onHandleIntent()来执行异步任务
  2. 当消息队列中所有任务执行完成后,IntentService会在内部自动调用stopSelf()关闭自己,不用手动调用stopService()

总结

本文讲了ServiceThread的区别与应用场景,Service的启动方式以及不同启动方式分别对应的生命周期,同时也介绍了用于异步任务处理的IntentService类。对于Service原理感兴趣的可以继续看Service详解(原理篇)

相关文章

网友评论

      本文标题:Service详解(基础篇)

      本文链接:https://www.haomeiwen.com/subject/cwpmyctx.html