preface

The update of Android app is a problem we often need to meet in our daily development. Usually we use a third party network loading library to download the address and then update it. For example, okHttp, Volley, etc., all have download function.

However, when we use these third-party libraries for downloading, we may need to do a lot of extra processing, such as processing progress during update. Writing a Notification to tell you to download the display adds a lot of unnecessary trouble when writing code. In fact, the Android system has already come with a download library, DownloadManage, and it has helped us deal with a lot of things, we just need to know how to use it, and then do some packaging will be able to deal with most of our daily download problems.

So let’s start with some common API uses.

DownloadManager

First of all, download, of course, need network access and file read and write permission, otherwise how to download without network? Where to put the APK after downloading? So we first add permissions to the manifest file.

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    
Copy the code

The next step is to instantiate the DownloadManager class and pass in the download address.

 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
Copy the code

Inside the DownloadManager, we determine what environment the phone is in, which means we can set whether to download on wifi or mobile.

request.setAllowedNetworkTypes()
Copy the code
  • DownloadManager.Request.NET WORK_WIFI: under the condition of wifi to download

  • DownloadManager.Request.NET WORK_MOBILE: representative under the mobile network to download

If the download is set to wifi, but switched to 4G network, then the program will automatically stop, if this time to switch back, then it will automatically download, and will automatically continue.

Notification customization:

When you click to download, a notification appears in the dropdown box to indicate that the download has taken place. The DownloadManager does this smartly, with a few lines of code directly handling this complex function.

        // Display notification when downloading
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        // Add the description
        request.setDescription(description);
Copy the code
  • VISIBILTY_HIDDEN: Notification: Will not be displayed, permissions must be added if this property is set. Android. Permission. DOWNLOAD_WITHOUT_NOTIFICATION. VISIBILITY_VISIBLE: Notification, but only in the process of downloading task execution, according to the download is complete disappear automatically. (Default value)

  • VISIBILITY_VISIBLE_NOTIFY_COMPLETED: Notification display, both when the download is in progress and after completion.

  • VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION: The Notification is displayed only when the task is complete.

You can then set the storage address

        //file:///storage/emulated/0/Download/downloadName.apk
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, downloadName +".apk");
Copy the code

Finally, the request is enqueued and the download can begin.

        request.setMimeType("application/vnd.android.package-archive");
        DownloadManager systemService = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
        systemService.enqueue(request);
Copy the code

At this point, you should see the download begin:

So how do you know when the download is complete to install? Inside the DownloadManager, a broadcast is sent after the download is complete to tell the download is complete. DownloadManager.ACTION_DOWNLOAD_COMPLETE

We can then write a broadcast to receive the broadcast and handle the installation event.

class DownloadCompleteBroadcast extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){

                //TODO...}}}Copy the code

Compatible processing:

At the time of installation began to reflect the version of the difference, the need to start to do compatibility. We in the 6.0 version below, can directly use the following code to install.

            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.parse("file:///storage/emulated/0/Download/" + downloadName +".apk"), "application/vnd.android.package-archive");
            // Start a new activity stack for the new APK
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            // Start installation
            startActivity(intent);
Copy the code

6.0 compatible with:

Dynamic permissions introduced in 6.0. In other words, we set permissions in the manifest file, but when we need some more private permissions, the user must choose. If we don’t let the user choose these permissions, the application will crash. A useful dynamic permissions library, RxPersmissions, is recommended. This library utilizes the chain thinking of RxJava to deal with dynamic permissions. Github address: RxPermissions It is also very simple to use, directly in the download of the time to give permission to authorize prompts.

RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    .subscribe(new Consumer<Permission>() {
                        @Override
                        public void accept(Permission permission) throws Exception {
                            if (permission.granted){
                                //TODO...
                                
                            }else {
                                Toast.makeText(context, "Permissions not enabled", Toast.LENGTH_SHORT).show(); }}});Copy the code

7.0 compatible with:

Android 7 starts to increase security, privatize files, and share files with other applications, such as the APK installer. You need to configure shared files through the FileProvider. The configuration table is based on XML files, and then through Content Uris carry the configuration file XML to share files.

There are two steps to configure the FileProvider: Step 1: Configure the Androidmanifest.xml manifest.

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.qubin.downloadmanager"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
Copy the code

Step 2: Create the file res/ XML /file_paths.xml.

<? xml version="1.0" encoding="utf-8"? > <resources> <paths> <! -- files-path: provides files/subdirectories in the internal storage area of the application. It corresponds to the path returned by context.getfilesdir: eg: "/data/data/com.***.***/files". Cache-path: provides files in the cache subdirectory of the internal storage of the application. It corresponds to the path returned by context. getCacheDir :eg: "/data/data/com.***.***/cache"; External-path: provides files in the root directory of the external storage area. It corresponds to the Environment. External.getexternalstoragedirectory return to the path of the external files - path: Context. GetExternalFilesDir (null) external cache - path: Context. GetExternalCacheDir (String) - > < external - path name ="external" path="" />  
    </paths>  
</resources>

Copy the code

Where path=”” stands for root, that is, sharing the root and any files in its subdirectories with shared applications. Theoretically, if the sharing program is a malicious program, it can access all of your application’s shared file information.

Finally, prepare the above two steps to install the file


            File file= new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "/" + downloadName +".apk");
            Parameter 1 Context, Parameter 2 Provider Host address consistent with that in the configuration file parameter 3 Shared file
            Uri apkUri = FileProvider.getUriForFile(context, "com.qubin.downloadmanager", file);

            Intent intent = new Intent(Intent.ACTION_VIEW);
            // Since the Activity is not started in the Activity environment, set the following labels
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            // Add this sentence to apply temporary authorization to the file represented by the Uri to the target
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            startActivity(intent);
Copy the code

8.0 compatible with:

What changes have Android 8 made to the way apK is installed?

In an August 29, 2017, Google developer blog post wrote that “Getting apps more securely in Android O” is a new installation of unknown apps,Android O The option of always installing unknown applications is disabled, and the setting prompt is displayed when installing unknown applications. This reduces the behavior of malicious applications deceiving users through fake installation interfaces. So developers need to adjust permissions in the AndroidManifest file and add REQUEST_INSTALL_PACKAGES permissions.

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

Google suggest is through the PackageManager canRequestPackageInstalls () API, track the status of the permissions, and then use ACTION_MANAGE_UNKNOWN_APP_SOURCES Intent.

Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);

startActivityForResult(intent, RESULT_CODE);

Copy the code

However, I do not recommend using this Intent because ACTION_MANAGE_UNKNOWN_APP_SOURCES Intent jumps to the list of all applications, and then selects the corresponding APP from a large number of applications to enter and then enable the permissions. This provides poor user experience. You can wait until the installation time to click on the jump to develop this permission.

encapsulation

Ok, with some of the above operations, then I use the Builder mode directly for a layer of encapsulation operations, it will be convenient for us to use this download method. The specific Builder writing method is not difficult, here do not do too much explanation, directly look at the code can understand.

In addition, when we use update, generally speaking, we will first go through the network request interface, get the update prompt copy, pop up a dialog popup window, and click download to start downloading. I’ve also written a generic dialog here, from which I can operate. Also use the Builder design mode. If you don’t understand this, you can refer to another article I wrote. Build the Wheel – create a generic version of the Dialog in Builder mode.

Here just wrote a general interface, the specific interface operation, you can go according to the demo to modify.

Before we use this dialog:

commonDialog = new CommonDialog.Builder(MainActivity.this)
                        .view(R.layout.dialog) // Layout file
                        .style(R.style.Dialog) // Style transparent
                        .setMessage(R.id.txt_sure,"Start updating") // Update button text
                        .setMessage(R.id.txt_cancel,"Cancel update") // Cancel button text
                        .addViewOnClick(R.id.txt_sure, new View.OnClickListener() { // Click the start update button click the event
                            @Override
                            public void onClick(View v) {

                                Toast.makeText(MainActivity.this."Start downloading", Toast.LENGTH_SHORT).show();
                                commonDialog.dismiss();
                            }
                        })
                        .addViewOnClick(R.id.txt_cancel, new View.OnClickListener() { // Cancel the button click event
                            @Override
                            public void onClick(View v) {
                                commonDialog.dismiss();
                            }
                        })
                        .build();

                commonDialog.show();
Copy the code

While you’re updating, write the next line of code to get started

                                        new DownLoadBuilder.Builder(MainActivity.this)
                                        .addUrl(url)
                                        .isWiFi(true)
                                        .addDownLoadName(apkName)
                                        .addDscription("Start downloading")
                                        .builder();
Copy the code

Is it convenient? Don’t forget to write a broadcast to receive the download completion event.

All of the code is on Github, and if you want to use the above two methods, you just need to add the DownLoadBuilder and CommonDialog classes to your project.

If so, start is welcome.

Github address for code dmeo

Have interest can pay attention to my small column, learn more workplace product thinking knowledge: small column