安卓截屏需要一个前台服务(需要权限)
开启前台服务需要显示一个通知(需要设置标题栏,文字等)
1.开启相关权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<service
android:name=".service.ScreenRecorderService"
android:exported="false"
android:foregroundServiceType="mediaProjection"
tools:ignore="Instantiatable" />
2.在Activity里判断权限&&启动服务并且使用MediaProjectionManager开启截屏:
3.在onActivityResult()里面对图像数据进行处理:
相关代码如下:
Activity: //启动MediaService服务
fun StartMediaService() {
if (isstartservice) {
startService(Intent(this, MediaService::class.java))
isstartservice = false
}
}
val REQUEST_MEDIA_PROJECTION = 10001
private var mediaProjectionManager: MediaProjectionManager? = null
// 申请截屏权限
private fun getScreenShotPower() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
if (mediaProjectionManager != null) {
val intent = mediaProjectionManager!!.createScreenCaptureIntent()
startActivityForResult(intent, REQUEST_MEDIA_PROJECTION)
}
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_MEDIA_PROJECTION && data != null) {
// val mediaProjection = mediaProjectionManager!!.getMediaProjection(RESULT_OK, data)
// val bitmap = screenShot(mediaProjection)
// val bs = getBitmapByte(bitmap)
// //对图片进行处理逻辑 例如可以保存本地、进行图片分享等等操作
// Log.e("WWW", "EEE" + bitmap)
// binding.img.setImageBitmap(bitmap)
}
}
处理图片:
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun screenShot(mediaProjection: MediaProjection): Bitmap? {
val wm1 = this.windowManager
val width = wm1.defaultDisplay.width
val height = wm1.defaultDisplay.height
Objects.requireNonNull(mediaProjection)
@SuppressLint("WrongConstant") val imageReader: ImageReader =
ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 60)
var virtualDisplay: VirtualDisplay? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
virtualDisplay = mediaProjection.createVirtualDisplay(
"screen",
width,
height,
1,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.getSurface(),
null,
null
)
}
SystemClock.sleep(1000)
//取最新的图片
val image: Image = imageReader.acquireLatestImage()
// Image image = imageReader.acquireNextImage();
//释放 virtualDisplay,不释放会报错
virtualDisplay!!.release()
return image2Bitmap(image)
}
//将Image转为Bitmap
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun image2Bitmap(image: Image?): Bitmap? {
if (image == null) {
println("image 为空")
return null
}
val width: Int = image.getWidth()
val height: Int = image.getHeight()
val planes: Array<Image.Plane> = image.planes
val buffer: ByteBuffer = planes[0].buffer
val pixelStride: Int = planes[0].pixelStride
val rowStride: Int = planes[0].rowStride
val rowPadding = rowStride - pixelStride * width
val bitmap =
Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(buffer)
//截取图片
// Bitmap cutBitmap = Bitmap.createBitmap(bitmap,0,0,width/2,height/2);
//压缩图片
// Matrix matrix = new Matrix();
// matrix.setScale(0.5F, 0.5F);
// System.out.println(bitmap.isMutable());
// bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
image.close()
return bitmap
}
// 位图转 Byte
private fun getBitmapByte(bitmap: Bitmap?): ByteArray {
val out = ByteArrayOutputStream()
// 参数1转换类型,参数2压缩质量,参数3字节流资源
bitmap!!.compress(Bitmap.CompressFormat.PNG, 100, out)
try {
out.flush()
out.close()
} catch (e: IOException) {
e.printStackTrace()
}
return out.toByteArray()
}
Service:
package com.allynav.iefa.service
import android.R
import android.app.*
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
/**
*@author zd
*@time 2023/4/10 9:35
*@description :
*
**/
class MediaService : Service() {
private val NOTIFICATION_CHANNEL_ID = "com.tencent.trtc.apiexample.MediaService"
private val NOTIFICATION_CHANNEL_NAME = "com.tencent.trtc.apiexample.channel_name"
private val NOTIFICATION_CHANNEL_DESC = "com.tencent.trtc.apiexample.channel_desc"
override fun onCreate() {
super.onCreate()
startNotification()
}
fun startNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Call Start foreground with notification
val notificationIntent = Intent(this, MediaService::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0)
val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setLargeIcon(
BitmapFactory.decodeResource(
getResources(),
R.drawable.alert_dark_frame
)
)
.setSmallIcon(R.drawable.alert_dark_frame)
.setContentTitle("Starting Service")
.setContentText("Starting monitoring service")
.setContentIntent(pendingIntent)
val notification: Notification = notificationBuilder.build()
val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
)
channel.description = NOTIFICATION_CHANNEL_DESC
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
startForeground(
1,
notification
) //必须使用此方法显示通知,不能使用notificationManager.notify,否则还是会报上面的错误
}
}
override fun onBind(intent: Intent?): IBinder {
throw UnsupportedOperationException("Not yet implemented")
}
}
使用完毕释放资源,关闭服务!