Android应用更新升级实现

时间:2023-03-08 17:54:52

介绍

在产品的开发中,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(",\n已是最新版,无需更新。");
// 这里的提示框是我自定义的
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("\n");
sb.append("更新内容:\n");
// 这里我们使用;作为分隔符,显示多条跟新内容信息。
if (mAppVersion.getContent().contains(";")) {
String[] up = mAppVersion.getContent().split(";");
for (String s : up) {
sb.append(s).append("\n");
}
} else {
sb.append(mAppVersion.getContent()).append("\n");
}
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();
}
}

看起来是这样的

Android应用更新升级实现Android应用更新升级实现Android应用更新升级实现Android应用更新升级实现

demo下载地址:

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