介绍

在产品的开发中,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();
}
}

看起来是这样的

demo下载地址:

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

Android应用更新升级实现的更多相关文章

  1. android开发 更新升级安装到一半自动闪退

    如题:android开发 更新升级安装到一半自动闪退,,,解决办法,如下(红色为我新增的代码) /**     * 安装APK文件     */    private void installApk( ...

  2. Android Studio更新升级方法

    自从2013 Google I/O大会之后,笔者就将android ide开发工具从eclipse迁移到Android Studio了,android studio一直在更新完善,为了与时俱进,我们当 ...

  3. Android Studio更新升级方法(转)

    自从2013 Google I/O大会之后,笔者就将android ide开发工具从eclipse迁移到Android Studio了,android studio一直在更新完善,为了与时俱进,我们当 ...

  4. Android应用程序的自动更新升级(自身升级、通过tomcat)(转)

    Android应用程序的自动更新升级(自身升级.通过tomcat) http://blog.csdn.net/mu0206mu/article/details/7204746 刚入手android一个 ...

  5. Android 增量更新(BSDiff / bspatch)

    Android 增量更新 BSDiff / bspatchhttp://www.daemonology.net/bsdiff/android的代码目录下 \external\bsdiff bsdiff ...

  6. Android热更新开源项目Tinker集成实践总结

    前言 最近项目集成了Tinker,开始认为集成会比较简单,但是在实际操作的过程中还是遇到了一些问题,本文就会介绍在集成过程大家基本会遇到的主要问题. 考虑一:后台的选取 目前后台功能可以通过三种方式实 ...

  7. Android数据库无缝升级方案

    软件迭代过程中,业务不断更新,也要求软件持续更新.相应地,数据库更新升级也是不可避免的一个环节.Android作为客户端应用,数据库升级相对于服务端来说会麻烦一些.常见的升级方式有: 1.删除旧表和数 ...

  8. 浅析android应用增量升级(转)

    By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处 很久没有更新博客了,真是堕落啊,几次想提起笔,却总是被各种琐事耽搁,以后会多写文章记录点滴. 背景         ...

  9. Android 增量更新研究

    Android 增量更新实例(Smart App Updates) http://blog.csdn.net/duguang77/article/details/17676797 Android AP ...

随机推荐

  1. .net深入体验与实战精要--ASP.NET开发大杂烩(转)

    转自:http://www.cnblogs.com/sunhan/p/3371337.html 正巧今天遇到一个获取动态生成table中的一个动态生成的TextBox的值的时候总是findcontro ...

  2. BZOJ1037: [ZJOI2008]生日聚会Party

    DP… /************************************************************** Problem: 1037 User: zhuohan123 L ...

  3. javascript和swf在网页中交互的一些总结

    Javascript和swf在网页中交互一般可有以下几种情况: 1.swf和这些调用的javascript在同域 2.swf和这些调用的javascript在不同域,比如加载远程的swf然后call别 ...

  4. 使用Varnish代替Squid做网站缓存加速器的详细解决方案----转载

    [文章作者:张宴 本文版本:v1.2 最后修改:2008.01.02 转载请注明出处:http://blog.s135.com] 我曾经写过一篇文章──<初步试用Squid的替代产品──Varn ...

  5. jQuery实现表格隔行换色且感应鼠标高亮行变色

    jQuery插件实现表格隔行换色且感应鼠标高亮行变色 http://www.jb51.net/article/41568.htm jquery 操作DOM的基本用法分享http://www.jb51. ...

  6. Visual Studio 2015支持为Linux构建应用

    点这里 微软著名的集成开发环境有可能是首次在其产品页提及了竞争对手Linux.Visual Studio 2015的页面声称,“Build for iOS, Android, Windows devi ...

  7. 使用Visio进行UML建模

    http://www.qdgw.edu.cn/zhuantiweb/jpkc/2009/rjkf/xmwd/Visio_UmlModel.htm#_Toc80417837 内容提纲: 1.VISIO中 ...

  8. ***mysql索引总结----mysql索引类型以及创建

    文章归属:http://feiyan.info/16.html,我想自己去写了,但是发现此君总结的非常详细.直接搬过来了 关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基 ...

  9. HTTP返回码总结

    HTTP协议状态码表示的意思主要分为五类,大体是:  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~  1×× 保留  2×× 表示请求成功地接收  3×× 为完成请求客户需进一步细化请求  ...

  10. SPRING IN ACTION 第4版笔记-第九章Securing web applications-011-把敏感信息请求转为https(requiresChannel())

    1.把包含敏感信息的请求转为https请求,则较为安全,但如何只把有需要安全的请求转为https,而不是不加分辩就把所有请求都转为https呢?可以用requiresChannel() @Overri ...