zoukankan      html  css  js  c++  java
  • Android应用更新升级实现

    介绍

    在产品的开发中,android升级提示,下载更新是必备的功能,否则等用户被动去官方网,或者第三方商店提示,就为时已晚了。

    原理

    在用户每次打开应用的时候,都与服务器进行一次交互,获取版本信息,对比之后,如果版本号大于当前版本号,那么就提示用户升级,否则就当什么都没发生。

    直接看代码。

    实现

     权限

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

    使用起来非常简单

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            AppVersion av = new AppVersion();
            av.setApkName("AppUpdate.apk");
            av.setSha1("FCDA0D0E1E7D620A75DA02A131E2FFEDC1742AC8");
            av.setAppName("博客园");
            av.setUrl("http://down.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk");
            av.setContent("1、测试升级;2、测试升级2!!;3、一大波功能!");
            av.setVerCode(2);
            av.setVersionName("1.1");
            AppUpdateUtils.init(MainActivity.this, av, true,false);
            AppUpdateUtils.upDate();
        }

    自定义消息提示布局,可以弄成自己喜欢的样子。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="3dp" >
    
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_margin="3dp"
            android:src="@drawable/ic_launcher" />
    
        <TextView
            android:id="@+id/fileName"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_alignBottom="@id/imageView"
            android:layout_toRightOf="@id/imageView"
            android:gravity="center_vertical"
            android:textColor="@android:color/white" />
    
        <TextView
            android:id="@+id/rate"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignRight="@id/imageView"
            android:layout_below="@id/imageView"
            android:gravity="center"
            android:text="0%"
            android:textColor="@android:color/white" />
    
        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@id/fileName"
            android:layout_below="@id/fileName"
            android:max="100"
            android:progress="0" />
    
    </RelativeLayout>

    核心代码

    /**
     * @author Leestar54 
     * http://www.cnblogs.com/leestar54
     */
    
    package com.example.appupdate;
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.math.BigInteger;
    import java.net.URL;
    import java.net.URLConnection;
    import java.security.MessageDigest;
    
    import android.app.AlertDialog;
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.ProgressDialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.DialogInterface.OnCancelListener;
    import android.content.Intent;
    import android.content.pm.PackageManager.NameNotFoundException;
    import android.net.Uri;
    import android.os.AsyncTask;
    import android.os.Environment;
    import android.util.Log;
    import android.widget.RemoteViews;
    
    public class AppUpdateUtils {
        private static Context mContext;
        private static int mVersionCode;
        private static AppVersion mAppVersion;
        private static String mVersionName;
        private static String mFileName;
        private static ProgressDialog mProgressDialog;
        private static String mLocalFilepath;
        private static DownloadFileAsyncTask mDownloadFileAsyncTask;
        private static boolean hasCancel = false;
        public static final String TAG = "AppUpDate";
        // 如果是自动更新,那么就不跳出已经是最新版本的对话框。否则用户点击更新应用按钮,弹出已经是最新版本的提示框
        private static boolean mIsAuto = false;
        private static boolean mShowProgressDialog = false;
        private static final int NOTIFY_ID = 54;
        private static NotificationManager mNotificationManager;
        private static Notification mNotification;
    
        /**
         * 初始化
         * 
         * @param context
         *            执行上下文
         * @param newAv
         *            对比版本
         * @param isauto
         *            指示是否是自动升级,当版本号没变时,true无响应,false弹出已是最新版的对话框。
         * @param showProgressDialog
         *            true弹出下载进度对话框,false为通知消息提示进度
         */
        public static void init(Context context, AppVersion newAv, boolean isauto,
                boolean showProgressDialog) {
            mIsAuto = isauto;
            mShowProgressDialog = showProgressDialog;
            mContext = context;
            mNotificationManager = (NotificationManager) context
                    .getSystemService(android.content.Context.NOTIFICATION_SERVICE);
            mVersionCode = getVerCode(mContext);
            mVersionName = getVerName(mContext);
            mAppVersion = newAv;
            mFileName = mAppVersion.getApkName();
            File sdDir = null;
    
            // 判断sd卡是否存在
            boolean sdCardExist = Environment.getExternalStorageState().equals(
                    android.os.Environment.MEDIA_MOUNTED);
            if (sdCardExist) {
                // 获取根目录
                sdDir = Environment.getExternalStorageDirectory();
            }
    
            // 注意FileOutputStream不会自动创建路径,所以初始化的时候要主动创建路径。
            String dirpath;
            if (sdDir != null) {
                // AppName为你想保存的路径,一般为应用目录
                dirpath = sdDir.toString() + "/AppName/";
            } else {
                dirpath = "/AppName/";
            }
            File dir = new File(dirpath);
            if (!dir.exists()) {
                dir.mkdir();// 如果路径不存在就先创建路径
            }
            mLocalFilepath = dirpath + mFileName;
        }
    
        /**
         * 获取版本号
         * 
         * @param context
         * @return
         */
        private static int getVerCode(Context context) {
            int verCode = -1;
            try {
                verCode = context.getPackageManager().getPackageInfo(
                        context.getPackageName(), 0).versionCode;
            } catch (NameNotFoundException e) {
                Log.e(TAG, e.getMessage());
            }
            return verCode;
        }
    
        /**
         * 获取版本名称
         * 
         * @param context
         * @return
         */
        private static String getVerName(Context context) {
            String verName = "";
            try {
                verName = context.getPackageManager().getPackageInfo(
                        context.getPackageName(), 0).versionName;
            } catch (NameNotFoundException e) {
                Log.e(TAG, e.getMessage());
            }
            return verName;
        }
    
        /**
         * 更新应用
         */
        public static void upDate() {
            if (mVersionCode < mAppVersion.getVerCode()) {
                doNewVersionUpdate();
            } else {
                notNewVersionShow();
            }
        }
    
        /**
         * 不执行更新
         */
        private static void notNewVersionShow() {
            // 如果不是自动升级
            if (!mIsAuto) {
                StringBuffer sb = new StringBuffer();
                sb.append("当前版本:");
                sb.append(mVersionName);
                sb.append(",
    已是最新版,无需更新。");
                // 这里的提示框是我自定义的
                AlertDialog ad = new AlertDialog.Builder(mContext)
                        .setTitle("提示")
                        .setMessage(sb.toString())
                        .setPositiveButton("确认",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,
                                            int which) {
                                        // 取消
                                        dialog.dismiss();
                                    }
                                }).create();
                ad.show();
            }
        }
    
        /**
         * 更新应用提示
         */
        private static void doNewVersionUpdate() {
            StringBuffer sb = new StringBuffer();
            sb.append("当前版本:");
            sb.append(mVersionName);
            sb.append(", 发现新版本:");
            sb.append(mAppVersion.getVersionName()).append("
    ");
            sb.append("更新内容:
    ");
            // 这里我们使用;作为分隔符,显示多条跟新内容信息。
            if (mAppVersion.getContent().contains(";")) {
                String[] up = mAppVersion.getContent().split(";");
                for (String s : up) {
                    sb.append(s).append("
    ");
                }
            } else {
                sb.append(mAppVersion.getContent()).append("
    ");
            }
            sb.append("是否更新?");
            AlertDialog ad = new AlertDialog.Builder(mContext)
                    .setTitle("提示")
                    .setMessage(sb.toString())
                    .setNegativeButton("取消", null)
                    .setPositiveButton("确认", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
    
                            updateFile(mAppVersion.getUrl());
                            dialog.dismiss();
                        }
                    })
                    .setNegativeButton("暂不更新",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog,
                                        int whichButton) {
                                    // 点击"取消"按钮之后退出程序
                                    dialog.dismiss();
                                }
                            }).create();// 创建
            // 显示对话框
            ad.show();
        }
    
        // 执行应用更新
        private static void updateFile(final String path) {
            // pBar.show();
    
            File file = new File(mLocalFilepath);
    
            // 根据服务器传回的更新包sha1进行比对,判断是否下载完成,如果不匹配,则重新下载,否则直接打开。
            if (mAppVersion.getSha1() != null) {
                if (getFileSHA1(file).toUpperCase().equals(mAppVersion.getSha1())) {
                    openFile(file);
                } else {
                    try {
                        // downloadFile(path, localFilepath);
                        mDownloadFileAsyncTask = new DownloadFileAsyncTask();
                        mDownloadFileAsyncTask.execute(path);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } else {
                try {
                    mDownloadFileAsyncTask = new DownloadFileAsyncTask();
                    mDownloadFileAsyncTask.execute(path);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 取消更新
         */
        public void cancelUpDate() {
            if (mDownloadFileAsyncTask != null) {
                if (!mDownloadFileAsyncTask.isCancelled()) {
                    mDownloadFileAsyncTask.cancel(true);
                }
            }
        }
    
        /**
         * 应用下载Task
         * 
         */
        private static class DownloadFileAsyncTask extends
                AsyncTask<String, Integer, String> {
    
            // 后台下载任务
            @Override
            protected String doInBackground(String... params) {
                try {
                    URL url = new URL(params[0]);
                    URLConnection connection;
                    connection = url.openConnection();
                    // 2.2以上默认用“gzip”,这里要取消使用,否则大小永远为-1.
                    connection.setRequestProperty("Accept-Encoding", "identity");
                    connection.connect();
                    int length = connection.getContentLength();
                    InputStream input = new BufferedInputStream(
                            connection.getInputStream());
                    FileOutputStream output = new FileOutputStream(mLocalFilepath);
                    byte data[] = new byte[1024];
                    long total = 0;
                    int count;
                    while ((count = input.read(data)) != -1) {
                        if (!hasCancel) {
                            total += count;
                            publishProgress((int) (total * 100 / length));
                            output.write(data, 0, count);
                        } else {
                            cancel(true);
                            break;
                        }
                    }
                    output.flush();
                    output.close();
                    input.close();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return null;
            }
    
            // 进度更新
            private int tempv;
    
            @Override
            protected void onProgressUpdate(Integer... values) {
                // TODO Auto-generated method stub
                super.onProgressUpdate(values);
                // 如果需要用到进度提示框,则执行
                if (mShowProgressDialog) {
                    if (values[0] != tempv) {
                        mProgressDialog.setProgress(values[0]);
                    }
                } else {
                    // 防止多次重复提示,影响性能
                    if (values[0] != tempv) {
                        RemoteViews contentView = mNotification.contentView;
                        contentView.setTextViewText(R.id.rate,
                                String.valueOf(values[0]) + "%");
                        contentView.setProgressBar(R.id.progress, 100, values[0],
                                false);
                        mNotificationManager.notify(NOTIFY_ID, mNotification);
                        tempv = values[0];
                    }
                }
            }
    
            // 下载完成后执行
            @Override
            protected void onPostExecute(String result) {
                // TODO Auto-generated method stub
                super.onPostExecute(result);
                if (mShowProgressDialog) {
                    openFile(new File(mLocalFilepath));
                } else {
                    Intent intent = new Intent();
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.setAction(android.content.Intent.ACTION_VIEW);
                    // 设定intent的file与MimeType
                    intent.setDataAndType(Uri.fromFile(new File(mLocalFilepath)),
                            "application/vnd.android.package-archive");
    
                    // 下载完毕后变换通知形式
                    mNotification.flags = Notification.FLAG_AUTO_CANCEL;
                    mNotification.contentView = null;
                    // Intent intent = new Intent(mContext, FileMgrActivity.class);
                    // // 告知已完成
                    // intent.putExtra("completed", "yes");
                    // //更新参数,注意flags要使用FLAG_UPDATE_CURRENT
                    PendingIntent contentIntent = PendingIntent.getActivity(
                            mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                    mNotification.setLatestEventInfo(mContext, "下载完成",
                            "文件已下载完毕,点击进行安装。", contentIntent);
                    mNotificationManager.notify(NOTIFY_ID, mNotification);
                }
            }
    
            @Override
            protected void onPreExecute() {
                // TODO Auto-generated method stub
                super.onPreExecute();
                hasCancel = false;
                if (mShowProgressDialog) {
                    showProgressDialog();
                } else {
                    tempv = 0;
                    int icon = R.drawable.ic_launcher;
                    CharSequence tickerText = "开始下载";
                    long when = System.currentTimeMillis();
                    mNotification = new Notification(icon, tickerText, when);
    
                    // 在通知栏上点击此通知后自动清除此通知
                    mNotification.flags = Notification.FLAG_AUTO_CANCEL;
    
                    RemoteViews contentView = new RemoteViews(
                            mContext.getPackageName(), R.layout.notification_update);
                    contentView.setTextViewText(R.id.fileName, "更新文件下载中……");
                    // 指定个性化视图
                    mNotification.contentView = contentView;
                    Intent intent = new Intent();
                    PendingIntent contentIntent = PendingIntent.getActivity(
                            mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                    // 指定内容意图
                    mNotification.contentIntent = contentIntent;
                    mNotificationManager.notify(NOTIFY_ID, mNotification);
                }
            }
        }
    
        // 在手机上打开文件
        private static void openFile(File f) {
            // mProgressDialog.dismiss();
            Intent intent = new Intent();
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setAction(android.content.Intent.ACTION_VIEW);
            // 设定intent的file与MimeType,安装文件
            intent.setDataAndType(Uri.fromFile(f),
                    "application/vnd.android.package-archive");
            mContext.startActivity(intent);
        }
    
        // 使用自带算法 获取文件的SHA1
        private static String getFileSHA1(File file) {
            if (file.exists()) {
                MessageDigest digest = null;
                byte buffer[] = new byte[1024];
                int len;
                try {
                    digest = MessageDigest.getInstance("SHA-1");// ("SHA-1");
                    FileInputStream in = new FileInputStream(file);
                    while ((len = in.read(buffer, 0, 1024)) != -1) {
                        digest.update(buffer, 0, len);
                    }
                    in.close();
                } catch (Exception e) {
                    e.printStackTrace();
                    return "";
                }
    
                // 直接用这玩意转换成16进制,碉堡了、
                BigInteger bigInt = new BigInteger(1, digest.digest());
                return bigInt.toString(16);
    
            } else {
                return "";
            }
        }
    
        /**
         * 显示下载进度对话框,可以取消下载任务。
         */
        private static void showProgressDialog() {
            mProgressDialog = new ProgressDialog(mContext);
            mProgressDialog.setOnCancelListener(new OnCancelListener() {
    
                @Override
                public void onCancel(DialogInterface dialog) {
                    if (mDownloadFileAsyncTask != null) {
                        if (!mDownloadFileAsyncTask.isCancelled()) {
                            mDownloadFileAsyncTask.cancel(true);
                            hasCancel = true;
                        }
                    }
                }
            });
            mProgressDialog.setTitle("正在下载");
            mProgressDialog.setMax(100);
            mProgressDialog.setIndeterminate(false);
            mProgressDialog.setMessage("请稍候...");
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.show();
        }
    }

    看起来是这样的

    demo下载地址:

    链接:http://pan.baidu.com/s/1eQs2vCm 密码:3gkc

  • 相关阅读:
    测试一下你的T-SQL基础知识-count
    测试一下你的T-SQL基础知识-subquery
    Microsoft SQL Server 2012 管理 (2): Auditing
    Webpack
    react
    Webpack 傻瓜式指南(一)
    谈谈react-router学习
    使用Flexible 实现手淘H5 页面的终端适配学习
    Promise 让异步更优
    基于LeanCloud云引擎的Web全栈方案
  • 原文地址:https://www.cnblogs.com/leestar54/p/4229876.html
Copyright © 2011-2022 走看看