Android Service

Service的生命周期

Service是Android四大组件之一,它通常用来实现需要在后台进行耗时操作的需求,它的生命周期如下:
这里写图片描述

可以看到Service的生命周期分成了两条线,左边这条是使用startService()方法启动Service后走的生命周期,右边这条是使用onBind()方法绑定Service后走的生命周期,下面我们先来学习使用startService()的情况下Service的生命周期。

创建一个Service类,命名为MyService,令其继承自Service类,并重写其下的生命周期回调函数:

  • onCreate
  • onStartCommand
  • onBind
  • onUnBind
  • onDestory

如果是在AndroidStudio中直接创建的Service类,那么AndroidStudio会自动为我们继承自Service类并且在Manifest配置文件中注册该Service,否则我们需要亲自去Manifest文件中注册刚刚创建的MyService。

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true"/>
public class MyService extends Service {

    public MyService() {
    }

    //创建Service
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("TAG", "Service:onCreate()");
    }

    //启动Service
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("TAG", "Service:onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    //绑定Service
    @Override
    public IBinder onBind(Intent intent) {
        Log.d("TAG", "Service:onBind()");
        return null;
    }

    //解绑Service
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("TAG", "Service:onUnBind()");
        return super.onUnbind(intent);
    }

    //销毁Service
    @Override
    public void onDestroy() {
        Log.d("TAG", "Service:onDestroy()");
        super.onDestroy();
    }
}

接着我们在Activity中放置四个按钮,其功能分别为启动服务、停止服务、绑定服务和解绑服务,id分别为start、stop、bind、unbind,并且每个按钮都指定了一个点击事件处理函数operate()
这里写图片描述

//四个按钮的点击事件处理函数
public void operate (View view) {
    switch (view.getId()) {
        case R.id.start: {
            //启动服务
            Intent intent = new Intent(this, MyService.class);
            startService(intent);
            break;
        }
        case R.id.stop: {
            //停止已经启动的服务
            Intent intent = new Intent(this, MyService.class);
            stopService(intent);
            break;
        }
        case R.id.bind: {
            //绑定服务
            break;
        }
        case R.id.unbind: {
            //解绑服务
            break;
        }
    }
}

分析上面的代码可以知道,我们通过调用startService()stopService()来启动和停止一个服务,参数都是Intent对象。

接下来我们首先点击第一个按钮启动服务,查看控制台输出信息:

D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()

可以看到onCreate()onStartCommand()方法被执行了,这说明我们在启动Service的时候会回调Service下的onCreate()onStartCommand()方法。

这时我们再点击停止服务的按钮,查看控制台输出信息:

D/TAG: Service:onDestroy()

这次回调了Service的onDestroy()方法销毁了Service。

了解了startService()启动Service后的生命周期流程后,我们再接着学习使用bindService()绑定Service时走过的生命周期。

首先完成点击绑定和解绑按钮时的事件处理函数,bindService()绑定服务函数除了需要传入Intent意图对象外,还需要传入一个ServiceConnection类和一个flags(BIND_AUTO_CREATE 表示在绑定Service的时候也创建Service),由于ServiceConnection对象不仅需要用在bindService()中,也需要用在解绑服务的方法unbindService()中,因此我们将ServiceConnection声明为全局变量并实例化:

//绑定和解绑Service时需要用到的ServiceConnection类
private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};
......
......
//四个按钮的点击事件处理函数
public void operate (View view) {
    switch (view.getId()) {
        ......
        ......
        case R.id.bind: {
            //绑定服务
            Intent intent = new Intent(this, MyService.class);
            bindService(intent, serviceConnection, BIND_AUTO_CREATE);
            break;
        }
        case R.id.unbind: {
            //解绑服务
            unbindService(serviceConnection);
            break;
        }
    }
}

在停止服务后,我们点击绑定服务按钮,查看控制台输出语句:

D/TAG: Service:onCreate()
D/TAG: Service:onBind()

可以看到,我们如果在不启动服务的情况下直接绑定服务,系统会依次回调onCreate()onBind()方法来创建和绑定服务,但注意此时后台实际上是没有服务的,因为服务在没有回调onStartCommand()方法前并不会活跃在后台进程中!即使这个服务被创建了(不相信的同学可以在Android后台服务查看一下)

然后我们点击解绑服务,查看控制台输出语句:

D/TAG: Service:onUnBind()
D/TAG: Service:onDestroy()

在我们解绑一个没有被启动(注意这几个字)的Service时,会依次回调Service的onUnbind()onDestroy()来解绑和销毁Service。

接下来我们再进行几个尝试,深入了解一下Service的生命周期。

(1)、点击启动服务,再点击绑定服务,查看控制台输出语句:

D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
D/TAG: Service:onBind()

(2)、在(1)的基础上,点击解绑服务,查看控制台输出语句:

D/TAG: Service:onUnBind()

(3)、在(1)的基础上,点击停止服务,查看控制台输出语句:

D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
D/TAG: Service:onBind()

(4)、在(3)的基础上,点击解绑服务,查看控制台输出语句:

D/TAG: Service:onUnBind()
D/TAG: Service:onDestroy()

总结:只有回调了onStartCommand()方法的Service才是一个启动的Service,没有被启动的Service是无法发挥功能的,同时也不会出现在Android系统后台,因此直接绑定服务是无法让服务生效的;直接绑定的服务在解绑时会依次调用onUnbindonDestroy,已经启动的绑定了的服务在解绑时只会执行onUnbind而不会再执行onDestroy来销毁Service;绑定服务会随着Activity的结束而结束,但是启动服务不受Activity的影响。

Service与Activity的通信

Service在大多数情况下都是用来进行后台耗时任务的,但如果只是单纯的startService后Activity是无法知道Service在后台执行耗时任务的进度的,因此Activity需要与Service进行通信,这就需要用到我们之前没有详细说明的IBinder接口和ServiceConnection接口。

首先在MyService的onCreate方法中开启一个新线程进行耗时操作:

//线程执行进度
private int process;

@Override
public void onCreate() {
    super.onCreate();
    Log.d("TAG", "Service:onCreate()");
    //开启一个新线程从1数到100,循环一次休眠一秒,用来模拟耗时任务
    new Thread(){
        @Override
        public void run() {
            super.run();
            for (process = 1; process <= 100; process++) {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}

至于为什么不在onStartCommand中开启新线程,是因为如果在已经创建并启动了这个服务的基础上再次启动服务,系统会再次回调onStartCommand方法而不会回调onCreate方法,这样可以防止同时出现两个耗时的线程。

在之前的onBind()中我们可以看到,这个方法需要返回一个实现了IBinder接口的类,由于之前我们不需要Activity和Service之间进行通信,因此我们返回了一个null:

    //绑定Service
    @Override
    public IBinder onBind(Intent intent) {
        ...
        return null;
    }

于是我们这次尝试着返回一个实现了IBinder接口的类试试看:

    @Override
    public IBinder onBind(Intent intent) {
        Log.d("TAG", "Service:onBind()");
        return new IBinder() {
            @Override
            public String getInterfaceDescriptor() throws RemoteException {
                return null;
            }

            @Override
            public boolean pingBinder() {
                return false;
            }

            @Override
            public boolean isBinderAlive() {
                return false;
            }

            @Override
            public IInterface queryLocalInterface(String descriptor) {
                return null;
            }

            @Override
            public void dump(FileDescriptor fd, String[] args) throws RemoteException {

            }

            @Override
            public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {

            }

            @Override
            public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
                return false;
            }

            @Override
            public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {

            }

            @Override
            public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
                return false;
            }
        };
    }

可以看到如果我们要自己去实现IBinder接口的话,我们需要重写很多没有见过的方法,为了节约开发者的时间和简化开发难度,Android提供了一个Binder类,Binder是一个实现了IBinder接口并且实现了其中抽象方法的类,因此我们可以创建一个继承自Binder的内部类来免去实现IBinder接口的步骤以及在我们自己的类中编写一些我们自己需要的方法,例如下面的MyBinder类:

class MyBinder extends Binder {
    //获取线程完成进度的自定义方法
    public int getProcess () {
        return process;
    }
}

在MyBinder类中我们提供了一个对外的接口getProcess来让Activity能访问到Service中耗时任务的进度,但是现在的问题是,接口是有了,我们如何让Activity能调用这个接口呢?

还是要看回onBind方法,这个方法是用来绑定Service和Activity的,因此要想实现Activity和Service的通信,这个方法返回的IBinder尤为重要,由于我们刚刚创建了一个MyBinder内部类,那么我们就在这个方法中返回一个MyBinder的实例化对象:

//绑定Service
@Override
public IBinder onBind(Intent intent) {
    Log.d("TAG", "Service:onBind()");
    return new MyBinder();
}

现在让我们回想一下,之前我们在Activity中调用绑定Service的方法bindService的时候,除了传入一个Intent和一个flags外,还传入了一个ServiceConnection类的对象,在实例化这个对象的时候我们的代码如下:

//绑定和解绑Service时需要用到的ServiceConnection类:用户绑定客户端和Service
private ServiceConnection serviceConnection = new ServiceConnection() {
    //当客户端和Service正常连接时调用
    @Override
    public void onServiceConnected(ComponentName name, IBinder iBinder) {
        Log.d("ServiceConnection", "onServiceConnected()");
    }

    //当客户端和Service丢失连接时调用
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d("ServiceConnection", "onServiceDisconnected()");
    }
};

其中的onServiceConnected方法会在Service和Activity发起通信(绑定[其实并不准确])时调用,我们观察一下可以发现,这个方法里面会传过来一个IBinder类型的参数,再想想我们之前的onBind方法里,是不是也正好返回了一个IBinder类型的参数?没错,onBind方法里返回的IBinder就正好传入了onServiceConnected!这样,Service和Activity的通信桥梁就通过ServiceConnection和IBinder搭建起来了,我们就可以把在onServiceConnected方法中获取到的IBinder强制类型转换为MyBinder,从而调用我们之前在MyBinder中声明的接口来知道Service中耗时任务的进度了!

但是这里我们还要了解一个知识点:在创建并开启一个服务后,我们将其绑定到Activity上后解绑,之后再次绑定的时候onBind方法是不会再次执行的,也就是说,onBind方法只会在Service第一次绑定的时候被调用!但是每次我们再次绑定的时候,不论onBind执行与否,ServiceConnection的onServiceConnected是一定会被执行的,因此我们就可以通过解绑再绑定的方式获取到后台Service耗时任务的完成进度了!

//绑定和解绑Service时需要用到的ServiceConnection类:用户绑定客户端和Service
private ServiceConnection serviceConnection = new ServiceConnection() {
    //当客户端和Service正常连接时调用
    @Override
    public void onServiceConnected(ComponentName name, IBinder iBinder) {
        Log.d("ServiceConnection", "onServiceConnected()");
        MyService.MyBinder myBinder = (MyService.MyBinder) iBinder;
        int process = myBinder.getProcess();
        Log.d("ServiceConnection", "当前进度:" + process);
    }

    //当客户端和Service丢失连接时调用
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d("ServiceConnection", "onServiceDisconnected()");
    }
};

我们执行一次绑定->解绑->绑定的效果如下:

D/TAG: Service:onCreate()
D/TAG: Service:onStartCommand()
D/TAG: Service:onBind()
D/ServiceConnection: onServiceConnected()
D/ServiceConnection: 当前进度:4
D/TAG: Service:onUnBind()
D/ServiceConnection: onServiceConnected()
D/ServiceConnection: 当前进度:13

这样我们就了解了Service、IBinder和ServiceConnection各自的作用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值