Recently, I am working on the APP upgrade function, which needs to be installed after the APK file is downloaded. I thought it was a very simple function, which can be completed by directly calling the interface of the system, but I did not expect that there are still many pits.

Android version 6.0 installation

This is the most common, and is common in version 6.0 and earlier.

/** * Install apK **@param* /
public static void install(Context mContext, File file) {
    Intent install = new Intent(Intent.ACTION_VIEW);
    install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
    mContext.startActivity(install);
}
Copy the code

Android 7.0 installation

The above code in Android version 7.0 and above to install apk will meet Android. The OS. FileUriExposedException problem. The official explanation:

For Android 7.0 oriented applications, StrictMode API policies enforced by the Android framework prohibit exposing the file:// URI outside of your application. If an intent containing the URI of the file leaves your application, the application fails and a FileUriExposedException occurs.

Solutions are as follows:

1. Add the following code to androidmanifest.xml:

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="Your package name.fileProvider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
Copy the code

Pay attention to

Authorities: the package name of your app fileProvider grantUriPermissions: must betrueExported: Specifies that a URI is granted temporary access permissionfalse@xml/ File_paths in Resource: is the file we will add nextCopy the code

There are additional pits ☠️

I encountered manifest merge conflicts after adding the above provider code in the use process:

Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed with multiple errors, see logs
Copy the code

Solution: Because multiple lib libraries define the same class and therefore conflict, integrate FileProvider to rewrite the class. If this problem is not encountered, you can skip it. The code is as follows:

public class InstallApkProvider extends FileProvider {}Copy the code

So I added the following code to the Manifest:

<provider
    android:name=".InstallApkProvider"
    android:authorities="My package name.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
Copy the code

2. Create file_paths.xml under RES/XML

<?xml version="1.0" encoding="utf-8"? >
<resources>
    <paths>
        <root-path name="root" path="" />
        <files-path name="files" path="" />
        <cache-path name="cache" path="" />
        <external-path name="external" path="" />
        <external-files-path name="external_file_path" path="" />
        <external-cache-path name="external_cache_path" path="" />
    </paths>
</resources>
Copy the code

Pay attention to

Path: indicates the path that requires temporary authorization. Name: indicates that you give the access path a nameCopy the code

In 6.0, uri.fromfile (apkFile) was used directly to construct a Uri. Now we use uri.fromfile

FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
Copy the code

Buildconfig. APPLICATION_ID is directly the package name of the application.

At this point, we are happy to install APK on 7.0. The code is as follows:

    /** * Install apK **@param* /
    public static void install(Context mContext, File file) {
        Intent install = new Intent(Intent.ACTION_VIEW);
        install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(mContext, "com.skycar.vehicleassistor.fileProvider", file);

            install.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        mContext.startActivity(install);
    }
Copy the code

Android 8.0 installation

If you are running 8.0+, you need to add an extra permission, otherwise it still won’t work! 🤣

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

conclusion

That’s it. We’re done installing apK files on different Versions of Android. Then spend 🎉