android自动更新模块实现中知识总结

认真查看第八项


1、获取version code

/**
     * 获取包版本号version code
     * @param context context
     * @return 正常来说大于零,如果小于0(也就是为-1)时,则意为着获取失败
     */
    public static int getVersionCode(Context context){
        int versionCode = -1;
        PackageManager packageManager = context.getPackageManager() ;

        if (packageManager != null) {

            PackageInfo packageInfo = null ;
            try{
                packageInfo = packageManager.getPackageInfo(context.getPackageName(),0) ;
            }catch (PackageManager.NameNotFoundException e){
                e.printStackTrace();
            }

            if (packageInfo != null){
                versionCode = packageInfo.versionCode;

            }
        }
        return versionCode;
    }


2、获取本程序的包名

/**
     * 获取包版本号packageName
     * @param context context
     * @return 程序的包名
     */
    public static String getPackageName(Context context){
        String packageName = null;
        PackageManager packageManager = context.getPackageManager() ;

        if (packageManager != null) {

            PackageInfo packageInfo = null ;
            try{
                packageInfo = packageManager.getPackageInfo(context.getPackageName(),0) ;
            }catch (PackageManager.NameNotFoundException e){
                e.printStackTrace();
            }

            if (packageInfo != null){
                packageName = packageInfo.packageName;

            }
        }
        return packageName;
    }
3、获取application中的指定meta-data

/**
     * 获取application中指定的meta-data
     * @return 如果没有获取成功(没有对应值或者异常),则返回值为空
     */
    public static String getAppMetaData(Context ctx, String key) {
        if (ctx == null || TextUtils.isEmpty(key)) {
            return null;
        }
        String resultData = null;
        try {
            PackageManager packageManager = ctx.getPackageManager();
            if (packageManager != null) {
                ApplicationInfo applicationInfo = packageManager.getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA);
                if (applicationInfo != null) {
                    if (applicationInfo.metaData != null) {
                        resultData = applicationInfo.metaData.getString(key);
                    }
                }

            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        return resultData;
    }
4、调用系统下载

/**
     * 调用系统下载模块下载apk
     * @param context 上下文
     * @param apkUrl    apk对应的
     * @param folderName 下载文件将要保存的sd卡文件夹名称
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static void downloadApkViaSystemMethod(Context context, String apkUrl, String folderName) {

        //以下情况不予处理
        if (context == null || TextUtils.isEmpty(apkUrl)) {
            return;
        }
        if (TextUtils.isEmpty(folderName)) {
            folderName = "XXX" ;
        }
        GlobalContext.mFolderName = folderName;

        GlobalContext.mUrlDownload = apkUrl;

        GlobalContext.mSharedprename = folderName + "XXX";

        DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);

        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));

        isFolderExist(folderName);

        //表示设置下载地址为sd卡的<span style="font-family: Arial, Helvetica, sans-serif;">XXX</span><span style="font-family: Arial, Helvetica, sans-serif;">文件夹,文件名为</span><span style="font-family: Arial, Helvetica, sans-serif;">XXX</span><span style="font-family: Arial, Helvetica, sans-serif;">.apk</span>
        request.setDestinationInExternalPublicDir(folderName, "XXX.apk");
        //设置下载中通知栏提示的标题
        request.setTitle("标题XXX");
        //设置下载中通知栏提示的介绍
        request.setDescription("下载更新");
        //表示下载进行中和下载完成的通知栏是否显示。
        // 默认只显示下载中通知。VISIBILITY_VISIBLE_NOTIFY_COMPLETED表示下载完成后显示通知栏提示。
        // VISIBILITY_HIDDEN表示不显示任何通知栏提示,
        // 这个需要在AndroidMainfest中添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        //表示下载允许的网络类型,默认在任何网络下都允许下载。有NETWORK_MOBILE、NETWORK_WIFI、NETWORK_BLUETOOTH三种及其组合可供选择。
        // 如果只允许wifi下载,而当前网络为3g,则下载会等待。
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
        //移动网络情况下是否允许漫游。
        request.setAllowedOverRoaming(false);
        //设置下载文件的mineType。因为下载管理Ui中点击某个已下载完成文件及下载完成点击通知栏提示都会根据mimeType去打开文件
        request.setMimeType("application/vnd.android.package-archive");
        //调用downloadManager的enqueue接口进行下载,返回唯一的downloadId
        long downloadId = downloadManager.enqueue(request);
        LogAbout.d(TAG, "将downloadid存起来id=" + downloadId + "mSharedprename:" + mSharedprename);
        //将downloadId存起来,以备后用
        //java.lang.IllegalArgumentException: File http://www.lvye.cn/app/lvyehuodong.apk.xml contains a path separator
        GlobalContext.storageDownloadId(downloadId, GlobalContext.SHARED_PRE_KEY, GlobalContext.SHARED_PRE_NAME, context);
    }


5、监听下载完成事件

class CompleteReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            LogAbout.d(TAG, "接收到广播了");

            String action = intent.getAction();
            if (TextUtils.isEmpty(action)) {
                AlertUtil.alert("action为空", context);
                return;
            }
            assert action != null;

            if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
                LogAbout.d(TAG, "接收到的广播为下载完成");
                // get complete download id
                    long completeDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                    // to do here
                    long downloadId = GlobalContext.getDownloadId(SHARED_PRE_KEY, SHARED_PRE_NAME, context);
                    LogAbout.d(TAG, "接收到的downloadid为" + completeDownloadId + "本地存储的id为" + downloadId);
                    if (completeDownloadId == downloadId) {
                        String fileName = Environment.getExternalStoragePublicDirectory(GlobalContext.mFolderName).getAbsolutePath() + File.separator + "lvyehds.apk";
                        LogAbout.d(TAG, "apk文件路径为" + fileName);
                        if (TextUtils.isEmpty(fileName)) {
                            return;
                        }
                        VersionCheckUtil.installAPK(fileName, context);
                        //执行安装操作之后,取消监听
                        unRegisterReceiver(context);
                    }
            }

        }
    }

6、安装apk文件

/**
     * 安装apk文件
     */
    public static void installAPK(String apkPath, Context context) {
        File apkfile = new File(apkPath);
        if (!apkfile.exists()) {
            return;
        }
        // 通过Intent安装APK文件
        Intent intent = new Intent(Intent.ACTION_VIEW);
//记得一定要加上此句,否则会报 Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //"application/vnd.android.package-archive"
        intent.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
        context.startActivity(intent);
        // 如果不加上这句的话在apk安装完成之后点击单开会崩溃
        android.os.Process.killProcess(android.os.Process.myPid());
    }

7、卸载apk

    /**
     * 卸载应用程序
     */
    public static void uninstallAPK(Context context, String packageName) {
        if (TextUtils.isEmpty(packageName) || context == null) {
            return;
        }
        LogAbout.d(TAG, "packagename" + packageName);
        Uri packageURI = Uri.parse("package:" + packageName);
        Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
        //记得一定要加上此句,否则会报 Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
        uninstallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(uninstallIntent);
    }


8、很重要的一个,就是需要判断是否需要下载,因为有可能用户中止暂停了下载,或者网络不好等原因造成的下载中断


/**
     * 判断是否需要调用系统下载
     * @param id 调用downloadManager.enqueue(request)时返回的id,that means  an ID for the download, unique across the system. This ID is used to make future calls related to this download
     * @return true if need download,else return false
     */
    private static boolean isNeedDownload(long id, DownloadManager downloadManager) {

        if (downloadManager == null) {
            return true;
        }

        boolean isNeedDownloadAgain = true;

        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(id);
        Cursor cursor = downloadManager.query(query);
        if (cursor != null && cursor.moveToFirst()) {
            int columnStatus = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
            int status = cursor.getInt(columnStatus);
            int columnReason = cursor.getColumnIndex(DownloadManager.COLUMN_REASON);
            int reason = cursor.getInt(columnReason);

            switch (status) {
                case DownloadManager.STATUS_FAILED:
                    switch (reason) {
                        case DownloadManager.ERROR_CANNOT_RESUME:
                            //some possibly transient error occurred but we can't resume the download
                            isNeedDownloadAgain = true;
                            AlertUtil.alert("开始重新下载更新!", mContext);
                            break;
                        case DownloadManager.ERROR_DEVICE_NOT_FOUND:
                            //no external storage device was found. Typically, this is because the SD card is not mounted
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("请检查存储设备是否连接!", mContext);
                            break;
                        case DownloadManager.ERROR_FILE_ALREADY_EXISTS:
                            //the requested destination file already exists (the download manager will not overwrite an existing file)
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("文件已存在,请查看下载列表!", mContext);
                            break;
                        case DownloadManager.ERROR_FILE_ERROR:
                            //a storage issue arises which doesn't fit under any other error code
                            isNeedDownloadAgain = true;
                            break;
                        case DownloadManager.ERROR_HTTP_DATA_ERROR:
                            //an error receiving or processing data occurred at the HTTP level
                            isNeedDownloadAgain = true;
                            break;
                        case DownloadManager.ERROR_INSUFFICIENT_SPACE://sd卡满了
                            //here was insufficient storage space. Typically, this is because the SD card is full
                            isNeedDownloadAgain = true;
                            AlertUtil.alert("请检查存储设备是否还有剩余空间!", mContext);
                            break;
                        case DownloadManager.ERROR_TOO_MANY_REDIRECTS:
                            //there were too many redirects
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("服务器错误,请到官网下载更新。", mContext);
                            break;
                        case DownloadManager.ERROR_UNHANDLED_HTTP_CODE:
                            //an HTTP code was received that download manager can't handle
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("服务器错误,请到官网下载更新。", mContext);
                            break;
                        case DownloadManager.ERROR_UNKNOWN:
                            //he download has completed with an error that doesn't fit under any other error code
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("未知错误,请到官网下载更新。", mContext);
                            break;
                    }

                    break;
                case DownloadManager.STATUS_PAUSED:

                    switch (reason) {
                        case DownloadManager.PAUSED_QUEUED_FOR_WIFI:
                            //the download exceeds a size limit for downloads over the mobile network and the download manager is waiting for a Wi-Fi connection to proceed
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("下载已暂停,请检查网络连接!", mContext);
                            break;
                        case DownloadManager.PAUSED_UNKNOWN:
                            //the download is paused for some other reason
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("下载已暂停,请等待重试或以其他方式下载更新!", mContext);
                            break;
                        case DownloadManager.PAUSED_WAITING_FOR_NETWORK:
                            //the download is waiting for network connectivity to proceed
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("下载已暂停,请检查网络连接!", mContext);
                            break;
                        case DownloadManager.PAUSED_WAITING_TO_RETRY:
                            //the download is paused because some network error occurred and the download manager is waiting before retrying the request
                            isNeedDownloadAgain = false;
                            AlertUtil.alert("下载已暂停,正在等待重试!", mContext);
                            break;
                    }

                    break;
                case DownloadManager.STATUS_PENDING:
                    //the download is waiting to start
                    isNeedDownloadAgain = false;
                    AlertUtil.alert("更新正在下载!", mContext);
                    break;
                case DownloadManager.STATUS_RUNNING:
                    //the download is currently running
                    isNeedDownloadAgain = false;
                    AlertUtil.alert("更新正在下载!", mContext);
                    break;
                case DownloadManager.STATUS_SUCCESSFUL:
                    //the download has successfully completed
                    //isNeedDownloadAgain = false;
                    if (TextUtils.isEmpty(GlobalContext.mFolderName)) {
                        GlobalContext.mFolderName = "<span style="font-family: Arial, Helvetica, sans-serif;">XXXX</span>";
                    }
                    String fileName = Environment.getExternalStoragePublicDirectory(GlobalContext.mFolderName).getAbsolutePath() + File.separator + "XXXX.apk";
                    File apkfile = new File(fileName);
                    if (!apkfile.exists()) {
                        //apk文件不存在,则重新下载
                        isNeedDownloadAgain = true;
                    } else {
                        isNeedDownloadAgain = false;
                        installApk(id, downloadManager, mContext);
                    }
                    break;
            }

        }
        return isNeedDownloadAgain;
    }

9、还有一个问题就是如果下载链接出错时的容错,此问题根据自己情况做不同处理吧,就不再贴代码了


NOTE:遇到的问题(一定注意)

1、注册监听器监听下载完成操作时,一定要使用context的registerReceiver而不要用LocalBroadcastManager的registerReceiver,因为此处是调用的系统的下载功能,如果注册的是本地监听器肯定是接受不到“完成信号”的

2、测试安装程序(installApk)时,一定要打包之后测试,否则会报“已经安装不同签名的同包名的应用”





你可能感兴趣的:(android,apk,应用,自动检测更新,代码安装apk)