Android10以上MediaProjection截屏

起因

在系统升级到Android10以上之后,之前的截屏方式不能用了,而且必须将MediaProjection放在forground service里面跑才行。网上搜了一圈,都是语焉不详或者没有完整的一个代码应用。只能自己写一个,记录下

代码实现

新建一个ScreenRecorder.java


public class ScreenRecorder extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private MediaProjection mMediaProjection;
    private MediaProjectionManager mMediaProjectionManager;
    private int SERVICE_ID = 123;
    private String NOTIFICATION_CHANNEL_ID = "Screen Capture channel";
    public static final String ACTION_START = "RecordService:Start";
    public static final String ACTION_SNAPSHOT = "RecordService:Snapshot";
    public static final String ACTION_STOP = "RecordService:Stop";
    public static final String EXTRA_RESULT_DATA = "RecordService:Extra:ResultData";

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        startForeground(SERVICE_ID, new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).build());
        mMediaProjectionManager = (MediaProjectionManager) getApplicationContext().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    }

    @Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
        if (intent != null) {
            if (intent.getAction() != null) {
                switch (intent.getAction()) {
                    case ACTION_START:
                        Log.d(TAG, "onStartCommand: start recorder");
                        mMediaProjection = mMediaProjectionManager.getMediaProjection(Activity.RESULT_OK,
                                Objects.requireNonNull(intent.getParcelableExtra(EXTRA_RESULT_DATA)));
                        startCapture();
                        return START_STICKY;
                    case ACTION_SNAPSHOT:
//                        stopSelf();
                        snapshot();
                        break;
                    case ACTION_STOP:
                        Log.d(TAG, "onStartCommand: close imagereader");
                        if (mImageReader != null){
                            try{
                                mImageReader.close();
                            }catch (Exception exception){
                                Log.e(TAG, "onStartCommand: "+ exception.getMessage() );
                            }
                        }
                        stopSelf();
                        return START_NOT_STICKY;
                }

            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    private void createNotificationChannel() {
        NotificationChannel notificationChannel = new NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                "ScreenRecorder",
                NotificationManager.IMPORTANCE_DEFAULT
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(notificationChannel);
    }

    private ImageReader mImageReader;
    private void startCapture() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        int screenWidth = displayMetrics.widthPixels;
        int screenHeight = displayMetrics.heightPixels;
        mImageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 2);
        mMediaProjection.createVirtualDisplay("mediaprojection", screenWidth, screenHeight,
                displayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader.getSurface(), null, null);

    }

    private void snapshot(){
        if (mImageReader != null){
            Image image = mImageReader.acquireLatestImage();
            int width = image.getWidth();
            int height = image.getHeight();
            final Image.Plane[] planes = image.getPlanes();
            final ByteBuffer buffer = planes[0].getBuffer();
            int pixelStride = planes[0].getPixelStride();
            int rowStride = planes[0].getRowStride();
            int rowPadding = rowStride - pixelStride * width;
            Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height,
                    Bitmap.Config.ARGB_8888);
            bitmap.copyPixelsFromBuffer(buffer);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
            //保存图片到本地
            CommonLib.saveMyBitmap("snapshot", bitmap);
            image.close();
        }

    }
}

AndroidManifest.xml中注册

<!--   申请权限 />-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<service android:name=".ScreenRecorder"
            android:exported="true"
            android:foregroundServiceType="mediaProjection"/>

Activity中调用

 public void onCreate(Bundle bundle) {
 	super.onCreate(bundle);
 	requestCapturePermission();
 }
/**
* 申请权限
*/
 public void requestCapturePermission() {
     MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
     Intent intent = new Intent(mediaProjectionManager.createScreenCaptureIntent());
     startActivityForResult(intent, REQUESTRESULT);
 }
// 回调里面启动服务
@Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (resultCode == RESULT_OK) {
          Intent intent = new Intent(this, ScreenRecorder.class);
          intent.setAction(ScreenRecorder.ACTION_START);
          intent.putExtra(ScreenRecorder.EXTRA_RESULT_DATA, data);
          startForegroundService(intent);
          EnableTakePhotos = true;
	}
}

snapshot方法

private void startSnapshot(){
 	Intent intent = new Intent(this, ScreenRecorder.class);
    intent.setAction(ScreenRecorder.ACTION_SNAPSHOT);
    startService(intent);
}

可以设置一个按钮,然后在点击事件里面调用startSnapshot()方法即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559042.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java框架】Spring框架(二)——Spring基本核心(AOP)

目录 面向切面编程AOPAOP的目标&#xff1a;让我们可以“专心做事”专心做事专心做事解决方案1.0专心做事解决方案2.0蓝图 AOP应用场景AOP原理AOP相关术语术语理解 AOP案例实现前置/后置/异常/最终增强的配置实现1.依赖2.业务类3.日志类4.配置切入点表达式匹配规则举例 环绕增强…

Spring AOP (二)

本篇将介绍Spring AOP的相关原理。 一、代理模式 Spring 在实现AOP时使用了代理模式这种设计模式&#xff0c;什么是代理模式呢我们来了解一下。 代理模式可以理解为当我们需要调用某个类的方法时&#xff0c;在我们与这个目标类之间增加一个代理类&#xff0c;我们要使用目标…

vue2响应式 VS vue3响应式

Vue2响应式 存在问题&#xff1a; 新增属性&#xff0c;删除属性&#xff0c;界面不会更新。 直接通过下标修改数组界面不会自动更新。 Vue2使用object.defineProperty来劫持数据是否发生改变&#xff0c;如下&#xff1a; 能监测到获取和修改属性&#xff1a; 新增的属性…

【C++打怪之路】-- C++开篇

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;C打怪之路&#xff0c;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &…

【C语言】深入解析选择排序算法

一、算法原理二、算法性能分析三、C语言实现示例四、总结 一、算法原理 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的工作原理是不断地选择剩余元素中的最小&#xff08;或最大&#xff09;元素&#xff0c;放到已排序的序列的末尾&#xff…

securecrt 批量登录服务器介绍

一、前言 在有一些IT环境中&#xff0c;可能存在各种情况的服务器&#xff0c;因为各种原因不能统一部署类似ansible、saltstack等批量操控软件&#xff0c;当遇到需要对这些服务器进行某项信息的排查或调整配置时&#xff0c;你是否还是通过securecrt一台一台登录后进行操作&a…

endnote21从安装到使用!文献引用!Mac版

视频学习和资源获取 新建库 选择上方导航栏处的File下的New 软件 软件界面可以分成四个部分 2是个人图书馆 3是对某一分类中文献的展示 最右侧是对具体一篇文献的摘要、编辑以及PDF 有回形针标志意味着这篇有全文&#xff0c;也就是有pdf 如果没有回形针代表它只有引文信…

社交媒体数据恢复:BF Messager

BF Messenger 数据恢复方法 一、前言 BF Messenger&#xff08;BF加密聊天软件&#xff09;是一款基于布尔式循环移位加密算法的聊天应用程序。它使用对称密钥加密技术&#xff0c;用户可以在安全的环境下进行私密聊天。除此之外&#xff0c;该应用还具有防截屏、应用锁屏、密…

LeetCode in Python 55. Jump Game (跳跃游戏)

跳跃游戏的游戏规则比较简单&#xff0c;若单纯枚举所有的跳法以判断是否能到达最后一个下标需要的时间复杂度为O()&#xff0c;为此&#xff0c;本文采用贪心策略&#xff0c;从最后一个下标开始逆着向前走&#xff0c;若能跳到第一个元素则表明可以完成跳跃游戏&#xff0c;反…

本地主机搭建服务器后如何让外网访问?快解析内网端口映射

本地主机搭建应用、部署服务器后&#xff0c;在局域网内是可以直接通过计算机内网IP网络地址进行连接访问的&#xff0c;但在外网电脑和设备如何访问呢&#xff1f;由于内网环境下&#xff0c;无法提供公网IP使用&#xff0c;外网访问内网就需要一个内外网转换的介质。这里介绍…

stm32开发之netxduo组件之mqtt客户端的使用记录

前言 1使用mqtt协议的简单示例记录 代码 MQTT服务端(C# 编写,使用MQTTnet提供的示例代码) 主程序 namespace ConsoleApp1;public class Program {public static async Task Main(string[] args){await Run_Server_With_Logging();}}public static async Task Run_Server_Wi…

CERLAB无人机自主框架: 1-环境搭建

前言&#xff1a;更多更新文章详见我的个人博客主页【MGodmonkeyの世界】 描述&#xff1a;欢迎来到CERLAB无人机自主框架&#xff0c;这是一个用于自主无人飞行器 (UAV) 的多功能模块化框架。该框架包括不同的组件 (模拟器&#xff0c;感知&#xff0c;映射&#xff0c;规划和…

后台管理系统加水印(react)

效果 代码图片 代码 window.waterMark function (config) {var defaultConfig {content: 我是水印,fontSize: 16px,opacity: 0.3,rotate: -15,color: #ADADAD,modalId: J_waterMarkModalByXHMAndDHL,};config Object.assign({}, defaultConfig, config);var existMarkModal…

亚信安全入选中国数据安全市场图谱

近日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布了《IDC Market Glance&#xff1a;中国数据安全市场图谱&#xff0c;2024》报告&#xff08;以下简称“报告”&#xff09;&#xff0c;报告展示了中国数据安全市场的构成和格局&#xff0c;遴选出不同细分市场领域的主…

rabbitmq 使用SAC队列实现顺序消息

rabbitmq 使用SAC队列实现顺序消息 前提 SAC: single active consumer, 是指如果有多个实例&#xff0c;只允许其中一个实例消费&#xff0c;其他实例为空闲 目的 实现消息顺序消费&#xff0c;操作&#xff1a; 创建4个SAC队列,消息的路由key 取队列个数模&#xff0c;这…

java调用讯飞星火认知模型

前往讯飞开发平台选择产品&#xff0c;获取appId、apiKey、APISecret&#xff0c;这里我选择的是v3.0模型。 java后端实现 本项目以及实现了基本的会话功能&#xff0c;小伙伴可以自己扩充其他的例如绘画功能。 注意&#xff1a;星火模型的api使用的是websocket协议&#xf…

C++11(下篇)

文章目录 C111. 模版的可变参数1.1 模版参数包的使用 2. lambda表达式2.1 Lambda表达式语法捕获列表说明 2.2 lambda的底层 3. 包装器3.1 function包装器3.2 bind 4. 线程库4.1 thread类4.2 mutex类4.3 atomic类4.4 condition_variable类 C11 1. 模版的可变参数 C11支持模版的…

数据结构习题-- 相交链表

数据结构习题-- 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 如上图&#xff0c;返回c1结点 注意&#xff1a;这两个链表非环形 方法&#xff1a;集合 分析 由…

关于ERA5气压和温度垂直补偿公式的对比情况

1. 气压和温度垂直补偿对比 「谨代表给个人观点&#xff0c;杠精请自测&#xff0c;对对对&#xff0c;好好好&#xff0c;你说啥都对」。 使用2020-2022陆态网GNSS与探空站并址的48个站点实验&#xff0c;以探空站为真值&#xff0c;验证ERA5精度。怎么确定并址请看前面文章…

Django中的实时通信:WebSockets与异步视图的结合

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代Web应用程序中&#xff0c;实时通信已经成为了必不可少的功能之一。无论是在线聊天、…