Android Service基础

Posted ITRenj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Service基础相关的知识,希望对你有一定的参考价值。

Android Service 代码地址

简单介绍

Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。服务可以分为前台、后台和绑定三种类型。

分类

前台服务

前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

后台服务

后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
注意:如果您的应用面向 API 级别 26 或更高版本,当应用本身未在前台运行时,系统会对运行后台服务施加限制。

绑定服务

当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

说明

  1. 虽然分开讨论启动服务和绑定服务,但服务可以同时以这两种方式运行,换言之,它既可以是启动服务(以无限期运行),亦支持绑定。唯一的问题在于您是否实现一组回调方法:onStartCommand()(让组件启动服务)和 onBind()(实现服务绑定)。
  2. 无论服务是处于启动状态还是绑定状态(或同时处于这两种状态),任何应用组件均可像使用 Activity 那样,通过调用 Intent 来使用服务(即使此服务来自另一应用)。不过,可以通过清单文件将服务声明为私有服务(通过添加 android:exported 属性并将其设置为 false),并阻止其他应用访问该服务。
  3. 服务在其托管进程的主线程中运行,它既不创建自己的线程,也不在单独的进程中运行(除非通过 android:process=“progress name” 另行指定)。 如果服务将执行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或联网),则应通过在服务内创建新线程来完成这项工作。通过使用单独的线程降低发生“应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。

基本使用

创建服务

创建服务必须创建 Service 的子类(或使用它的一个现有子类),同时Service也是Android四大组件之一,所以需要在清单文件中进行注册。

public class LocalService extends Service 
	 @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        Log.i(TAG, "onBind: ");
        return null;
    

	 @Override
    public void onCreate() 
        super.onCreate();
        Log.i(TAG, "onCreate: ");
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        Log.i(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    

    @Override
    public void onDestroy() 
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    

清单文件中进行配置:

 <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=".local.LocalService" />
    
</application>

启动服务

应用组件(如 Activity)可通过调用 startService() 方法并传递 Intent 对象(指定服务并包含待使用服务的所有数据)来启动服务。服务会回调 onStartCommand() 方法(onStart()方法也会调用,已过时),在 onStartCommand() 方法接收此 Intent。

停止服务

服务启动后,其生命周期即独立于启动它的组件。即使系统已销毁启动服务的组件,该服务仍可在后台无限期地运行。因此,服务应在其工作完成时通过调用 stopSelf() 来自行停止运行,或者由另一个组件通过调用 stopService() 来将其停止。如果服务同时处理多个对 onStartCommand() 的请求,则不应在处理完一个启动请求之后停止服务,因为服务可能已收到新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为避免此问题,可以使用 stopSelf(int) 确保服务停止请求始终基于最近的启动请求。换言之,在调用 stopSelf(int) 时,需要传递与停止请求 ID 相对应的启动请求 ID(传递给 onStartCommand() 的 startId)。此外,如果服务在调用 stopSelf(int) 之前收到新启动请求,则 ID 不匹配,服务也不会停止。

绑定服务

如需与 Activity 或者其他应用组件中的服务进行交互,或需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。要创建绑定服务,需要通过实现 onBind() 回调方法返回 IBinder,从而定义与服务进行通信的接口。然后,其他应用组件通过调用 bindService() 方法来启动并绑定服务,并通过ServiceConnection的回调方法获取到的IBinder开始调用与服务相关的方法。

绑定服务只用于与其绑定的应用组件,多个客户端可以同时绑定到服务。完成与服务的交互后,客户端应该要通过调用 unbindService() 来取消绑定。如果没有绑定到服务的客户端或所有绑定的客户端都通过 unbindService() 方法取消绑定了,则系统会销毁该服务。而不必像通过 onStartCommand() 启动的服务那样,以相同方式停止绑定服务。

创建绑定Service

// 定义绑定服务方法
public interface ILocalBinder 
    String getServiceMsg();

    void serviceShowToast(String msg);


// 创建服务
public class LocalBinderService extends Service 
    private final String TAG = LocalService.class.getSimpleName();

    private class LocalBinder extends Binder implements ILocalBinder 

        @Override
        public String getServiceMsg() 
            return new Random().nextInt(100) + 1 + " service info";
        

        @Override
        public void serviceShowToast(String msg) 
            Toast.makeText(LocalService.this, "service: " + msg, Toast.LENGTH_SHORT).show();
        
    

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        Log.i(TAG, "onBind: ");
        return new LocalBinder();
    

	// ...

操作Service

private ILocalBinder iLocalBinder;

public void binderService(Context context) 
    ServiceConnection conn = new ServiceConnection() 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            iLocalBinder = (ILocalBinder) service;
        

        @Override
        public void onServiceDisconnected(ComponentName name) 

        
    ;
    // 绑定服务
    Intent intent = new Intent(context, LocalBinderService.class);
    context.bindService(intent, conn, Service.BIND_AUTO_CREATE);

    // 调用Service的方法
    String serviceMsg = iLocalBinder.getServiceMsg();
    iLocalBinder.serviceShowToast("调动Service方法");
    
    // 解绑服务
    context.unbindService(conn);

在前台运行服务

前台服务是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。前台服务必须为状态栏提供通知,将其放在运行中的标题下方。这意味着除非将服务停止或从前台移除,否则不能清除该通知。

注意: 在 Android 9(API 级别 28)或更高版本中使用前台服务,必须请求 FOREGROUND_SERVICE 权限。这是一种普通权限,因此,系统会自动为请求权限的应用授予此权限,否则系统会抛出 SecurityException

让服务运行在前台

通过调用Service的 startForeground(int id, Notification notification) 方法【参数1:通知标识(不能为 0)参数2:用于状态栏的 Notification】,可以让服务运行在前台。

移出前台运行的服务

通过调用Service的 stopForeground(boolean removeNotification)。指定是否需同时移除状态栏通知。此方法并不会停止服务。但是,如果在服务运行于前台时将其停止,则通知也会随之移除。

特别注意

  1. 不要为服务Intent声明过滤器

    为确保应用的安全性,在启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务会响应 Intent,而用户也无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),则系统会抛出异常。

  2. Android 8适配

    在Android 8.0 以上系统不允许后台应用创建后台服务。 因此引入了一种全新的方法,即通过 Context.startForegroundService() 方法在前台启动新服务,通过调用该方法,在系统创建服务后,应用有五秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知,如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR。

以上是关于Android Service基础的主要内容,如果未能解决你的问题,请参考以下文章

Android基础:Service —— 默默为你服务

可以从 Android 应用程序中跳过曲目吗?

Android Service基础

Android SoundPool 不打开很多曲目/如何创建 CBR OGG 文件?

Android中的Service基础

Spotify Android 集成,是不是可以在没有高级帐户的情况下预览曲目?