Downloadmanager实现app实现的升级下载使用

Posted luzhouxiaoshuai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Downloadmanager实现app实现的升级下载使用相关的知识,希望对你有一定的参考价值。

1、app升级下载现在不推荐使用downloadmanager下载:

原因有下面的几个方面:

(1)三星note系列部分手机需要手动打开这个权限才能用这个功能,而有些国产手机更加nb了直接个阉割了(downloadmanager),所以考虑到手机的适配性,最后自己编写app下载的后台代码

2、但是这里还是对downloadmanager下载进行一些详细的分析,很多下载的思路还是值得借鉴的

1、首先后台的apk的搭建

1、打开本地Tomcat服务器,放入一个Apk文件

 

apk存放在webapps/root目录下

// 首先确保浏览器能够访问http://localhost:8080/123456.apk

下面我将内容大致分为以下几个部分:

(1)App版本检测

(2)Apk下载

(3)Apk更新安装

(4)对以上功能进行封装

基于以上4部分,我们逐一展开。

1.App版本检测:

要实现App的更新下载,我们上面介绍了,前提是服务器要保存一个App的版本号(通常的方式是保存versionCode,当然你要对比versionName也没关系)。当用户去手动检测版本,或者进入首页自动检测时,第一步是需要请求服务器的版本号,拿到版本号之后与当前App版本号(当前版本号可通过PackageInfo获取)进行对比。服务器返回的版本号大于当前App版本号,证明App已经有更新,那么进入第2步。

2.Apk下载

Apk文件是保存在服务器的。我们可以通过Http流将其下载到本地手机,然后更新安装。Android中下载的方式很多种:HttpUrlConnection,Retrofit,okHttp,以及Android原生的下载工具类DownLoadManager 等等。我们采用的方式是Google推荐的下载工具类DownLoadManager。关于DownLoadManager的使用其实很简单,简单概括如下:

(1)通过getSystemService获取DownLoadManager。

(2)初始化DownLoadManager的Request,构建下载请求。

(3)调用DownLoadManager的enqueue异步发起请求,该方法返回值为标识当前下载任务的id,即downloadId。

(4)当下载完成后,系统会发出条件为android.intent.action.DOWNLOAD_COMPLETE的广播,我们可以自定义广播接受器,然后在onReceive中处理下载完成的逻辑即可。

详细使用方式大家可以参考网上的教程,此处就不再赘述。

上面通过下载啰嗦了一堆。此时我们要想一个问题:当我们下载完成后,并没有安装。当用户再次进入App时该如何操作?

有朋友会说,那就再去下载一次,然后继续执行更新安装呀!哈哈,这种方式是没有错误的,但是如果用户恶意行为,每次下载完成都不安装,那我们岂不是每次都要去下载100次,1000次。。(然后手机boom!!!)这种方式肯定是不能采用的。那么我们该如何解决呢?

很简单,当我们在下载之前,先去指定的文件夹下查看有木有已经下载好的Apk,并且该Apk的版本是高于本App的版本,此时我们就去执行安装操作。如果上面条件不成立,此时再去执行下载操作。

3.Apk更新安装

相信大家对于如何安装一个Apk都比较熟悉吧,原理也是比较简单的。

(1)通过downloadId获取下载的Uri。

(2)将Uri设置到Itent的setDataAndType作为启动条件。

(3)调用startActivity启动对应Intent即可。

以上3步,即可完成App的更新功能。

整体的流程很清晰:

版本检测 → Apk下载 (检查是否存在未安装的Apk) → Apk安装 → 完成更新

下面,通过代码来具体分析整个流程:

关于App版本检测其实就是一个Http请求,不再多说。我们从Apk下载开始:

上面我们提到,在下载之前需要去检测是否存在已经下载的Apk。通过什么获取呢?没错,肯定是downloadId了。

1> 如果存在downloadId,那么我们通过downloadId获取当前下载的状态status。status分为成功,失败两种状态。

(1)当status为成功状态时,即已经下载完成,我们就通过downloadId获取下载文件的Uri。然后可以通过Uri获取PackageInfo,与当前App进行包名和版本号的对比,当包名相同,并且当前版本号是小于下载的Apk版本号两个条件同时成立时,直接执行安装操作。否则,执行remove,通过downloadId删除下载任务以及文件,继续执行下载。

(2)当status为失败状态时,即下载未完成,我们就直接执行重新下载即可。

2> 如果不存在downloadId,即没有下载过Apk,执行下载即可。

 

下载完成后,系统会发出广播,在广播中,我们对比downloadId是否相同,相同情况下,直接通过downloadId获取Uri,然后跳转到安装界面,提示用户安装即可:

 

所以,别忘了在下载之前要先将该大喇叭(广播接受器)注册。

 

最后,当我们安装完成后,再次进入App,就将其已下载的Apk文件进行删除(将该方法放在onCreate生命周期中即可):

 

上面通过downloadApk获取下载文件的地址。downloadApk地址是在下载完成后广播接收器中保存的。

通过上面的步骤,我们就完成了App更新下载安装的全部工作。相信大家也有了更深的认识和理解。

下面博客:

http://chuansong.me/n/1090429551927

http://chuansong.me/n/1090429551927

也是对downloadManger做了详细的解释

接下来我们看下代码

程序的框架如下所示:

 

package co.huiqu.webapp;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import co.huiqu.webapp.download.DownLoadUtils;
import co.huiqu.webapp.download.DownloadApk;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        //1.注册下载广播接收器
        DownloadApk.registerBroadcast(this);
        //2.apk按照成功之后再次进场到app删除已存在的Apk
        DownloadApk.removeFile/**/(this);
        fab.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
       /*         String packageName = "com.android.providers.downloads";
                Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.parse("package:" + packageName));
              startActivity(intent);*/

                //3.如果手机已经启动下载程序,执行downloadApk。否则跳转到设置界面
                if (DownLoadUtils.getInstance(getApplicationContext()).canDownload()) {
                    //DownloadApk.downloadApk(getApplicationContext(), "http://www.huiqu.co/public/download/apk/huiqu.apk", "Hobbees更新", "Hobbees");
                    DownloadApk.downloadApk(getApplicationContext(), "http://10.12.8.13:8080/123456.apk", "酷狗更新", "Hobbees下载");
                } else {
                    DownLoadUtils.getInstance(getApplicationContext()).skipToDownloadManager();
                }
            }
        });
    }

    @Override
    protected void onDestroy() {

        //4.反注册广播接收器
        DownloadApk.unregisterBroadcast(this);
        super.onDestroy();
    }
}

 

 sharePrefer的工具类代码:

package co.huiqu.webapp.config;

import java.util.Set;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * 
 * 保存系统信息
 * @author song
 * @date 2015年11月4日
 */
public class SystemParams {

    private static SystemParams instance;
    private static SharedPreferences sharedPrederences = null;

    private SystemParams() {
    }

    //在Application初始化
    public static void init(Context context) {
        sharedPrederences = context.getSharedPreferences("hobbees", Context.MODE_PRIVATE);
    }
    public static SystemParams getInstance() {
        
        if(instance == null) {
            synchronized (SystemParams.class) {
                if(instance == null) {
                     instance = new SystemParams();
                }
            }
        }
        return instance;
    }
    
    /**get**/
    public int getInt(String key){
        return sharedPrederences.getInt(key, 0);
    }
    
    public int getInt(String key,int defValue){
        return sharedPrederences.getInt(key, defValue);
    }    

    public float getFloat(String key){
        return sharedPrederences.getFloat(key, 0);
    }
    
    public float getFloat(String key,float defValue) {
        return sharedPrederences.getFloat(key, defValue);
    }    
    
    public long getLong(String key){
        return sharedPrederences.getLong(key, 0);
    }
    
    public long getLong(String key,long defValue) {
        return sharedPrederences.getLong(key, defValue);
    }        

    public String getString(String key){
        return sharedPrederences.getString(key, null);
    }
    
    public String getString(String key,String defValue) {
        return sharedPrederences.getString(key, defValue);
    }    
    
    public boolean getBoolean(String key){
        return sharedPrederences.getBoolean(key, false);
    }
    
    public boolean getBoolean(String key,boolean defValue) {
        return sharedPrederences.getBoolean(key, defValue);
    }
    
    /**set**/
    public void setInt(String key,int value) {
        Editor editor = sharedPrederences.edit();
        editor.putInt(key, value);
        editor.commit();
    }
    
    public void setFloat(String key,float value) {
        Editor editor = sharedPrederences.edit();
        editor.putFloat(key, value);
        editor.commit();
    }
    
    public void setLong(String key,long value) {
        Editor editor = sharedPrederences.edit();
        editor.putLong(key, value);
        editor.commit();
    }
    
    public void setString(String key,String value) {
        Editor editor = sharedPrederences.edit();
        editor.putString(key, value);
        editor.commit();
    }
    
    public void setBoolean(String key,boolean value) {
        Editor editor = sharedPrederences.edit();
        editor.putBoolean(key, value);
        editor.commit();
    }
    
    public void setSetString(String key,Set<String> values) {
        Editor editor = sharedPrederences.edit();
        editor.putStringSet(key, values);
        editor.commit();
    }
    
    public void remove(String key) {
        Editor editor = sharedPrederences.edit();
        editor.remove(key);
        editor.commit();
    }
    
    public void clear() {
        Editor editor = sharedPrederences.edit();
        editor.clear().commit();
    }
}

 

package co.huiqu.webapp.download;

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.widget.Toast;

import co.huiqu.webapp.config.SystemParams;

/**
 * Created by Song on 2016/11/2.
 */
public class ApkInstallReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        long downloadApkId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
            Toast.makeText(context,"收到apk下载完成的广播",Toast.LENGTH_LONG).show();

            int status =  DownLoadUtils.getInstance(context).checkStatus(downloadApkId);
            switch (status) {
                //下载暂停
                case DownloadManager.STATUS_PAUSED:
                    //获得下载暂停的原因等信息
                    Toast.makeText(context,"暂停下载"+DownLoadUtils.getInstance(context).getPausedReason(downloadApkId),Toast.LENGTH_LONG).show();
                    break;
                //下载延迟
                case DownloadManager.STATUS_PENDING:
                    //获得下载延迟的原因
                    break;
                //正在下载
                case DownloadManager.STATUS_RUNNING:
                    break;
                //下载完成
                case DownloadManager.STATUS_SUCCESSFUL:
                    //下载完成安装APK
                    installApk(context, downloadApkId);
                    break;
                //下载失败
                case DownloadManager.STATUS_FAILED:
                   // 获得下载失败的原因
                    Toast.makeText(context,"暂停下载"+DownLoadUtils.getInstance(context).getFailedReason(downloadApkId),Toast.LENGTH_LONG).show();
                    break;
            }


        }else if(DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())){

            //点击通知栏取消下载
            Toast.makeText(context,"通知栏被点击",Toast.LENGTH_LONG).show();

        }
    }

    /**
     * 安装apk
     */
    private void installApk(Context context,long downloadId) {

        long downId = SystemParams.getInstance().getLong(DownloadManager.EXTRA_DOWNLOAD_ID, -1L);
        if(downloadId == downId) {
            DownloadManager downManager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            Uri downloadUri = downManager.getUriForDownloadedFile(downloadId);
            SystemParams.getInstance().setString("downloadApk",downloadUri.getPath());
            if (downloadUri != null) {
                Intent install= new Intent(Intent.ACTION_VIEW);
                install.setDataAndType(downloadUri, "application/vnd.android.package-archive");
                install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(install);
            } else {
                Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
            }
        }
    }


}
package co.huiqu.webapp.download;

import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import java.io.File;

import co.huiqu.webapp.config.SystemParams;

/**
 * Apk下载
 * Created by Song on 2016/11/2.
 */
public class DownloadApk {

    private static ApkInstallReceiver apkInstallReceiver;

    /**
     * 下载APK文件
     * @param context
     * @param url
     * @param title
     * @param appName
     */
    public static void downloadApk(Context context, String url, String title,final String appName) {

        //获取存储的下载ID
        long downloadId = SystemParams.getInstance().getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L);
        if(downloadId != -1) {
            //存在downloadId
            DownLoadUtils downLoadUtils = DownLoadUtils.getInstance(context);
            //获取当前状态
            int status = downLoadUtils.getDownloadStatus(downloadId);
            if(DownloadManager.STATUS_SUCCESSFUL == status) {
                //状态为下载成功
                //获取下载路径URI
                Uri downloadUri = downLoadUtils.getDownloadUri(downloadId);
                if(null != downloadUri) {
                    //存在下载的APK,如果两个APK相同,启动更新界面。否之则删除,重新下载。
                    if(compare(getApkInfo(context,downloadUri.getPath()),context)) {
                        startInstall(context, downloadUri);
                        return;
                    } else {
                        //删除下载任务以及文件
                        downLoadUtils.getDownloadManager().remove(downloadId);
                    }
                }
                start(context, url, title,appName);
            } else if(DownloadManager.STATUS_FAILED == status) {
                //下载失败,重新下载
                start(context, url, title,appName);
            }else {
                Log.d(context.getPackageName(), "apk is already downloading");
            }
        } else {
            //不存在downloadId,没有下载过APK
            start(context, url, title,appName);
        }
    }

    /**
     * 开始下载
     * @param context
     * @param url
     * @param title
     * @param appName
     */
    private static void start(Context context, String url, String title,String appName) {

        if(hasSDKCard()) {
            long id = DownLoadUtils.getInstance(context).download(url,
                    title, "下载完成后点击打开", appName);
            SystemParams.getInstance().setLong(DownloadManager.EXTRA_DOWNLOAD_ID,id);
        } else {
            Toast.makeText(context,"手机未安装SD卡,下载失败",Toast.LENGTH_LONG).show();
        }
    }

    public static void registerBroadcast(Context context) {
        apkInstallReceiver = new ApkInstallReceiver();
        context.registerReceiver(apkInstallReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
        context.registerReceiver(apkInstallReceiver, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED));
    }

    public static void unregisterBroadcast(Context context) {
        if(null != apkInstallReceiver) {
            context.unregisterReceiver(apkInstallReceiver);
        }
    }

    /**
     * 跳转到安装界面
     * @param context
     * @param uri
     */
    private static void startInstall(Context context, Uri uri) {

        Intent install = new Intent(Intent.ACTION_VIEW);
        install.setDataAndType(uri, "application/vnd.android.package-archive");
        install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(install);
    }


    /**
     * 获取APK程序信息
     * @param context
     * @param path
     * @return
     */
    private static PackageInfo getApkInfo(Context context, String path) {

        PackageManager pm = context.getPackageManager();
        PackageInfo pi = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
        if(null != pi) {
            return pi;
        }
        return null;
    }


    /**
     * 比较两个APK的信息
     * @param apkInfo
     * @param context
     * @return
     */
    private static boolean compare(PackageInfo apkInfo,Context context) {

        if(null == apkInfo) {
            return false;
        }
        String localPackageName = context.getPackageName();
        if(localPackageName.equals(apkInfo.packageName)) {
            try {
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackageName, 0);
                //比较当前APK和下载的APK版本号
                if (apkInfo.versionCode > packageInfo.versionCode) {
                    //如果下载的APK版本号大于当前安装的APK版本号,返回true
                    return true;
                }
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 是否存在SD卡
     */
    private static boolean hasSDKCard() {
        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    }

    /**
     * 删除已下载的文件
     */
    public static void removeFile(Context context) {
        String filePath = SystemParams.getInstance().getString("downloadApk",null);
        if(null != filePath) {
            File downloadFile = new File(filePath);
            if(null != downloadFile && downloadFile.exists()) {
                //删除之前先判断用户是否已经安装了,安装了才删除。
                if(!compare(getApkInfo(context,filePath),context)) {
                    downloadFile.delete();
                    Log.e("----", "已删除");
                }
            }
        }
    }
}

 

package co.huiqu.webapp.download;

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import java.io.File;

import co.huiqu.webapp.config.SystemParams;

/**
 * 封装 DownLoadManager 下载
 * Created by Song on 2016/11/2.
 */
public class DownLoadUtils {

    private Context mContext;
    private DownloadManager mDownloadManager;
    private static volatile DownLoadUtils instance;

    private DownLoadUtils(Context context) {
        this.mContext = context.getApplicationContext();
        mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    }

    /**
     * 获取单例对象
     *
     * @param context
     * @return
     */
    public static DownLoadUtils getInstance(Context context) {

        if (instance == null) {
            synchronized (DownLoadUtils.class) {
                if (instance == null) {
                    instance = new DownLoadUtils(context);
                    return instance;
                }
            }
        }
        return instance;
    }

    /**
     * 下载
     *
     * @param uri
     * @param title
     * @param description
     * @param appName
     * @return downloadId
     */
    public long download(String uri, String title, String description, String appName) {

        //1.构建下载请求
        DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(uri));
        downloadRequest.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
        downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        /**设置漫游状态下是否可以下载*/
        downloadRequest.setAllowedOverRoaming(false);
        /**如果我们希望下载的文件可以被系统的Downloads应用扫描到并管理,
         我们需要调用Request对象的setVisibleInDownloadsUi方法,传递参数true.*/
        downloadRequest.setVisibleInDownloadsUi(true);
        //文件保存位置
        //file:///storage/emulated/0/Android/data/your-package/files/Download/appName.apk
        downloadRequest.setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, appName + ".apk");
        // 设置一些基本显示信息
        downloadRequest.setTitle(title);
        downloadRequest.setDescription(description);
        //req.setMimeType("application/vnd.android.package-archive");
        return mDownloadManager.enqueue(downloadRequest);//异步请求
    }

    /**
     * 获取文件下载路径
     *
     * @param downloadId
     * @return
     */
    public String getDownloadPath(long downloadId) {

        DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
        Cursor c = mDownloadManager.query(query);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    return c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
                }
            } finally {
                c.close();
            }
        }

        return null;
    }

    /**
     * 获取文件保存的地址
     *
     * @param downloadId
     * @return
     */
    public Uri getDownloadUri(long downloadId) {
        return mDownloadManager.getUriForDownloadedFile(downloadId);
    }

    public DownloadManager getDownloadManager() {
        return mDownloadManager;
    }

    /**
     * 获取下载状态
     *
     * @param downloadId
     * @return
     */
    public int getDownloadStatus(long downloadId) {

        DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
        Cursor c = mDownloadManager.query(query);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                }
            } finally {
                c.close();
            }
        }
        return -1;
    }

    /**
     * 判断下载管理程序是否可用
     *
     * @return
     */
    public boolean canDownload() {

        try {
            int state = mContext.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
            if (state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                    || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
                    || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 进入 启用/禁用 下载管理程序界面
     */
    public void skipToDownloadManager() {

        String packageName = "com.android.providers.downloads";
        Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse("package:" + packageName));
        mContext.startActivity(intent);
    }


    /**
     * 进入 启用/禁用 下载管理程序界面
     */
    public int checkStatus(long downloadId) {
        DownloadManager.Query query = new DownloadManager.Query();
        //通过下载的id查找
        query.setFilterById(downloadId);
        Cursor c = mDownloadManager.query(query);
        int status = -1;
        if (c.moveToFirst()) {
            status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));

        }
        return status;
    }


    /**
     * 查询当前下载暂停失败的原因
     */

    安卓开发实战之app之版本更新升级(DownloadManager和http下载)完整实现

Android中的文件下载——DownLoadManager

如何使用 DownloadManager 将下载的文件存储到应用程序分配的文件目录中?

Fiddler用AutoResponder实现app升级异步更新

关于android8.1实现多个app升级时的注意事项

鹅厂bugly应用升级不能安装(Android7.0的新变化)