Android - Kotlin Service

1. Basic Service 

The onCreate() method is called when the Service is first created, and the onStartCommand() method is called every time the Service is started.

The first time you click the "Start Service" button, the Service has not yet been created at this time, so both methods will be executed. After that, if you click the "Start Service" button several times in a row, you will find that only the onStartCommand() method can got carried out.

class MyService : 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()
 }
}

2. Start and stop

An Intent object is constructed and the startService() method is called to start MyService.

In the click event of the "Stop Service" button, we also build an Intent object and call the stopService() method to stop MyService.

The startService() and stopService() methods are both defined in the Context class, so we can call these two methods directly in the Activity.

In addition, the Service can also stop running by itself, just call the stopSelf() method inside the Service.

class MainActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main)
 startServiceBtn.setOnClickListener {
 val intent = Intent(this, MyService::class.java)
 startService(intent) // 启动Service
 }
 stopServiceBtn.setOnClickListener {
 val intent = Intent(this, MyService::class.java)
 stopService(intent) // 停止Service
 }
 }
}

3. Activity and Service communicate

We created a new DownloadBinder class and let it inherit from Binder, and then provide methods to start downloading and view the download progress inside it. Of course, these are just two simulation methods, and do not realize the real function. We have printed a line of logs in these two methods respectively. Next, an instance of DownloadBinder is created in MyService, and then this instance is returned in the onBind() method, so that all the work in MyService is completed.

class MyService : Service() {
 private val mBinder = 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 mBinder
 }
 ...
}

The two buttons are used to bind and unbind the Service respectively, so who needs to bind the Service? Of course it is Activity. When an Activity is bound to a Service, the method provided by the Binder in the Service can be called. Modify the code in MainActivity

class MainActivity : AppCompatActivity() {
 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?) {
 ...
 bindServiceBtn.setOnClickListener {
 val intent = Intent(this, MyService::class.java)
 bindService(intent, connection, Context.BIND_AUTO_CREATE) // 绑定Service
 }
 unbindServiceBtn.setOnClickListener {
 unbindService(connection) // 解绑Service
 }
 }
}

We first created an anonymous class implementation of ServiceConnection, and rewritten the onServiceConnected() method and onServiceDisconnected() method in it.

The onServiceConnected() method will be called when the Activity is successfully bound to the Service, while the onServiceDisconnected() method will only be called when the Service creation process crashes or is killed. This method is not commonly used. Then in the onServiceConnected() method, we get an instance of DownloadBinder through downward transformation. With this instance, the relationship between Activity and Service becomes very close. Now we can call any public method in DownloadBinder according to the specific scenario in the Activity, that is, to realize the function of directing the Service to do whatever the Service does.

Here is still just a simple test, and the startDownload() and getProgress() methods of DownloadBinder are called in the onServiceConnected() method. Of course, the Activity and the Service have not been bound yet. This function is completed in the click event of the "Bind Service" button. As you can see, here we still build an Intent object, and then call the bindService() method to bind MainActivity and MyService. The bindService() method receives 3 parameters, the first parameter is the Intent object just constructed, the second parameter is the instance of the ServiceConnection created earlier, and the third parameter is a flag bit, which is passed in BIND_AUTO_CREATE here After Activity and Service are bound, Service is automatically created. This will cause the onCreate() method in MyService to be executed, but the onStartCommand() method will not be executed.

Fourth, the front desk Service

Starting from the Android 8.0 system, only when the application remains visible in the foreground can the Service ensure stable operation. Once the application enters the background, the Service may be recycled by the system at any time. And if you want the Service to keep running, you can consider using the foreground Service. The biggest difference between the foreground Service and the normal Service is that it will always have a running icon displayed in the status bar of the system. After pulling down the status bar, you can see more detailed information, which is very similar to the effect of a notification

class MyService : Service() {
 ...
 override fun onCreate() {
 super.onCreate()
 Log.d("MyService", "onCreate executed")
 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, 0)
 val notification = NotificationCompat.Builder(this, "my_service")
 .setContentTitle("This is content title")
 .setContentText("This is content text")
 .setSmallIcon(R.drawable.small_icon)
 .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.large_icon))
 .setContentIntent(pi)
 .build()
 startForeground(1, notification)
 }
 ...
}

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

5. IntentService

class MyIntentService : IntentService("MyIntentService") {
 override fun onHandleIntent(intent: Intent?) {
 // 打印当前线程的id
 Log.d("MyIntentService", "Thread id is ${Thread.currentThread().name}")
 }
 override fun onDestroy() {
 super.onDestroy()
 Log.d("MyIntentService", "onDestroy executed")
 }
}

Here, it is first required that the constructor of the parent class must be called first, and a string be passed in. This string can be specified at will and is only useful during debugging. Then implement the abstract method onHandleIntent() in the subclass, which can handle some time-consuming logic without worrying about ANR

start up

class MainActivity : AppCompatActivity() {
 ...
 override fun onCreate(savedInstanceState: Bundle?) {
 ...
 startIntentServiceBtn.setOnClickListener {
 // 打印主线程的id
 Log.d("MainActivity", "Thread id is ${Thread.currentThread().name}")
 val intent = Intent(this, MyIntentService::class.java)
 startService(intent)
 }
 }
}

register

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.servicetest">
 <application
 android:allowBackup="true"
 android:icon="@mipmap/ic_launcher"
 android:roundIcon="@mipmap/ic_launcher_round"
 android:label="@string/app_name"
 android:supportsRtl="true"
 android:theme="@style/AppTheme">
 ...
 <service
 android:name=".MyIntentService"
 android:enabled="true"
 android:exported="true"/>
 </application>
</manifest>

Guess you like

Origin blog.csdn.net/m0_59482482/article/details/129930782