面试重点一个不落:FileProvider你了解多少?透过FileProvider来看看四大组件之一ContentProvider!

面试重点一个不落:FileProvider你了解多少?透过FileProvider来看看四大组件之一ContentProvider!,第1张

概述前言大家应该都熟悉FileProvider吧,但是其诞生的原因,内部怎么实现的,又是怎么转化为文件的,大家有了解多少呢?今天就通过它重新看看ContentProvider这个四大组件之一。在Android7.0,Android提高了应用的隐私权,限制了在应用间共享文件。如果需要在应用间共享,需要授予要访问的URI临

前言

大家应该都熟悉fileProvIDer吧,但是其诞生的原因,内部怎么实现的,又是怎么转化为文件的,大家有了解多少呢?今天就通过它重新看看ContentProvIDer这个四大组件之一。

Android7.0,AndroID提高了应用的隐私权,限制了在应用间共享文件。如果需要在应用间共享,需要授予要访问的URI临时访问权限。

以下是官方说明:

对于面向 AndroID 7.0 的应用,AndroID 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 fileUrIExposedException 异常。要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 fileProvIDer 类。

为什么限制在应用间共享文件

打个比方,应用A有一个文件,绝对路径为file:///storage/emulated/0/Download/photo.jpg

现在应用A想通过其他应用来完成一些需求,比如拍照,就把他的这个文件路径发给了照相应用B,然后应用B照完相就把照片存储到了这个绝对路径。

看起来似乎没有什么问题,但是如果这个应用B是个“坏应用”呢?

泄漏了文件路径,也就是应用隐私。

如果这个应用A是“坏应用”呢?

自己可以不用申请存储权限,利用应用B就达到了存储文件的这一危险权限。

可以看到,这个之前落伍的方案,从自身到对方,都是不太好的选择。

所以Google就想了一个办法,把对文件的访问限制在应用内部。

如果要分享文件路径,不要分享file:// URI这种文件的绝对路径,而是分享content:// URI,这种相对路径,也就是这种格式:content://com.jimu.test.fileprovIDer/external/photo.jpg然后其他应用可以通过这个绝对路径来向文件所属应用 索要 文件数据,所以文件所属的应用本身必须拥有文件的访问权限。

也就是应用A分享相对路径给应用B,应用B拿着这个相对路径找到应用A,应用A读取文件内容返给应用B。

配置fileProvIDer

搞清楚了要做什么事,接下来就是怎么做。

涉及到应用间通信的问题,还记得IPC的几种方式吗?

文件AIDLContentProvIDerSocket等等。

从易用性,安全性,完整度等各个方面考虑,Google选择了ContentProvIDer为这次限制应用分享文件的 解决方案。于是,fileProvIDer诞生了。

具体做法就是:

<!-- 配置fileProvIDer--><provIDer    androID:name="androIDx.core.content.fileProvIDer"    androID:authoritIEs="${applicationID}.provIDer"    androID:exported="false"    androID:grantUriPermissions="true">    <Meta-data        androID:name="androID.support.file_PROVIDER_PATHS"        androID:resource="@xml/provIDer_paths"/></provIDer><?xml version="1.0" enCoding="utf-8"?><paths xmlns:androID="http://schemas.androID.com/apk/res/androID">    <external-path name="external" path="."/></paths>

 

//修改文件URL获取方式Uri photoURI = fileProvIDer.getUriForfile(context, context.getApplicationContext().getPackagename() + ".provIDer", createImagefile());

这样配置之后,就能生成content:// URI,并且也能通过这个URI来传输文件内容给外部应用。

fileProvIDer这些配置属性也就是ContentProvIDer的通用配置:

androID:name,是ContentProvIDer的类路径。androID:authoritIEs,是唯一标示,一般为包名+.provIDerandroID:exported,表示该组件是否能被其他应用使用。androID:grantUriPermissions,表示是否允许授权文件的临时访问权限。

其中要注意的是androID:exported正常应该是true,因为要给外部应用使用。

但是fileProvIDer这里设置为false,并且必须为false。

这主要为了保护应用隐私,如果设置为true,那么任何一个应用都可以来访问当前应用的fileProvIDer了,对于应用文件来说不是很可取,所以Android7.0以上会通过其他方式让外部应用安全的访问到这个文件,而不是普通的ContentProvIDer访问方式,后面会说到。

也正是因为这个属性为true,在Android7.0以下,AndroID默认是将它当成一个普通的ContentProvIDer,外部无法通过content:// URI来访问文件。所以一般要判断下系统版本再确定传入的Uri到底是file格式还是content格式。

fileProvIDer源码

接着看看fileProvIDer的主要源码:

public class fileProvIDer extends ContentProvIDer {    @OverrIDe    public boolean onCreate() {        return true;    }    @OverrIDe    public voID attachInfo(@NonNull Context context, @NonNull ProvIDerInfo info) {        super.attachInfo(context, info);        // Sanity check our security        if (info.exported) {            throw new SecurityException("ProvIDer must not be exported");        }        if (!info.grantUriPermissions) {            throw new SecurityException("ProvIDer must grant uri permissions");        }        mStrategy = getPathStrategy(context, info.authority);    }    public static Uri getUriForfile(@NonNull Context context, @NonNull String authority,            @NonNull file file) {        final PathStrategy strategy = getPathStrategy(context, authority);        return strategy.getUriForfile(file);    }    @OverrIDe    public Uri insert(@NonNull Uri uri, ContentValues values) {        throw new UnsupportedOperationException("No external inserts");    }    @OverrIDe    public int update(@NonNull Uri uri, ContentValues values, @Nullable String selection,            @Nullable String[] selectionArgs) {        throw new UnsupportedOperationException("No external updates");    }    @OverrIDe    public int delete(@NonNull Uri uri, @Nullable String selection,            @Nullable String[] selectionArgs) {        // ContentProvIDer has already checked granted permissions        final file file = mStrategy.getfileForUri(uri);        return file.delete() ? 1 : 0;    }    @OverrIDe    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,            @Nullable String[] selectionArgs,            @Nullable String sortOrder) {        // ContentProvIDer has already checked granted permissions        final file file = mStrategy.getfileForUri(uri);        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;    }    @OverrIDe    public String getType(@NonNull Uri uri) {  final file file = mStrategy.getfileForUri(uri);        final int lastDot = file.getname().lastIndexOf('.');        if (lastDot >= 0) {            final String extension = file.getname().substring(lastDot + 1);            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);            if (mime != null) {                return mime;            }        }        return "application/octet-stream";    }}

任何一个ContentProvIDer都需要继承ContentProvIDer类,然后实现这几个抽象方法:

onCreate,getType,query,insert,delete,update。

(其中每个方法中的Uri参数,就是我们之前通过getUriForfile方法生成的content URI

我们分三部分说说:

数据调用方面

其中,query,insert,delete,update四个方法就是数据的增删查改,也就是进程间通信的相关方法。

其他应用可以通过ContentProvIDer来调用这几个方法,来完成对本地应用数据的增删查改,从而完成进程间通信的功能。

具体方法就是调用getContentResolver()的相关方法,例如:

Cursor cursor = getContentResolver().query(uri, null, null, null, "userID"); 

再回去看看fileProvIDer

query,查询方法。在该方法中,返回了file的name和length。insert,插入方法。没有做任何事。delete,删除方法。删除Uri对应的file。update,更新方法。没有做任何事。MIME类型

再看getType方法,这个方法主要是返回 Url所代表数据的MIME类型。

一般是使用默认格式:

如果是单条记录返回以vnd.androID.cursor.item/ 为首的字符串

如果是多条记录返回vnd.androID.cursor.dir/ 为首的字符串

具体怎么用呢?可以通过Content URI对应的ContentProvIDer配置的getType来匹配Activity。

有点拗口,比如Activity和ContentProvIDer这么配置的:

<activity      androID:name=".SecondActivity">      <intent-filter>          <action androID:name=""/>          <category androID:name=""/>          <data androID:mimeType="type_test"/>     </intent-filter>  </activity>

 

    @OverrIDe    public String getType(@NonNull Uri uri) {        return "type_test";    }    intent.setData(mContentRUI);      startActivity(intent)

这样配置之后,startActivity就会检查Activity的mineType 和 Content URI 对应的ContentProvIDer的getType是否相同,相同情况下才能正常打开Activity。

初始化

最后再看看onCreate方法。

在APP启动流程中,自动执行所有ContentProvIDerattachInfo方法,并最后调用到onCreate方法。一般在这个方法中就做一些初始化工作,比如初始化ContentProvIDer所需要的数据库。

而在fileProvIDer中,调用了attachInfo方法作为了一个初始化工作的入口,其实和onCreate方法的作用一样,都是App启动的时候会调用的方法。

在这个方法中,也是限制了exported属性必须为false,grantUriPermissions属性必须为true。

        if (info.exported) {            throw new SecurityException("ProvIDer must not be exported");        }        if (!info.grantUriPermissions) {            throw new SecurityException("ProvIDer must grant uri permissions");        }

这个初始化方法和特性,也是被很多三方库所利用,可以进行静默无感知的初始化工作,而无需单独调用三方库初始化方法。比如Facebook SDK:

    <provIDer        androID:name="com.facebook.internal.FacebookInitProvIDer"        androID:authoritIEs="${applicationID}.FacebookInitProvIDer"        androID:exported="false" />

 

public final class FacebookInitProvIDer extends ContentProvIDer {    private static final String TAG = FacebookInitProvIDer.class.getSimplename();    @OverrIDe    @SuppressWarnings("deprecation")    public boolean onCreate() {        try {            FacebookSdk.sdkInitialize(getContext());        } catch (Exception ex) {            Log.i(TAG, "Failed to auto initialize the Facebook SDK", ex);        }        return false;    }    //...}

这样一写,就无需单独集成FacebookSDK的初始化方法了,实现静默初始化。

而Jetpack中的App Startup也是考虑到这些三方库的需求,对三方库的初始化进行了一个合并,从而优化了多次创建ContentProvIDer的耗时。

拿到Content URI 该怎么使用?

很多人都知道该怎么配置fileProvIDer让别人(比如照相APP)来获取我们的Content URI,但是你们知道别人拿到Content URI之后又是怎么获取具体的file的呢?

其实仔细找找就能发现,在fileProvIDer.java中有注释说明:

The clIEnt app that receives the content URI can open the file and access its contents by calling {@link androID.content.ContentResolver#openfileDescriptor(Uri, String) ContentResolver.openfileDescriptor}  to get a {@link ParcelfileDescriptor}

也就是openfileDescriptor方法,拿到ParcelfileDescriptor类型数据,其实就是一个文件描述符,然后就可以读取文件流了。

ParcelfileDescriptor parcelfileDescriptor = getContentResolver().openfileDescriptor(intent.getData(), "r");fileReader reader = new fileReader(parcelfileDescriptor.getfileDescriptor());BufferedReader bufferedReader = new BufferedReader(reader);
ContentProvIDer 实际应用

在平时的工作中,主要有以下以下几种情况和ContentProvIDer打交道比较多:

和系统的一些App通信,比如获取通讯录,调用拍照等。上述的fileProvIDer也是属于这种情况。与自己的APP有一些交互。比如自家多应用之间,可以通过这个进行一些数据交互。三方库的初始化工作。很多三方库会利用ContentProvIDer自动初始化这一特性,进行一个静默无感知的初始化工作。总结

ContentProvIDer作为四大组件之一,似乎并没有其他组件的存在感那么强。

但是他还是有自己的那一份职责,也就是在保证安全的情况下进行应用间通信,还可以扩展作为帮助初始化的组件。所以了解他,掌握它也是很重要的,没准以后哪个时候你就需要他了。

不要忽视任何一个知识点。

关于面试知识点的复习

之前为了面试我花半年时间整理了一份大厂的《AndroID开发2020年度面试真题合集》,深入学习源码底层,架构设计。而已也刷了很多大厂面试真题。也切身体会到了一分耕耘一分收获。

该面试宝典不仅收录了本人亲身面试遇到的问题,还收录了从一些朋友那里收集过来的问题。在以后的工作中本 人也会不断的更新和充实该面试宝典,当然也希望大家能够多多奉献比较优质的面试题。

总共分为6个部分:

Java 基础(★★)Java 高级(★★)AndroID 基础(★★★)AndroID 高级(★★★)AndroID 项目(★★★)项目面试常见问题(★★★)
一、Java 基础(★★)

面向对象思想
多态
异常处理
数据类型
Java 的 IO
集合
Java 多线程

Java 高级(★★)

Java 中的反射
Java 中的动态代理
Java 中的设计模式&回收机制
Java 的类加载器

AndroID 基础(★★★)

AndroID 基本常识
Activity
Service
broadCastReceiver
ContentProvIDer&数据库

AndroID 中的布局
ListVIEw
JNI & NDK
AndroID 中的网络访问
Intent
Fragment

AndroID 高级(★★★)

AndroID 性能优化
AndroID 屏幕适配
AIDL
自定义控件
AndroID 中的事件处理
AndroID 签名
AndroID 中的动画
网络协议
其他

项目面试常见问题(★★★)开发周期项目中遇到的难题项目中最大的收获项目是如何上线的项目是如何盈利的绘制项目架构图项目开发流程你在项目中的角色你负责项目中的哪些模块讲讲你负责模块的具体实现项目中都用到了哪些第三发框架有没有自己写过框架业余时间你是如何提高自己(学习)的有没有自己的技术 blog你的职业规划为什么离职为什么选择我们公司说说你们项目的亮点和不足你们的项目是如何保持风格一致的项目架构是如何搭建的屏幕适配是如何解决的都看过哪些源码项目版本是如何升级的用的什么版本控制工具你能独立开发吗App 跟服务器是如何交互的需求文档写过吗接口文档写过吗云服务器都用过哪些第三方平台都用过哪些简历+社招解答+经典HR面试解析

以上是整理总结的AndroID中高级面试遇到的真题解析,希望对大家有帮助;同时很多人经常也会遇到很多关于简历制作,职业困惑、HR经典面试问题回答等有关面试的问题。同样我也搜集整理了全套简历制作、金三银四社招困惑、HR面试等问题解析,有疑问,可以提供专业的解答。

对于AndroID开发的朋友来说应该是最全面最完整的面试资料,为了更好地整理每个模块,我参考了很多网上的优质博文和项目,力求不漏掉每一个知识点。很多朋友靠着这些内容进行复习,拿到了BATJ等大厂的offer,这个资料也已经帮助了很多的安卓开发者,希望也能帮助到你。

以上免费分享?

是的,免费分享,但是记得关注一下我哈,需要完整版的朋友,点这里可以看到全部内容。

总结

以上是内存溢出为你收集整理的面试重点一个不落:FileProvider你了解多少?透过FileProvider来看看四大组件之一ContentProvider!全部内容,希望文章能够帮你解决面试重点一个不落:FileProvider你了解多少?透过FileProvider来看看四大组件之一ContentProvider!所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1033254.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-24
下一篇 2022-05-24

发表评论

登录后才能评论

评论列表(0条)

保存