二十、Android-四大组件之Service

96 阅读6分钟

20. Android四大组件之Service

Service 的主要特点包括:

  1. 后台运行: Service 可以在应用程序的后台运行,即使用户切换到其他应用程序,它仍可以继续工作。
  2. 无用户界面: 与 Activity 不同,Service 不需要用户界面。它专注于执行后台任务而无需与用户进行交互。
  3. 长时间运行: Service 可以长时间运行,甚至在应用程序处于后台或设备处于休眠状态时也可以继续工作。
  4. 与主线程分离: 默认情况下,Service 运行在应用程序的主线程中。但是,您可以将 Service 配置为在单独的线程中运行,以避免阻塞应用程序的用户界面。
  5. 与 Activity 交互: Activity 可以与 Service 进行交互,发送命令、数据等,从而实现 Activity 与后台任务之间的通信。

Android 提供了两种类型的 Service:

  1. 普通 Service(Regular Service): 这是最常见的 Service 类型。它在主线程中运行,用于执行一些后台任务。如果任务可以在相对短的时间内完成,可以使用普通 Service。
  2. 前台 Service(Foreground Service): 前台 Service 是一种特殊类型的 Service,用于执行用户明确知晓的任务,例如播放音乐或进行导航。前台 Service 会在状态栏中显示一个持续的通知,以通知用户服务正在运行。这样可以防止系统在资源紧张时终止服务。

20.1 Service的基本用法

新建一个Service,右击com.example.servicetest→New→Service→Service

  • Exported: 属性表示是否将这个Service暴露给外部其他程序访问;
  • Enabled: 属性表示是否启用这个Service;

每一个Service都需要在AndroidManifest.xml文件中进行注册才能生效。这是Android四大组件共有的特点。Android Studio已自动帮我们完成了。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        ...
        android:theme="@style/Theme.ServiceTest"
        tools:targetApi="31">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>
​
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

常用的三个方法:

  • onCreate(): 会在Service创建的时候调用
  • onStartCommand(): 会在每次Service启动的时候调用
  • onDestroy(): 会在Service销毁的时候调用
class MyService : Service() {
​
    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }
​
    override fun onCreate() {
        super.onCreate()
    }
​
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }
​
    override fun onDestroy() {
        super.onDestroy()
    }
}

20.2 启动和停止Service

启动和停止都是借助Intent来实现的

        viewBinding.startService.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            // 启动Service
            startService(intent)
        }
​
        viewBinding.stopService.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            // 停止Service
            stopService(intent)
        }

20.3 Activity和Service通信

这就需要借助我们刚刚忽略的onBind()方法了

class MyService : Service() {
​
    private val myBinder = DownloadBinder()
​
    class DownloadBinder : Binder() {
        fun startDownload() {
            Log.d("MyService", "startDownload executed")
        }
        fun getProgress(): Int {
            Log.d("MyService", "getProgress executed")
            return 0
        }
    }
​
    override fun onBind(intent: Intent): IBinder {
        return myBinder
    }
    // ...
}
class MainActivity : AppCompatActivity() {
​
    private lateinit var viewBinding: ActivityMainBinding
    lateinit var downloadBinder: MyService.DownloadBinder
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            downloadBinder = service as MyService.DownloadBinder
            downloadBinder.startDownload()
            downloadBinder.getProgress()
        }
        override fun onServiceDisconnected(name: ComponentName) {
        }
    }
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)
​
        viewBinding.bindService.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
        viewBinding.unbindService.setOnClickListener {
            unbindService(connection)
        }
​
    }
}

onServiceConnected()方法方法会在Activity与Service成功绑定的时候调用;

onServiceDisconnected()方法只有在Service的创建进程崩溃或者被杀掉的时候才会调用;

点击绑定:这里我们仍然构建了一个Intent对象,然后调用bindService()方法将MainActivity和MyService进行绑定。bindService()方法接收3个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service进行绑定后自动创建Service。这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。

20.4 Service的生命周期

Android中的Service有以下生命周期方法:

  1. onCreate(): 当Service首次创建时调用,通常在这里进行一次性的初始化工作。
  2. onStartCommand(Intent intent, int flags, int startId): 当另一个组件(如Activity)通过调用startService()启动Service时,会调用此方法。它用于处理Service的主要逻辑。它还接收一个Intent对象,该对象包含启动Service时传递的参数。
  3. onBind(Intent intent): 如果Service绑定到另一个组件(如Activity)时,将调用此方法。它允许组件与Service进行通信。
  4. onUnbind(Intent intent): 当与Service绑定的所有组件都解绑时,会调用此方法。
  5. onRebind(Intent intent): 当Service已被绑定且再次绑定到另一个组件时,会调用此方法。
  6. onDestroy(): 当Service即将被销毁时调用,您可以在这里释放资源和执行清理操作。

Service的生命周期取决于其启动方式(startService()bindService())以及是否有其他组件绑定到它。不同的生命周期方法允许您控制Service的行为,并在不同的阶段执行必要的操作。

20.5 前台Service

从Android 8.0系统开始,只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。

如果你希望Service能够一直保持运行状态,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。

    override fun onCreate() {
        super.onCreate()
        Log.d("MyForegroundService", "onCreate")
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel("my_service", "前台Service通知", NotificationManager.IMPORTANCE_DEFAULT)
            manager.createNotificationChannel(channel)
        }
        val intent = Intent(this, MainActivity::class.java)
        val pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
        val notification = NotificationCompat.Builder(this, "my_service")
            .setContentTitle("This is content title")
            .setContentText("This is content text")
            .setSmallIcon(R.drawable.penguin_red)
            .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.penguin_blue))
            .setContentIntent(pi)
            .build()
        startForeground(1, notification)
    }

从Android 9.0系统开始,使用前台Service必须在AndroidManifest.xml文件中进行权限声明才行:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.servicetest">
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 ...
</manifest>

20.6 使用IntentService

class MyIntentService : IntentService("MyIntentService") {
​
    override fun onHandleIntent(intent: Intent?) {
        Log.d("MyIntentService", "Thread id is ${Thread.currentThread().name}")
    }
​
    override fun onDestroy() {
        super.onDestroy()
        Log.d("MyIntentService", "onDestroy")
    }
}
​

IntentService 是 Android 中的一个特殊类型的 Service,具有以下特点:

  1. 异步处理: IntentService 是为了处理后台任务而设计的,它在后台线程中执行任务,因此不会阻塞主线程。这使得它非常适合处理需要耗时操作的任务,如下载、文件处理等。
  2. 自动停止: 一旦所有请求处理完毕,IntentService 会自动停止自己,无需手动调用stopService()stopSelf()来停止它。这简化了管理和维护。
  3. 请求队列: IntentService 会自动将传递给它的 Intent 放入队列中逐个处理。这确保了每个请求按顺序处理,而不会同时执行多个请求。
  4. 适用于多个请求: 您可以多次启动IntentService,每个请求都会排队处理,不会相互干扰。
  5. 默认实现: IntentService 默认实现了 onCreate()onStartCommand()onDestroy() 方法,您只需要实现 onHandleIntent() 方法,其中处理具体的工作。
  6. 线程安全: IntentService 内部处理多个请求的线程安全性,因此您不必担心多线程同步问题。

总之,IntentService 是一个方便的工具,用于在后台执行异步任务,它简化了多线程和生命周期管理,使得开发者能够专注于任务本身而不必处理繁琐的线程和生命周期问题。