美文网首页Android开发
Android 7.0和Android 8.0 使用Intent

Android 7.0和Android 8.0 使用Intent

作者: Everglow_111 | 来源:发表于2019-01-16 12:23 被阅读0次

    前言

    对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在应用外部公开 file:// URI , 如果一项包含文件 URI 的 intent 离开应用,则应用出现故障,并出现 FileUriExposedException 异常。

    要应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。如需了解有关权限和共享文件的详细信息,请参阅 共享文件

    FileProvider 实际上是 ContentProvider 的一个子类,它的作用也比较明显,file://Uri 不给用,那么换个 Uri 为 content:// 来替代。

    解决方法

    1.在manifest中注册FileProvider
      <application>
         <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="hzyj.come.p2p.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true"
                tools:replace="name,authorities,grantUriPermissions,exported">
    
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths"
                    tools:replace="name,resource"
                    />
            </provider>
        </application>
    
    

    如果出现如下报错,可能是引入的第三方jar包或类库已经添加过
    只需如上添加 :
    tools:replace="name,authorities,grantUriPermissions,exported"
    tools:replace="name,resource"

    image.png
    2、指定可用的文件路径

    在项目的res目录下,创建xml文件夹,并新建一个file_paths.xml文件。通过这个文件来指定文件路径:

    <?xml version="1.0" encoding="utf-8"?>
    <paths>
        <!--path:需要临时授权访问的路径(.代表所有路径) name:就是你给这个访问路径起个名字-->
        <external-path
            name="external-path"
            path="."/>
        <files-path
            name="files_path"
            path="."/>
        <external-cache-path
            name="external_cache_path"
            path="."/>
        <external-files-path
            name="external_files_path"
            path="."/>
        <cache-path
            name="cache_path"
            path="."/>
    </paths>
    

    paths 节点内部支持以下几个子节点,分别为:

    子节点 含义 目录
    <root-path> 代表设备的根目录 new File("/") 根目录
    <files-path> 代表 context.getFileDir() /data/data/com.xxx.app/files
    <cache-path> 代表 context.getCacheDir() /data/data/com.xxx.app/cache
    <external-path> 代表 Environment.getExternalStorageDirectory() /storage/emulated
    <external-files-path> 代表 context.getExternalFilesDirs() /storage/emulated/0/Android/data/com.xxx.app/files
    <external-cache-path> 代表 getExternalCacheDirs() /storage/emulated/0/Android/data/com.xxx.app/cache
    <external-path name="external" path="pics"/>
    

    代表的目录即为:Environment.getExternalStorageDirectory()/pics

    3、DownloadManager 进行下载并注册BroadcastReceiver监听
    
        /**
         * @param downloadUrl   下载url
         * @param desc          更新描述
         * @param latestVersion 最新版本号
         */
        private void initUpDate(final String downloadUrl, String desc, final int latestVersion) {
            apkPath = Tools.getExternalStoragePublicDirectory() + File.separator + getString(R.string.apk_name);
            mUpdateDialog = new UpdateDialog(getContext());
            mUpdateDialog.setUpdateDialogCallBack(() -> downloadApk(downloadUrl, latestVersion));
            mUpdateDialog.showDialog(desc);
        }
      /**
         * @param downloadUrl   下载url
         * @param latestVersion 最新版本号
         */
        public void downloadApk(String downloadUrl, int latestVersion) {
            final DownloadManager dManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
            File apkFile = new File(apkPath);
            if (apkFile.exists()) {
                PackageManager pm = getContext().getPackageManager();
                PackageInfo packageInfo = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
                if (packageInfo != null) {
                    int versionCode = packageInfo.versionCode;
                    //已下载最新安装包直接跳转安装界面
                    if (versionCode == latestVersion) {
                        startInstall();
                        return;
                    }
                }
                apkFile.delete();
            }
            Uri uri = Uri.parse(downloadUrl);
            DownloadManager.Request request = new DownloadManager.Request(uri);
            request.setDestinationUri(Uri.fromFile(new File(apkPath)));
            request.setDescription(getString(R.string.app_name) + "版本更新");
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            request.setMimeType("application/vnd.android.package-archive");
            // 设置为可被媒体扫描器找到
            request.allowScanningByMediaScanner();
            // 设置为可见和可管理
            request.setVisibleInDownloadsUi(true);
            // 获取此次下载的ID
            final long refernece = dManager.enqueue(request);
    
            // 注册广播接收器,当下载完成时自动安装
            IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
            mReceiver = new BroadcastReceiver() {
    
                public void onReceive(Context context, Intent intent) {
                    long myDwonloadID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                    if (refernece == myDwonloadID) {
                        startInstall();
                    }
                }
            };
            getActivity().registerReceiver(mReceiver, filter);
        }
    
    
    4、下载完成后跳转安装界面
        private void startInstall() {
            Intent localIntent = new Intent(Intent.ACTION_VIEW);
            localIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            Uri uri;
            /**
             * Android7.0+禁止应用对外暴露file://uri,改为content://uri;具体参考FileProvider
             */
            if (Build.VERSION.SDK_INT >= 24) {
    
                uri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID+".fileprovider",
                        new File(apkPath));
                localIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            } else {
                uri = Uri.fromFile(new File(apkPath));
            }
            Log.d("BroadcastReceiver", "uri =" + uri);
            localIntent.setDataAndType(uri, "application/vnd.android.package-archive"); //打开apk文件
            try {
                startActivity(localIntent);
            } catch (ActivityNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    

    FileProvider映射图示


    image.png

    另外需要注意android6.0动态权限以及

    android8.0要多加一条权限

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    

    判断一下8.0特殊处理 然后正常走就可以了,坑!!!!
    Demo

    相关文章

      网友评论

        本文标题:Android 7.0和Android 8.0 使用Intent

        本文链接:https://www.haomeiwen.com/subject/xaapdqtx.html