示例ServiceTest
MainActivity:
package com.example.servicetest
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.Button
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
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?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val startServiceBtn=findViewById<Button>(R.id.startServiceBtn)
val stopServiceBtn=findViewById<Button>(R.id.stopServiceBtn)
val bindServiceBtn=findViewById<Button>(R.id.bindServiceBtn)
val unbindService=findViewById<Button>(R.id.unbindService)
val startIntentServiceBtn=findViewById<Button>(R.id.startIntentServiceBtn)
startServiceBtn.setOnClickListener {
val intent=Intent(this,MyService::class.java)
startService(intent)
}
stopServiceBtn.setOnClickListener {
val intent=Intent(this,MyService::class.java)
stopService(intent)
}
bindServiceBtn.setOnClickListener {
val intent=Intent(this,MyService::class.java)
bindService(intent,connection,Context.BIND_AUTO_CREATE)//绑定Service
}
unbindService.setOnClickListener {
unbindService(connection)
}
startIntentServiceBtn.setOnClickListener {
Log.d("MainActivity", "Thread id is ${Thread.currentThread().name}")
val intent=Intent(this,yzlIntentService::class.java)
startService(intent)
}
}
}
activity_main布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/startServiceBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service"/>
<Button
android:id="@+id/stopServiceBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service"/>
<Button
android:id="@+id/bindServiceBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bind Service"/>
<Button
android:id="@+id/unbindService"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unbind Service"/>
<Button
android:id="@+id/startIntentServiceBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start IntentService"/>
</LinearLayout>
yzlIntentActivity:
package com.example.servicetest
import android.app.IntentService
import android.content.Intent
import android.util.Log
class yzlIntentService:IntentService("yzlIntentService") {
override fun onHandleIntent(intent: Intent?) {
//打印当前线程的id
Log.d("yzlIntentService", "Thread id is ${Thread.currentThread().name}")
//在这个方法中可以处理一些耗时逻辑,不会ANR
}
override fun onDestroy() {
super.onDestroy()
Log.d("yzlIntentService", "onDestroy executed")
}
}
示范代码
MainActivity:
package com.example.servicewyr
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import java.io.FileNotFoundException
class MainActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
private lateinit var selectedImageUri: Uri
private var factor = 1.0f
val fromAlbum = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imageView = findViewById(R.id.imageView)
val fromAlbumBtn: Button = findViewById(R.id.takePhotoBtn)
val processButton: Button = findViewById(R.id.processButton)
fromAlbumBtn.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(intent, fromAlbum)
}
processButton.setOnClickListener {
startService(Intent(this, ImageProcessingService::class.java).apply {
putExtra("factor", factor)
putExtra("selectedImageUri", selectedImageUri.toString())
})
}
val seekBar: SeekBar = findViewById(R.id.SeekBar01)
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
factor = progress / 100f
}
override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
override fun onStopTrackingTouch(seekBar: SeekBar?) = Unit
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == fromAlbum && resultCode == Activity.RESULT_OK && data != null) {
selectedImageUri = data.data!!
val bitmap = getBitmapFromUri(selectedImageUri)
imageView.setImageBitmap(bitmap)
}
}
private fun getBitmapFromUri(uri: Uri): Bitmap {
return contentResolver.openInputStream(uri)?.use { input ->
BitmapFactory.decodeStream(input)
} ?: throw FileNotFoundException("File not found for the Uri: $uri")
}
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == "imageProcessed") {
val processedBytes = intent.getByteArrayExtra("processedBitmap")
val processedBitmap =
processedBytes?.let { BitmapFactory.decodeByteArray(processedBytes, 0, it.size) }
imageView.setImageBitmap(processedBitmap)
}
}
}
override fun onResume() {
super.onResume()
val filter = IntentFilter("imageProcessed")
registerReceiver(broadcastReceiver, filter)
}
override fun onPause() {
super.onPause()
unregisterReceiver(broadcastReceiver)
}
}
onResume
和onPause
是Android生命周期方法,分别在活动可见和不可见时调用。在onResume
中注册BroadcastReceiver
,在onPause
中注销,以防止内存泄漏。
ImageProcessingService:
package com.example.servicewyr
import android.app.IntentService
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import android.net.Uri
import java.io.ByteArrayOutputStream
import java.io.FileNotFoundException
import java.io.InputStream
class ImageProcessingService : IntentService("ImageProcessingService") {
override fun onHandleIntent(intent: Intent?) {
val factor = intent?.getFloatExtra("factor", 1.0f)
val imageUriStr = intent?.getStringExtra("selectedImageUri")
val imageUri = Uri.parse(imageUriStr)
val originalBitmap = getBitmapFromUri(imageUri)
val processedBitmap = processImageWithFactor(factor ?: 1.0f, originalBitmap ?: return)
val broadcastIntent = Intent("imageProcessed")
val byteArray = convertBitmapToBytes(processedBitmap)
broadcastIntent.putExtra("processedBitmap", byteArray)
sendBroadcast(broadcastIntent)
}
private fun getBitmapFromUri(uri: Uri): Bitmap {
return contentResolver.openInputStream(uri)?.use { input ->
BitmapFactory.decodeStream(input)
} ?: throw FileNotFoundException("File not found for the Uri: $uri")
}
private fun processImageWithFactor(factor: Float, originalBitmap: Bitmap): Bitmap {
val colorMatrix = ColorMatrix().apply {
setScale(factor, factor, factor, 1f)
}
val paint = Paint().apply {
colorFilter = ColorMatrixColorFilter(colorMatrix)
}
val processedBitmap = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, originalBitmap.config)
val canvas = android.graphics.Canvas(processedBitmap)
canvas.drawBitmap(originalBitmap, 0f, 0f, paint)
return processedBitmap
}
private fun convertBitmapToBytes(bitmap: Bitmap): ByteArray {
val outputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
return outputStream.toByteArray()
}
companion object {
const val FACTOR_KEY = "factor"
const val SELECTED_IMAGE_URI_KEY = "selectedImageUri"
const val BITMAP_KEY = "processedBitmap"
}
}