首页 > 其他分享 >Android 短视频和图片无读写权限TargetApi>=29解决方案

Android 短视频和图片无读写权限TargetApi>=29解决方案

时间:2023-04-11 10:32:28浏览次数:60  
标签:29 uri intent Uri context TargetApi Android null android


一、背景

        目前很多公司在适配API29,也就是targetSdkVersion=29的权限适配。不仅是权限的适配,还有政策的要求。

目前就有很多大公司已收到工信部要求,不给读写权限:


android.permission.WRITE_EXTERNAL_STORAGE和


android.permission.READ_EXTERNAL_STORAGE


        这种情况下,你再想通过ContentResolver去查询系统表中是无法满足给所有的Image和Video查出来。所以,我们只能通过系统方法来处理,而且在外存上的东西,我们是无法读取的,杀手锏就是把外存的东西搬到内存里,在data下面开辟属于我们自己包下的存储路径。

二、权限适配

        在没有读写权限下的方案,选中的图片存储到系统里面,为什么要选择copy这种方式?因为在没有读写权限时,文件在外存,也就是SD卡的时候,是无法进行一系列的操作,如果存储到内存中data目录下,是可以进行正常的读写操作,也不需要权限。

1、图片

1.1选择图片:

单选:

Intent intent = new Intent("android.intent.action.OPEN_DOCUMENT");
intent.setType("image/*");
intent.putExtra("android.intent.extra.MIME_TYPES", new String[]{"image/*"});

多选:

Intent intent = new Intent("android.intent.action.OPEN_DOCUMENT");
intent.setType("image/*");
intent.putExtra("android.intent.extra.MIME_TYPES", new String[]{"image/*"});
intent.putExtra("android.intent.extra.ALLOW_MULTIPLE", true);

1.2 选择后保存到data目录下:

单选和多选的返回处理不同:

多选:

ClipData clipData = data.getClipData();
            if (clipData != null) {
                Uri child = clipData.getItemAt(0).getUri();
            }


多选的数据存储子啊ClipData中,从里面可以遍历出来 单选:


Uri uri = data.getData();

保存到data目录下: 通过Uri获取到bitmap: bitmap = Media.getBitmap(context, uri);

public String saveFileIntoDataCache(Context context, String fileName, Bitmap bitmap) {
        FileOutputStream fos = null;
        String path = null;

        try {
            File folder = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            if (folder.exists() || folder.mkdir()) {
                File file = new File(folder, fileName);
                fos = new FileOutputStream(file);
                bitmap.compress(CompressFormat.JPEG, 100, fos);
                fos.flush();
                path = file.getAbsolutePath();
            }
        } catch (Exception var16) {
           
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException var15) {
              
            }

        }

        return path;
    }

2、短视频

选择:已mp4为例,如果是*,将会把所有的video都会显示出来

视频也是一样,区分单选和多选

Intent intent = new Intent("android.intent.action.OPEN_DOCUMENT", Media.EXTERNAL_CONTENT_URI);
        intent.setType("video/mp4");

多选新增一个:

intent.putExtra("android.intent.extra.ALLOW_MULTIPLE", true);

返回的结果也是一样,可以参考上面图片的处理,多选在ClipData,单选就是一个data

,其他也是一样,根据uri,将文件copy进data下方

3、uri的协议scheme

        uri的有一个关键的type叫协议,scheme,不同的版本协议也不一样。有人在文件选择回来发现协议是file://,并非content://。这个时候,再想通过ContentResolver去操作是返回null的。

遇到这种情况,我们需要转换协议

//路径转Uri
        fun getContentPathToUri(packageName:String,path: String, context: Context): Uri {
            val AUTHORITY=" ${packageName}.fileprovider"
            var inputUri: Uri? = null
            val filePath = File(path)
            if (Build.VERSION.SDK_INT >= 24) {
                // 通过FileProvider创建一个content类型的Uri
                inputUri = FileProvider.getUriForFile(context, AUTHORITY, filePath)

            } else {
                val inputUri = Uri.fromFile(filePath)
            }
            return inputUri!!
        }

三、文件信息获取

        使用文件的时候,我们常常需要获取文件的大小和名称之类。因为这些文件都存储在表中,所以获取是通过查询接口。但是,有些公司的应用targetapi已提到了29或者更高。在查询结果后,获取和以往有所不同。

val course = contentResolver.query(uri, null, null, null, null)
        if (course != null && course.moveToFirst()) {
            course.getString(course.getColumnIndex(OpenableColumns.DISPLAY_NAME))
            course.getString(course.getColumnIndex(OpenableColumns.SIZE))
        }

以往都是通过media下面获取,由于29后,已不在提供详细的。

只能获取名称和大小。这个时候有人会发现,如果我需要短视频的时长怎么办?

视频获取时长:MediaMetadataRetriever

fun getVideoTimeLength(uri: Uri, context: Context): Int {
    var metadataRetriever = MediaMetadataRetriever()
    metadataRetriever.setDataSource(context, uri)
    val longtime =
        metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
    if (TextUtils.isEmpty(longtime)) {
        return 0
    }
    return longtime!!.toInt()

}

如果需要读取uri中的内容,如下

ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri,"rw");

FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();

四、总结

        在没有读写权限以及targetapi调整到29时,又因为内存中读取不需要权限,所以策略调整是将外存的内容复制到内存中,再通过内存的uri进行操作。

这里主要有:

  1.         内容从外存转到内存;
  2.         uri scheme的协议转换;
  3.         contentResolver在target 29后的查询,只支持_name和_size
  4.         短视频的时长获取

标签:29,uri,intent,Uri,context,TargetApi,Android,null,android
From: https://blog.51cto.com/u_16065093/6182723

相关文章

  • Android 代码混淆 包名被混淆 主工程二次混淆 一站解决你的混淆
    代码混淆(Obfuscatedcode)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。代码混淆可以用于程序源代码,也可以用于程序编译而成的中间代码。执行代码混淆的程序被称作代码混淆器。已经存在许多种功能各异的代码混淆器。将代码中的各种元素,如......
  • Android DataStore Proto框架存储接入AndroidStudio教程详解与使用
    一、介绍        通过前面的文字,我们已掌握了DataStore的存储,但是留下一个尾巴,那就是Proto的接入。Proto是什么?Protobuf,类似于json和xml,是一种序列化结构数据机制,可以用于数据通讯等场景,相对于xml而言更小,相对于json而言解析更快,支持多语言官网:LanguageGuide(proto3)|......
  • Android Jetpack组件之WorkManager高级概念介绍与使用(三)
    一、介绍        通过前面两篇,我们基本掌握了组件的workmanager的接入,以及api的使用等。但是一个框架如果运用在复杂的项目中,肯定需要有其他额外的支持,介绍来我们将会介绍高级概念,以及对前面的知识点进行回顾与拓展。高级概念一、配置和初始化        默认情况下,当......
  • Android Imageview 图片置灰,图片特殊节日去真彩色
    ImageViewImageView是Android中的一个图片显示控件,用来加载网络或者本地图片资源。好看的图片可以让应用更被用户接收,如果图片作为应用的装饰,已成为主流,但不是所有的图片显示都符合要求,比如一些特殊时间,特别靓丽的色彩不符合当下假日要求,这个时候如果能让图片变成灰色,这样用助于达......
  • Android 学习任务缩略图
    运行环境1、下面案例在系统签名下可以运行版本:Android112、注意:我尝试在没有系统签名下打开//代码中FilexmlFile=newFile("/data/system_ce/0/recent_tasks/33_task.xml");会报以下错误2023-04-1016:23:38.2784411-4438/com.example.myapplicationW/Sys......
  • Android开发中Dialog填充满父容器
    Android开发中Dialog填充满父容器在Android原生开发中,通常会使用自定义的Dailog来设计二级面板,其自带一个黑色透明的遮蔽效果。但是想要将Dialog填充满父容器,是需要一些尝试的。环境介绍自定义Dialog类,加载自定义布局layout并进行数据绑定,同时创建接口进行信息传递。其中布局......
  • Android开发_记事本(7)
    搜索实现搜索图标的添加main_menu<itemandroid:id="@+id/action_search"android:icon="?attr/menu_search"app:showAsAction="always"app:actionViewClass="androidx.appcompat.widget.SearchView"android:title......
  • Android开发_记事本(6)
    删除键的功能实现删除当前笔记文件EditActivity中添加@OverridepublicbooleanonOptionsItemSelected(MenuItemitem){//监听状态栏上内容被点击switch(item.getItemId()){caseR.id.delete:newAlertDialog.Builder(EditActivity.this)/......
  • Android开发_记事本(3)
    适配器NoteAdapter相当于数据和ListView之间的中介 packagecom.example.note; ​ importandroid.content.Context; importandroid.content.SharedPreferences; importandroid.preference.PreferenceManager; importandroid.text.TextUtils; importandroid.view.Vie......
  • Android开发_记事本(4)
    BaseActivity用作大多数页面的父类 packagecom.example.note; ​ //用来当作大多是activity的superclass ​ ​ importstaticandroid.provider.ContactsContract.Intents.Insert.ACTION; ​ importandroid.app.StatusBarManager; importandroid.content.Broadcas......