Service基础使用


  • Service基础使用
  • 生命周期
  • 绑定式服务(Bound启动方式)
  • 非绑定式服务(Started启动方式)
  • 通信
  • 前台运行服务
  • startService 与 bindService 差异
  • Service 与 IntentService
  • 总结


Service是一个应用组件,它表示一个应用期望去执行一个不与用户交互的较长的操作或者通过一些功能为其他应用去使用。

作为android四大组件之一,Activity负责与用户交互,而Service则负责不与用户交互的那些工作,比如后台下载,后台播放音乐等需在后台长期运行的服务。

无用户界面、在后台运行、生命周期长

生命周期

service有两种生命周期,由于启动方式不同生命周期也不同。

Android项目中你是如何做组件化拆分的 android四个组件用途_ide

startService()

@Override
    public void onCreate() {
        super.onCreate();
        // 创建服务
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
        // 开始服务
    }

     @Override
    public void onDestroy() {
        super.onDestroy();
        // 销毁服务
    }

bindService()

@Override
    public void onCreate() {
        super.onCreate();
        // 创建服务
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
        //绑定服务
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
        // 解绑服务
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 销毁服务
    }

绑定式服务(Bound启动方式)

绑定服务

通过bindService()启动的Service

// 绑定服务
    ServiceConnection mConnection = ServiceConnection() { ... };
    Intent intent = new Intent(this, MyService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

解除绑定

// 解除绑定
    unbindService(mConnection);

关于绑定Service的流程

  • 实现一个ServiceConnection ,必须重写两个方法
  • onServiceConnection():系统调用这个来传送在service的onBind()中返回的IBinder.
  • OnServiceDisconnected():Android系统在同service的连接意外丢失时调用这个.比如当service崩溃了或被强杀了
  • 定义一个Intent,我们android四大组件都使用Intent进行交互
  • 传一个int值的flag:
  • 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的进程优先级,使用这个标识后,会无视其他标识。

非绑定式服务(Started启动方式)

启动服务

// 启动服务
Intent intent = new Intent(this, MyService.class);
startService(intent);

停止服务

stopService(intent)

关于非绑定服务

  • 使用这个方法启动的服务,再次调用startService()传入Intent即可与服务通信,因为这种方式启动的服务在完整的生命周期内onCreate()只会执行一次,而onStartCommand()会执行多次,我们每次调用startService()。
  • 服务不会停止,直到服务自己调了stopSelf()方法或者其他组件调用了stopService() 方法或者服务自身报错崩溃等情况才能停止服务。

通信

与服务间的通信大体有一下三种

  1. 万能通信机制:广播与本地数据共享(ContentProvider/SharePreference等等)
  2. Started启动模式下的startService()方法:传入一个Intent即可与服务通信,每次调用startService时,在onStartCommond方法中去处理Intent即可。
  3. Bound启动方式下
  1. Service中实现IBinder进行通讯
  2. 使用Messenger(简化版AIDL)
  3. AIDL
    如果你的Service只是应用在本进程中,那么只要使用第一种方法就可以满足通讯,如果是其他进程的Service那么你就要采取进程间通讯了(第2、3种方式)

前台运行服务

服务可以通过调用startForeground来转变为前台优先级

public final void startForeground(int id, Notification notification)

这个方法最后还是要调NotificationManager.notify(int id, Notification notification) 所以前面的id 就相当于notify方法的id,notification就是我们的通知体

如果你想从前台移除这个Service了,那就调用stopForeground

public final void stopForeground(boolean removeNotification)

参数是一个移除通知的标识,如果服务在前台运行时候被停止,状态栏的通知也会被移除。

startService 与 bindService 差异

大体先总结一下几点:


  1. 启动方式:这是最明显的,一个调startService(),一个调bindService();
  2. startService启动之后,正常来讲是无法返回结果的,也就是说前端页面上是没有办法干涉Service内部的动作的(你要说发广播或者监听之类的我也没法反驳,但是页面级别UI操作至少没办法)
  3. 而bindService由于里面重写了IBinder方法,我们可以和service交互,发送请求,得到结果甚至执行IPC通信,而且我们重载onServiceConnected还可以监听Service的状态。

然而这两种方式也并不是完全分开的,在我看来只是不同地方不同用法而已
也就是说我们start了一个Service之后,如果你想与之绑定,对内部进行操作,还是可以bindService的,只不过如果你bind了之后stopService()或 stopSelf()实际上并不能停止这个service,除非所有的客户都解除绑定。