解决 : FileUriExposedException: error when installing Apk on Android phones with version number greater than N

android.os.FileUriExposedException: file:///storage/emulated/0/Download/xxxAppName.apk exposed beyond app through Intent.getData()
Copy the code

The code of the mobile terminal call is as follows:

Intent intentUpdate = new Intent("android.intent.action.VIEW");
 intentUpdate.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 Uri apkUri = Uri.fromFile(new File(upgradeMsg.apkpath));   
 intentUpdate.setDataAndType(apkUri, "application/vnd.android.package-archive");
 startActivity(intentUpdate);  
Copy the code

An application cannot share a resource directly to another via the **file:// Uris ** Uri. Instead, the platform needs to share the resource via content://Uris ** so that the platform can extend the temporary permission of the receiving application to access the resource. However, the previous version of N can share resources via file://. The main reasons are summarized as follows:

  1. If the shared file is private, the App receiving the file://Uri cannot access the file
  2. If the app file://Uri does not apply for the manifest.permission.READ_EXTERNAL_STORAGE permission, it will crash when reading the file

Google also has a solution for this error, using the FileProvider, which creates a content://Uri for files instead of the file://Uri for secure file sharing. FileProvider main steps:

  1. Define FileProvider
  2. Specifying available files
  3. Retrieves the URI of the file contents
  4. Authorization URI Temporary permission
  5. Provide a content URI to another application

Add the following code to androidManifest.xml

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${APPLICATIONID}.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true"
            tools:replace="android:authorities">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path" />
</provider>
Copy the code

FileProvider grantUriPermissions: must be true. Grant URI temporary access. Exported: must be false. @xml/file_paths in is the next file to add

Create an XML folder in the res directory and create an XML file for file_Paths

  <paths>
        <external-path
            name="download"
            path="" />
    </paths>
Copy the code

Path: The path to which temporary authorization is required (.stands for all paths) name: The name you give to the access path

Now all you need to do is change the installation code, which looks like this:

 private void installApk(a) { // Setup program
        Intent  intentUpdate = new Intent("android.intent.action.VIEW");
        intentUpdate.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {  // Make a judgment on Android N and above
            Uri apkUriN = FileProvider.getUriForFile(MainActivity2.this,
            MainActivity2.this.getApplicationContext().getPackageName() + ".FileProvider".new File(upgradeMsg.apkpath));
            intentUpdate.addCategory("android.intent.category.DEFAULT");
            intentUpdate.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);   // What permissions do we need
            intentUpdate.setDataAndType(apkUriN, "application/vnd.android.package-archive");
        } else {
            Uri apkUri = Uri.fromFile(new File(upgradeMsg.apkpath)); 
            intentUpdate.setDataAndType(apkUri, "application/vnd.android.package-archive");
        }
        startActivity(intentUpdate);
    }
Copy the code

When the code is written, do you think it is ok? When you’re happily installing an update from 6.0 or 7.0 to 8.0, it’s fine, but when you’re installing an update from 8.0 or above, it flashes, or the parse package fails. Why is that? Because in Aandroid 8.0 Google has done some restrictions

In Android 8.0 Oreo, Google has removed the “allow location source” switch, which is easy to abuse. When installing Android apps from third-party sources other than the Play Store, there is no “allow unknown source” check box. If you still want to install an app from a developer you trust, you’ll need to manually grant an “install unknown app” license each time.

B: We need version 8.0 and above. You first need to add permissions in aAndroidManifest.xml

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

Second, when you click update, you need to judge the mobile version information:

private static final int INSTALL_PACKAGES_REQUESTCODE = 10011;
private static final int GET_UNKNOWN_APP_SOURCES = 10012;
private void checkAndroidO(a) { 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // The system is Android O or later
          // Whether to handle the unknown application source permission. True Indicates that the user trusts the installation package. False Indicates that the authorization is required
            boolean canRequestPackageInstalls = getPackageManager().canRequestPackageInstalls();  
            if (canRequestPackageInstalls) {  
                installApk();
            } else {
              Request permission to install an unknown application source
              ActivityCompat.requestPermissions(this.newString[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_PACKAGES_REQUESTCODE); }}else {  // Direct installation processinstallApk(); }}@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case INSTALL_PACKAGES_REQUESTCODE:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // If you already have this permission, install it directly. Otherwise, go to the authorization interface
                    installApk();
                } else {
                    Uri packageURI = Uri.parse("package:" + getPackageName());   // Get the package name and jump directly to the corresponding App authorization interface
                    Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
                    startActivityForResult(intent, GET_UNKNOWN_APP_SOURCES);
                }
                break;
        }
      
      // We need to continue to do some corresponding processing in the onActivityResult method, so that the successful authorization back App can be installed directly
       @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // After 8.0, the system strongly updates the authorization interface
        switch (requestCode) {
            case GET_UNKNOWN_APP_SOURCES:
                checkAndroidO();
                break;
            default:
                break; }}Copy the code

Well, here to adapt 7.0 above models of the installation problem is all solved.