转载注明出处:https://blog.csdn.net/skysukai
1、背景
项目中有这样一个需求:Android8.0使用AccessibilityService来进行浏览器拦截。AccessibilityService是用于残障人士的一个系统API,可以用来监听一些事件,方便残障人士使用智能手机。当然,用AccessibilityService来拦截浏览器是一种剑走偏锋的做法,姑且不论。(在使用AccessibilityService的过程中,需要配置AccessibilityServiceInfo来指定需要拦截的应用包名,很多讲AccessibilityServiceInfo的文章都忽略了;且Android8.0以后的系统对无障碍/AccessibilityService的管控越发严厉,每次拦截之前需先确认无障碍开关是否打开)。
当浏览器得到了输入网址且在拦截名单之内,跳转到拦截界面。这个拦截界面可以是一个服务器上的网址;最简单的方法是写一个HTML页面放到应用内,拦截到的时候直接加载这个界面。如何将应用内的文件共享给其他应用呢?
2、使用scheme
这种做法等价于从浏览器打开APP需要在浏览器加载一个界面,以跳转到APP,与在当前界面显示拦截页的需求不符。
3、使用Contentprovider
那如何在应用间共享数据呢?很容易就想到了contentprovider,用于对外暴露数据。这篇文章提供了一种思路,通过调用openFile()
这个方法,就可以拿到共享的应用内HTML页面。 不过,同样存在一个问题,openFile()的返回数据是ParcelFileDescriptor
即是二进制的文件,而不是浏览器可以直接加载的URI地址。浏览器拿到这个二进制文件之后,还需做相应的解析,把二进制文件还原为HTML页面。使用contentprovider这种方法看来也不行了。
4、使用Fileprovider
前面两种办法都行不通了,那文件该如何共享呢?
4.1 通过SDcard共享
将SDcard作为中转,将文件写到SDcard,再把路径通过Intent传给浏览器,即可达到目的。伪代码如下:
……
private final static String BLOCK_PAGE_FILENAME = "block.html";
public static final String getBlockPage(Context c, String sUrl, String codePath) {
if (c == null) {
return "";
}
String blockpage = "BlockPage.html";
return String.format(getHtmlFormat(c, blockpage), sUrl,
c.getString(R.string.exit_site));
}
public String createBlockPage(String blockUrl, WtpUrlEntry wtpEntry, Context c) {
……
String htmlData = getBlockPage(c, blockUrl, c.getPackageCodePath());;
try {
FileOutputStream outs = new FileOutputStream(
new File(getPathOnSDCard() + File.separator
+ BLOCK_PAGE_FILENAME));
outs.write(htmlData.getBytes());
outs.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
……
}
private String getPathOnSDCard() {
File path = new File(Environment.getExternalStorageDirectory());
if (!path.exists()) {
path.mkdirs();
}
return path.getAbsolutePath();
}
private void jumpToBlockPage() {
……
blockPageUrl = createBlockPage(blockedUrl, wtpEntry, c);
Intent intent = new Intent();
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
//android8.0需通过FileProvider共享文件路径,8.0以下直接共享
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Uri contentUri = FileProvider.getUriForFile(c,c.getPackageName() + ".provider", new File(blockPageUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(contentUri,"text/html");
} else {
intent.setData(Uri.parse(blockPageUrl));
}
try {
c.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
……
}
对应的FileProvider配置:
AndroidManifest.xml
……
<provider
android:authorities="{your package name}.provider"
android:name="android.support.v4.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
……
file_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path path="." name="external_storage_root"/>
</paths>
这样,就把SDcard根目录共享给了其他其他应用,注意需赋予临时共享URI权限,即:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
4.2直接共享应用内文件
通过先将文件写到SDcard在将路径传递给浏览器可以达到目的,不过这样做存在一个问题,即用户会在SDcard里看到写进去的文件,对用户来说不是特别友好。能不能选择其他路径作为文件中转的地方呢?FileProvider提供了以下共享文件的路径:
<files-path name="name" path="path" />:目录的位置在Context.getFilesDir()的目录,实际上就是/data/data/你的包名/files这个位置。
<cache-path name="name" path="path" />:目录的位置在 getCacheDir(),实际上就是/data/data/你的包名/cache目录。
<external-path name="name" path="path" />:目录的位置在Environment.getExternalStorageDirectory(),实际上就是sdcard的根目录了,一般是/sdcard/,当然实际的路径可能是/storage/emulated/0/这样的。
<external-files-path name="name" path="path" />:目录的位置在Context#getExternalFilesDir(String) Context.getExternalFilesDir(null),实际上就是/sdcard/Android/data/你的包名/files/目录。
<external-cache-path name="name" path="path" />:目录的位置在Context.getExternalCacheDir(),实际上就是/sdcard/Android/data/你的包名/cache/目录。
可以看到,files-path
提供了应用内的files文件夹作为共享路径,上面的代码只需要把SDcard路径改为应用内的files文件夹即可,这样用户也看不到写到这个路径下的文件,比较友好。
file_paths.xml文件改为
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path path="." name="external_storage_root"/>
<files-path path="." name="block_page_files" />
</paths>
同时,代码里getPathOnSDCard()
改为context.getFilesDir()
。详细代码此处省略。
相关参考:https://www.jianshu.com/p/5edbd0d1af54
相关参考:https://www.cnblogs.com/simov/p/3761243.html
相关参考:https://blog.csdn.net/wen0006/article/details/6224979
相关参考:http://www.voidcn.com/article/p-haggsbel-bsn.html
相关参考:https://www.kaelli.com/18.html
相关参考:https://blog.csdn.net/wxz1179503422/article/details/84874171