Android8.0以后共享文件

转载注明出处: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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值