Android-Service再探

Service

  • 先看一下官方的解释
  • A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding declaration in its package’s AndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().
  • 有道的翻译
  • 服务是应用程序组件,它表示应用程序希望在不与用户交互的情况下执行长时间运行的操作,或者提供其他应用程序使用的功能。每个服务类必须在其包的AndroidManifest.xml中具有相应的声明。可以使用Context.startService()和Context.bindService()启动服务。
  • 注意,服务和其他应用程序对象一样,运行在它们的宿主进程的主线程中。这意味着,如果您的服务要执行任何CPU密集型(如MP3播放)或阻塞(如网络)操作,那么它应该生成自己的线程来执行该操作。可以在进程和线程中找到关于此的更多信息。IntentService类可以作为服务的标准实现来使用,它有自己的线程来安排要完成的工作。
  • 并且,服务是运行在主线程当中的

本地服务使用详解

  • 一般的话,我们使用服务都是去干我们的事儿的,而这些事儿从来都是不确定的,所以说,我们通常是需要可以实时操作服务的,那么在此,就不再讨论直接启动服务的方法了
  • 我们直接来看一个活动时怎么和服务建立连接,并保持安全的通信的

第一步,创建我们的服务

  • 首先我们需要创建我们自己的服务类的,并让他继承自Service类
public class MyLocalService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
  • Service本身是个抽象类,只有一个onBinder方法需要我们实现,不过,显然我们要准确的操作服务,这么一个方法远远不够
  • 我们再重写他的Create方法,onDestroy方法,所以我们的服务类就变成了
public class MyLocalService extends Service {

    //当Activity或者其他组件调用StartService或者bindService时,如果服务还没有创建,就调用这个方法
    //如果已经创建,就不调用
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //绑定的时候会通过一系列的回调到此方法,他的作用是我们可以在我们的组件Activity或者其他组件绑定服务的同时拿到操作服务的引用
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    //最后释放资源
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
  • 关于方法的说明我在注释中已经说得差不多了,我们接着往下看

第二步,提供给外部操作服务的方式

  • 在上面的注释中我说过在绑定的时候需要返回给组件操作服务的引用,那么这个引用是什么呢?
  • 这里根据官方说法,他是一个IBinder的接口继承类,所以说,我们需要返回给绑定此服务的组件一个IBInder接口的子类的引用
  • 因为IBinder的需要实现的方法实在太多,而我们实际上是用不到那些方法的,所以Android为我们提供了一个这个接口的实现类,这个类做的方法很简单,将这个接口的所有方法能实现的实现,不方便写逻辑的方法也给他空实现,所以我们直接继承这个类,就不用去写那么多实现方法了
  • 所以,往我们的MyLocalService 服务类中添加继承BInder的内部类,并且改变我们刚才那个onBinder方法的返回值,这里我只贴增加的部分和修改的部分了
class MyBinder extends Binder{

}
private final IBinder mBinder = new MyBinder();
//绑定的时候会通过一系列的回调到此方法,他的作用是我们可以在我们的组件Activity或者其他组件绑定服务的同时拿到操作服务的引用
@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}
  • 有人可能会说,你直接返回个new MyBinder不就行了嘛,为什么还要先创建一个final私有变量,然后再返回呢,这里呢,是因为,通常我们的服务可能会被绑定到不止一个组件之上,如果我们每个组件都拿一个new MyBInder对象,虽然说不会出问题,但是造成了资源浪费啊,完全给他们使用一个对象就足够,对吧
  • 就这么简单,还记得我在上面的注释中是怎么说的嘛?返回一个可操作服务的引用,所以,在这里我们返回的这个MyBinder对象,就是我们的组件拿来操作服务的东西,可是你发现,这个类空空如也,怎么操作呢,简单啊,我们给他写一些操作的方法就行啦
class MyBinder extends Binder{

   MyLocalService getService(){
       return MyLocalService.this;
   }

   public void doOurWantTodoThings(){
       Log.d(TAG, "doOurWantTodoThings: 在这里可以调用我们在服务里面写好的方法哟");
   }
}
  • 这里我为什么还写了上面那个方法呢,因为我看的是google官方给的示例,可以看到官方的做法是,我直接拿这个引用把整个服务的引用给你,你想咋办就咋办呐,哇,虽然说这样简单,但是你想想,有可能我们绑定的不只是一个组件,一些组件我们不需要让他接触到某些方法,这样做就有些太过大胆了,就好比你将家里的某个房间出租,你肯定不能直接把你家所有钥匙给租客啊,对吧,所以我们只能给他一部门钥匙
  • 所以我们一般写的话还是不要直接将service的引用暴露出去,我们只需要给外部提供一些方法够用就可以了,比方说我在这里就写了一个方法doOurWantTodoThings(做我们想要做的事情,哈哈 名字是不是很灵性),我们在这里就可以调用我们写在service里的一些方法了
  • 所以,如果不是很特殊的需求的话,还是不要写第一个那种样子
  • 到这里,我们的服务端除了具体的逻辑代码之外已经写完了,那么服务创建好了,接下来就该看看我们的Activity怎么去绑定了

第三步,编写组件调用部分

  • 在这一部分代码其实很简单,我们要做的就是在需要的时候bindService,并且在不需要的时候UNBindService就好啦,
  • 不过这里有个地方需要注意,在服务和组件绑定的时候,还需要一个中间者,这个中间者是用来传递数据的
  • 就像我们之前说过,之所以使用绑定方式,因为绑定这种方式他为我们会返回一个对象引用,我们通过这个对象的引用可以很方便的使用服务里面的方法
  • 那么,这个可以操作服务的对象是什么时候传给我们的呢,去看了一下bindService的返回值,他是ComponentName类型的东西,通过查阅资料发现,这个东西只是我们绑定的服务的类名
  • 那么,这个时候这个中间者就派上用场了,服务通过这个中间者把那个Binder对象的引用传给我们的组件,组件就可以尽情的为所欲为了,所以我们先来看看这个中间者怎么编写
  • 它是一个待实现的接口,我们一般编写就是创建一个接口的匿名实现类,如下
private ServiceConnection mConnection = new ServiceConnection() {

        //当绑定服务的时候调用,带着被绑定的服务引用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mShouldUnbind = true;
            mLocalBinder = (LocalService.LocalBinder) service;
            mBoundService = ((LocalService.LocalBinder)service).getService();
            Log.d(TAG, "onServiceConnected: 服务已经连接 ComponentName = " + name);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBoundService = null;
            Log.d(TAG, "onServiceDisconnected: 服务被取消连接 ComponentName = " + name );
        }
    };
  • 通过名字我们就能猜出来,一个是绑定的时候调用的,一个是取消绑定的时候调用的,在绑定的时候我们发现,他的参数有两个,第一个是类名,第二个是一个IBinder类型的参数,通过我们前面的了解,这肯定就是我们所需要的操作服务的对象了
  • 那么,服务有了,中间者有了,我们的activity我也提前建好了,接下来就看看怎么连接吧
  • 看看我们的Activity当中的代码
    private boolean mShouldUnbind;//绑定的状态
    //绑定服务
    void doBindService(){
        if(bindService(new Intent(this, LocalService.class),mConnection, Context.BIND_AUTO_CREATE)){
            Log.d(TAG, "doBindService: 绑定成功");
        }else {
            Log.d(TAG, "doBindService: 绑定失败");
        }
    }
    //取消绑定
    void doUnbindService() {
        if (mShouldUnbind) {
            unbindService(mConnection);
            mShouldUnbind = false;
        }
    }
  • 当我们调用bindService的时候,如果启动成功,将会返回true,否则返回false,当启动成功的时候,就会去通过回调的方式调用我们之前说的中间者的onServiceConnected这个方法,于是这个IBinder对象也就成功的拿到了
  • 这里要注意的时候,启动是在主线程,但是绑定传递这个IBinder并不是在主线程的,所以说,虽然说可能现在bindService已经返回true,但是我们中间者的onServiceConnected方法可能还未调用,或者说还未调用完毕,在这个时候,我们是不能操作服务的,所以说在使用的时候,一定要确保onServiceConnected方法已经执行完毕,也就是我这里在他的内部设置了一个Boolean变量,以确保我们在使用的时候不出错
  • 这里再解释一下我们在bindService方法里面的第三个flag参数,这个参数表明了将要绑定的服务和组件的优先级关系,这个优先级可能涉及到在内存回收的时候,先销毁组件还是先销毁服务
  • 这里我们用的参数是Context.BIND_AUTO_CREATE,官方对他的解释是:bindService的标志(Intent, ServiceConnection, int):只要绑定存在,就自动创建服务。注意,这将创建服务及其服务。onStartCommand(Intent, int, int)方法仍然只会因为对startService(Intent)的显式调用而被调用。但是,即使没有这些,在创建服务时,它仍然为您提供对服务对象的访问。
  • 关于其他的更多参数还请参阅官方文档

服务使用拓展

  • 有时候我们会涉及到希望将我们服务的工作状态实时的展示给用户
  • 除了显示设置前台服务之外,我们还可以手动的为服务添加Notification,比如,在我们创建的service类内部
Notification notification = new Notification.Builder(this)
                .setSmallIcon(R.drawable.stat_sample) 
                .setTicker(text) 
                .setWhen(System.currentTimeMillis()) 
                .setContentTitle(getText(R.string.local_service_label))
                .setContentText(text) 
                .setContentIntent(contentIntent)  
                .build();
  • 然后我们通过在服务内部获取NotificationManager的方式来操作
NotificationManager mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
  • 在适当的时候显示通知
mNM.notify(1, notification);
  • 或者取消显示
mNM.cancel(1);
  • 还有一点要注意的是,服务是直接在主线程里面的,所以我们如果要在服务里面做耗时的操作的话,还是另起一个新线程吧
  • Service的基本使用到这里就结束了,接下来我们重点分析一下service的启动过程

猜你喜欢

转载自blog.csdn.net/asffghfgfghfg1556/article/details/81239617