Android中FileProvider的各种应用场景_安卓fileprovider

本文详细介绍了Android中FileProvider的使用,包括如何打开文件、获取文件扩展名,并提供了ContentProvider的自定义示例。此外,文章还强调了阅读源码和理解系统知识点的重要性,提供了包含BATJ等大厂面试真题的Android开发相关资料,旨在帮助开发者提升技术能力并为面试做准备。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

val bd: BitmapDrawable = drawable as BitmapDrawable
val bitmap = bd.bitmap
FilesUtils.getInstance().saveBitmap(bitmap, "naixiao-1122.jpg")

val filePath = FilesUtils.getInstance().sdpath + "naixiao-1122.jpg"

YYLogUtils.w("文件原始路径:$filePath")

val uri = FileProvider.getUriForFile(commContext(), "com.guadou.kt_demo.fileprovider", File(filePath))

YYLogUtils.w("打印Uri:$uri")

//到系统中找打开对应的文件
openFile(filePath, uri)

}

private fun openFile(path: String, uri: Uri) {
//取得文件扩展名
val extension: String = path.substring(path.lastIndexOf(“.”) + 1)

//通过扩展名找到mimeType
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
YYLogUtils.w("mimeType: $mimeType")

try {
    //构造Intent,启动意图,交由系统处理
    startActivity(Intent().apply {
        //临时赋予读写权限
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
        //表示用其它应用打开
        action = Intent.ACTION_VIEW
        //给Intent 赋值
        setDataAndType(uri, mimeType)
    })
} catch (e: Exception) {
    e.printStackTrace()
    YYLogUtils.e("不能打开这种类型的文件")
}

}


很简单的一个例子,我们把drawable中的一个图片,保存到我们私有沙盒目录中,目录为


文件原始路径:/storage/emulated/0/Android/data/com.guadou.kt\_demo/cache/pos/naixiao-1122.jpg  
 我们通过 FileProvider 拿到 content://开头的uri路径。然后通过Intent匹配找到对于的第三方App来接收。  
 那么打印就如下:


打印Uri:content://com.guadou.kt\_demo.fileprovider/external\_app\_cache/pos/naixiao-1122.jpg


content 是 scheme。  
 com.guadou.kt\_demo.fileprovider 即为我们在清单文件中定义的 authorities,即是我们的FileProvider的唯一表示,在接收的时候作为host。  
 这样封装之后,当其他的App收到这个Uri就无法从这些信息得知我们的文件的真实路径,相对有安全保障。


其他场景中,比如沙盒中的Apk文件想要安装,也是一样的流程,我们需要赋予读写权限,然后设置DataAndType即可。代码的注释很详细,大家可以参考参考。


此时我们都是发送了一个Intent,让系统自己去匹配符合条件的Activity。那有没有可能我们自己做一个App去匹配它。


这… 好像还真行。


### 二、能不能自定义接收文件?


其实我们仿造系统的App的做法,我们在自定义的Activity中加入指定Filter即可,比如这里我需要接收图片,那么我定义如下的 intent-filter:





        <category android:name="android.intent.category.LAUNCHER" />

    </intent-filter>
</activity>

<activity
    android:name=".ReceiveImageActivity"
    android:exported="true">

    <intent-filter>

        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="content" />
        <data android:scheme="file" />
        <data android:scheme="http" />
        <data android:mimeType="image/*" />

    </intent-filter>

</activity>

都是一些固定的写法,我们在Activity上指明,它可以接收图片数据,此时我们再回到第一个App,发送图片。  
 之前还是图片查看器,现在可以选择我们自己的App来接收图片数据了,但是我们如何接收数据呢?


其实都是一些固定的代码,主要是拿到input流,然后操作流的处理。



override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_receive_image)

if (intent != null && intent.action == Intent.ACTION_VIEW) {

    val uri = intent.data
    YYLogUtils.w("uri: $uri")

    if (uri != null && uri.scheme != null && uri.scheme == "content") {

        val fis = contentResolver.openInputStream(uri)

        if (fis != null) {

            val bitmap = BitmapFactory.decodeStream(fis)
            //展示
            if (bitmap != null) {
                val ivReveiverShow = findViewById<ImageView>(R.id.iv_reveiver_show)
                ivReveiverShow.setImageBitmap(bitmap)
            }

        }
    }
}

}


最简单的做法,直接根据uri打开输入流,然后我们可以通过 BitmapFactory就可以拿到 Bitmap了,就能展示图片到ImageView上面。  
 甚至我们拿到了 input 流,我们还能对流进行copy 操作,把你的图片保存到我自己的沙盒目录中,例如:



override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_receive_image)

if (intent != null && intent.action == Intent.ACTION_VIEW) {

    val uri = intent.data
    YYLogUtils.w("uri: $uri")

    if (uri != null && uri.scheme != null && uri.scheme == "content") {

        val fis = contentResolver.openInputStream(uri)

        if (fis != null) {

            val inBuffer = fis.source().buffer()

            val outFile = File(getExternalFilesDir("xiaoxiao"), "naixiao5566.jpg")
            outFile.sink().buffer().use {
                it.writeAll(inBuffer)
                inBuffer.close()
            }

            YYLogUtils.w("存放的路径:${outFile.absolutePath}")

            //展示
            val ivReveiverShow = findViewById<ImageView>(R.id.iv_reveiver_show)
            ivReveiverShow.extLoad(outFile.absolutePath)

        }
    }
}

}


### 三、能不能主动查询对方的沙盒?


转头一想,好像还真行,有操作空间啊… 既然 FileProvider 是继承自 ContentProvider 。那凭什么我们的App都能获取到别人App的数据库了,不能获取别人的沙盒文件呢?那数据库文件不也存在沙盒中么?


例如联系人App,我们开发的第三方App可以通过 ContentProvider 获取到联系人App中的联系人数据,那么只要第三方的App定义好对应的 ContentProvider 我不就能获取到它沙盒的文件了吗?


说到就做,我们先把FileProvider设置为可访问




</meta-data>

是的,android:exported="true"设置成功之后我们直接通过 contentResolver 去查询不就好了吗?先运行一下试试!运行就崩了?  
 什么鬼哦,看看FileProvider的代码,原来不允许开放。  
 这和我们的需求不符合啊,我就要主动访问,既然你不行,那我不用你行了吧!我继承 ContentProvider 行了吧!我自己实现文件获取、Cursor封装行了吧!


不皮了,其实我们直接通过继承 ContentProvider 并且允许 exported ,然后我们通过自己实现的query方法,返回指定的Cursor信息,就可以实现!


部分代码如下:



public class MyFileProvider extends ContentProvider {

@Override
public void attachInfo(Context context, ProviderInfo info) {
    super.attachInfo(context, info);

    mStrategy = getPathStrategy(context, info.authority);
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    YYLogUtils.w("走到query方法");

    final File file = mStrategy.getFileForUri(uri);
    YYLogUtils.w("file:" + file);

    if (!file.exists()) {
        return null;
    }

    boolean directory = file.isDirectory();
    if (directory) {
        YYLogUtils.w("说明是文件夹啊!");

        File[] files = file.listFiles();
        for (File childFile : files) {
            if (childFile.isFile()) {
                String name = childFile.getName();
                String path = childFile.getPath();
                long size = childFile.length();
                Uri uriForFile = mStrategy.getUriForFile(childFile);
                YYLogUtils.w("name:" + name + " path:" + path + " size: " + size +" uriForFile:"+uriForFile);
            }
        }
        //自己遍历封装Cursor实现    
        return null;

    } else {
        YYLogUtils.w("说明是文件啊!");

        if (projection == null) {
            projection = COLUMNS;
        }

        String[] cols = new String[projection.length];
        Object[] values = new Object[projection.length];
        int i = 0;
        for (String col : projection) {
            if (OpenableColumns.DISPLAY_NAME.equals(col)) {
                cols[i] = OpenableColumns.DISPLAY_NAME;
                values[i++] = file.getName();
            } else if (OpenableColumns.SIZE.equals(col)) {
                cols[i] = OpenableColumns.SIZE;
                values[i++] = file.length();
            }
        }

        cols = copyOf(cols, i);
        values = copyOf(values, i);

        final MatrixCursor cursor = new MatrixCursor(cols, 1);
        cursor.addRow(values);

        return cursor;
    }

}

}


我简单的做了文件和文件夹的处理,并不完整,如果是文件我们可以直接返回一个简单的cursor,如果是文件夹需要大家自己拼接子文件的cursor并返回。


接下来我们看看其他App如何主动这些文件,在另一个App中我们先加上权限:




 然后我们直接使用 contentResolver.query 
private fun queryFiles() {  
 val uri = Uri.parse(“content://com.guadou.kt\_demo.fileprovider/external\_app\_cache/pos/naixiao-1122.jpg”)



val cursor = contentResolver.query(uri, null, null, null, null)

if (cursor != null) {

    while (cursor.moveToNext()) {

        val fileName = cursor.getString(cursor.getColumnIndex("_display_name"));
        val size = cursor.getLong(cursor.getColumnIndex("_size"));

        YYLogUtils.w("name: $fileName  size: $size")
        Toast.makeText(this, "name: $fileName  size: $size", Toast.LENGTH_SHORT).show()
    }

    cursor.close()

} else {
    YYLogUtils.w("cursor-result: 为空啊")
    Toast.makeText(this, "cursor-result: 为空啊", Toast.LENGTH_SHORT).show()
}

}


如果我们知道它的指定文件Uri,我们可以通过query查询到文件的一些基本信息。具体是哪些信息,需要对方提供和定义。


如果想操作对方的文件,由于我们已经拿到了对方的Uri,我们可以直接通过inputStream来操作,例如:



val fis = contentResolver.openInputStream(uri)
if (fis != null) {

    val inBuffer = fis.source().buffer()

    val outFile = File(getExternalFilesDir(null), "abc")

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BATJ 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

链图片转存中…(img-GpXZWsif-1714354415432)]

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

[外链图片转存中…(img-VhTx1df8-1714354415432)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值