1、之前写过一篇说版本升级的,用到广播。感觉乱用,搞的有点复杂,且混乱。现在又用到了版本升级功能,然后梳理下思路,使用回调接口重新写了个。
2、需求同http://aokunsang.iteye.com/blog/1487429,部分源码已上传。
3、增加了点小功能:
1>、可以手动检查升级;
2>、显示升级日志;
3>、修改上篇博客潜在问题:
问题:后台查询到更新,提示更新的AlertDialog只能在启动更新的页面弹出;如果离开此页面,抛异常。
解决:在app的所有页面顶层弹出,参考代码:
//设置dialog
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
//加入权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
4、截图如下:
5、上代码(代码才能说明一切):
(1)、DownloadCallback.java
/**
* 下载数据接口
* @author: aokunsang
* @date: 2012-12-17
*/
public interface DownloadCallback {
/**
* 下载前准备
*/
public void onDownloadPreare();
/**
* 下载进度更新
* @param progress 进度值
*/
public void onChangeProgress(int progress);
/**
* 下载完成
* @param success 下载成功标示
* @param errorMsg 下载失败显示内容
*/
public void onCompleted(boolean success,String errorMsg);
/**
* 取消下载
*/
public boolean onCancel();
}
(2)、DownloadInstall.java
/**
* @author: aokunsang
* @date: 2012-12-18
*/
public class DownloadInstall implements DownloadCallback {
private Context mContext;
private String apkPath,apkVersion;
private int apkCode;
private LayoutInflater inflater;
private TextView textView;
private ProgressBar progressView;
private AlertDialog downloadDialog; //下载弹出框
private boolean interceptFlag = false; //是否取消下载
public DownloadInstall(Context mContext,String apkPath,String apkVersion,int apkCode) {
this.mContext = mContext;
this.apkCode = apkCode;
this.apkPath = apkPath;
this.apkVersion = apkVersion;
inflater = LayoutInflater.from(mContext);
}
@Override
public boolean onCancel() {
return interceptFlag;
}
@Override
public void onChangeProgress(int progress) {
progressView.setProgress(progress); //设置下载进度
textView.setText("进度:"+progress+"%");
}
@Override
public void onCompleted(boolean success, String errorMsg) {
if(downloadDialog!=null){
downloadDialog.dismiss();
}
if(success){ //更新成功
alearyUpdateSuccess();
installApk();
}else{
Toast.makeText(mContext, errorMsg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDownloadPreare() {
if(IntentUtil.checkSoftStage(mContext)){
File file = new File(Const.apkSavepath);
if(!file.exists()){
file.mkdir();
}
Builder builder = new AlertDialog.Builder(mContext);
builder.setIcon(R.drawable.upgrade).setTitle("正在更新版本");
//---------------------------- 设置在对话框中显示进度条 --------------------
View view = inflater.inflate(R.layout.upgrade_apk, null);
textView = (TextView)view.findViewById(R.id.progressCount_text);
textView.setText("进度:0");
progressView = (ProgressBar)view.findViewById(R.id.progressbar);
builder.setView(view);
builder.setNegativeButton("取消", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
interceptFlag = true;
}
});
downloadDialog = builder.create();
downloadDialog.show();
}
}
/**
* 升级成功,更新升级日期和版本号,和版本code
*/
private void alearyUpdateSuccess(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
SharedPreferences sharedPreference = mContext.getSharedPreferences(UpdateShared.SETTING_UPDATE_APK_INFO, 0);
sharedPreference.edit().putString(UpdateShared.UPDATE_DATE, sdf.format(new Date()))
.putString(UpdateShared.APK_VERSION, apkVersion).putInt(UpdateShared.APK_VERCODE, apkCode).commit();
}
/**
* 安装apk
*/
private void installApk(){
File file = new File(apkPath);
if(!file.exists()){
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
mContext.startActivity(intent);
}
}
(3)、DownloadAsyncTask.java
/**
* 异步下载数据
* @author: aokunsang
* @date: 2012-12-17
*/
public class DownloadAsyncTask extends AsyncTask<String, Integer, String> {
private DownloadCallback downCallBack;
private HttpURLConnection urlConn;
public DownloadAsyncTask(DownloadCallback downloadCallback){
this.downCallBack = downloadCallback;
}
@Override
protected void onPreExecute() {
downCallBack.onDownloadPreare();
super.onPreExecute();
}
@Override
protected String doInBackground(String... args) {
String apkDownloadUrl = args[0]; //apk下载地址
String apkPath = args[1]; //apk在sd卡中的安装位置
String result = "";
if(!IntentUtil.checkURL(apkDownloadUrl)){
result = "netfail";
}else{
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL(apkDownloadUrl);
urlConn = (HttpURLConnection)url.openConnection();
is = urlConn.getInputStream();
int length = urlConn.getContentLength(); //文件大小
fos = new FileOutputStream(apkPath);
int count = 0,numread = 0;
byte buf[] = new byte[1024];
while(!downCallBack.onCancel()&& (numread = is.read(buf))!=-1){
count+=numread;
int progressCount =(int)(((float)count / length) * 100);
publishProgress(progressCount);
fos.write(buf, 0, numread);
}
fos.flush();
result = "success";
} catch (Exception e) {
e.printStackTrace();
result = "fail";
}finally{
try {
if(fos!=null)
fos.close();
if(is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
result = "fail";
}
}
}
return result;
}
@Override
protected void onProgressUpdate(Integer... values) {
downCallBack.onChangeProgress(values[0]);
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String result) {
if(downCallBack.onCancel()){
downCallBack.onCompleted(false, "版本更新下载已取消。");
}else if("success".equals(result)){
downCallBack.onCompleted(true, null);
}else if("netfail".equals(result)){
downCallBack.onCompleted(false, "连接服务器失败,请稍后重试。");
}else{
downCallBack.onCompleted(false, "版本更新失败,请稍后重试。");
}
super.onPostExecute(result);
}
@Override
protected void onCancelled() {
if(urlConn!=null){
urlConn.disconnect();
}
super.onCancelled();
}
}
(4)、DownloadManager.java
/**
* 下载管理
* @author: aokunsang
* @date: 2012-12-18
*/
public class DownloadManager{
private Context mContext;
final static int CHECK_FAIL = 0;
final static int CHECK_SUCCESS = 1;
final static int CHECK_NOUPGRADE = 2;
final static int CHECK_NETFAIL = 3;
private ApkInfo apkinfo;
private AlertDialog noticeDialog; //提示弹出框
private ProgressDialog progressDialog;
private boolean isAccord; //是否主动检查软件升级
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public DownloadManager(Context mContext,boolean isAccord){
this.mContext = mContext;
this.isAccord = isAccord;
}
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
if(progressDialog!=null){
progressDialog.dismiss();
}
switch(msg.what){
case CHECK_SUCCESS:{
showNoticeDialog();
break;
}
case CHECK_NOUPGRADE:{ //不需要更新
if(isAccord) Toast.makeText(mContext, "当前版本是最新版。", Toast.LENGTH_SHORT).show();
break;
}
case CHECK_NETFAIL:{
if(isAccord) Toast.makeText(mContext, "网络连接不正常。", Toast.LENGTH_SHORT).show();
break;
}
case CHECK_FAIL:{
if(isAccord) Toast.makeText(mContext, "从服务器获取更新数据失败。", Toast.LENGTH_SHORT).show();
break;
}
}
};
};
/* 检查下载更新 [apk下载入口] */
public void checkDownload(){
if(isAccord) progressDialog = ProgressDialog.show(mContext, "", "请稍后,正在检查更新...");
new Thread() {
@Override
public void run() {
if(!IntentUtil.isConnect(mContext)){ //检查网络连接是否正常
handler.sendEmptyMessage(CHECK_NETFAIL);
}else if(checkTodayUpdate() || isAccord){//判断今天是否已自动检查过更新 ;如果手动检查更新,直接进入
String result = HttpRequestUtil.getSourceResult(Const.apkCheckUpdateUrl, null, mContext);
try {
//从服务器下载数据有中文,所以服务器对数据进行了编码;在这里需要解码
result = Escape.unescape(result);
JSONObject obj = new JSONObject(result);
String apkVersion = obj.getString("apkVersion");
int apkCode = obj.getInt("apkVerCode");
String apkSize = obj.getString("apkSize");
String apkName = obj.getString("apkName");
String downloadUrl = obj.getString("apkDownloadUrl");
String apkLog = obj.getString("apklog");
apkinfo = new ApkInfo(downloadUrl, apkVersion, apkSize, apkCode, apkName, apkLog);
if(apkinfo!=null && checkApkVercode()){ //检查版本号
alreayCheckTodayUpdate(); //设置今天已经检查过更新
handler.sendEmptyMessage(CHECK_SUCCESS);
}else{
handler.sendEmptyMessage(CHECK_NOUPGRADE);
}
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(CHECK_FAIL);
}
}
}
}.start();
}
/* 弹出软件更新提示对话框*/
private void showNoticeDialog(){
StringBuffer sb = new StringBuffer();
sb.append("版本号:"+apkinfo.getApkVersion()+"\n")
.append("文件大小:"+apkinfo.getApkSize()+"\n")
.append("更新日志:\n"+apkinfo.getApkLog());
Builder builder = new AlertDialog.Builder(mContext);
builder.setIcon(R.drawable.upgrade).setTitle("版本更新").setMessage(sb.toString());
builder.setPositiveButton("下载", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
String apkPath = Const.apkSavepath + apkinfo.getApkName();
DownloadCallback downCallback = new DownloadInstall(mContext, apkPath, apkinfo.getApkVersion(), apkinfo.getApkCode());
DownloadAsyncTask request = new DownloadAsyncTask(downCallback);
request.execute(apkinfo.getDownloadUrl(),apkPath);
dialog.dismiss();
}
});
builder.setNegativeButton("以后再说", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
noticeDialog = builder.create();
noticeDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); //设置最顶层Alertdialog
noticeDialog.show();
}
/**
* 根据日期检查是否需要进行软件升级
* @throws Exception
*/
private boolean checkTodayUpdate() {
SharedPreferences sharedPreference = mContext.getSharedPreferences(UpdateShared.SETTING_UPDATE_APK_INFO, 0);
String checkDate = sharedPreference.getString(UpdateShared.CHECK_DATE, "");
String updateDate = sharedPreference.getString(UpdateShared.UPDATE_DATE, "");
if("".equals(checkDate) && "".equals(updateDate)){ //刚安装的新版本,设置详细信息
int verCode = IntentUtil.getCurrentVersionCode(mContext);
String versionName = IntentUtil.getCurrentVersionName(mContext);
String dateStr = sdf.format(new Date());
sharedPreference.edit().putString(UpdateShared.CHECK_DATE, dateStr)
.putString(UpdateShared.UPDATE_DATE, dateStr)
.putString(UpdateShared.APK_VERSION, versionName)
.putInt(UpdateShared.APK_VERCODE, verCode).commit();
return true;
}
try {
//判断defaultMinUpdateDay天内不检查升级
if((new Date().getTime()-sdf.parse(updateDate).getTime())/1000/3600/24<Const.defaultMinUpdateDay){
return false;
}else if(checkDate.equalsIgnoreCase(sdf.format(new Date()))){//判断今天是否检查过升级
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 设置今天已经检查过升级
* @return
*/
private void alreayCheckTodayUpdate(){
String date = sdf.format(new Date());
SharedPreferences sharedPreference = mContext.getSharedPreferences(UpdateShared.SETTING_UPDATE_APK_INFO, 0);
sharedPreference.edit().putString(UpdateShared.CHECK_DATE, date).commit();
}
/**
* 检查版本是否需要更新
* @return
*/
private boolean checkApkVercode(){
SharedPreferences sharedPreference = mContext.getSharedPreferences(UpdateShared.SETTING_UPDATE_APK_INFO, 0);
int verCode = sharedPreference.getInt(UpdateShared.APK_VERCODE, 0);
if(apkinfo.getApkCode()>verCode){
return true;
}else{
return false;
}
}
static interface UpdateShared{
String SETTING_UPDATE_APK_INFO = "cbt_upgrade_setting";
String UPDATE_DATE = "updatedate";
String APK_VERSION = "apkversion";
String APK_VERCODE = "apkvercode";
String CHECK_DATE = "checkdate";
}
}
代码内容我不讲解,使用的是回调接口。里面的各种检查是否升级,参考上一篇软件更新博客http://aokunsang.iteye.com/blog/1487429。
(5)、其他操作类:
a、Const.java是一个常量存储类,保存检查更新地址等;
b、Escape.java是个URL解码编码类;
c、HttpRequestUtil.java获取远程数据类;
d、IntentUtil.java工具类,如:检查网络连接状态等。
(以上类参考附件源码)
/**
* apk更新信息
* @author: aokunsang
* @date: 2012-12-18
*/
public class ApkInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String downloadUrl; //下载地址
private String apkVersion; //apk版本
private String apkSize; //apk文件大小
private int apkCode; //apk版本号(更新必备)
private String apkName; //apk名字
private String apkLog; //apk更新日志
setter and getter...
}
(6)、服务器代码(这里的apklog可以是个txt文档,在UI上下载展示,我做的比较简单):
/**
* 获取apk更新信息
* {apkVersion:'1.10',apkSize:'36K',apkVerCode:2,apkName:'1.1.apk',apkDownloadUrl:'http://localhost:8080/myapp/1.1.apk',apklog:'1、修改页面;\n2、修改字体'}
*/
@Action(value="checkUpdateApk")
public String updateApk(){
ResourceLoader loader = new DefaultResourceLoader();
Properties pp = null;
try {
InputStream is = loader.getResource("classpath:config/sysconf.properties").getInputStream();
pp = new Properties();
pp.load(new InputStreamReader(is, "utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
if(pp!=null){
String apkVersion = pp.getProperty("apkVersion");
String apkDownloadUrl = pp.getProperty("apkDownloadUrl");
String apkName = pp.getProperty("apkName");
int apkVerCode = NumberUtils.toInt(pp.getProperty("apkVerCode"),0);
String apklog = pp.getProperty("apkLog");
String apkSize = pp.getProperty("apkSize");
Httptear.ResponseResultByEscape("{apkVersion:'"+apkVersion+"',apkSize:'"+apkSize+"',apkVerCode:"+apkVerCode+",apkName:'"+apkName+"',apkDownloadUrl:'"+apkDownloadUrl+"',apklog:'"+apklog+"'}");
}else{
Httptear.ResponseResultByEscape("{}");
}
return NONE;
}
6、upgrade_apk.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/progressCount_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:textSize="14dip" /> <ProgressBar android:id="@+id/progressbar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
7、需要加入以下权限:
<!-- 在SD卡中创建和删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 向SD卡中写入东西权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
8、启动更新检查代码:
/* 升级程序[主动] (因为会弹出一个ProgressDialog窗口,不能使用getApplicationContext())*/
DownloadManager downManger = new DownloadManager(this,true);
downManger.checkDownload();
/* 升级程序启动[被动](使用this引用会导致:如在1页面启动升级,当前页面为2页面,此时弹出Dialog抛异常)*/
DownloadManager downManger = new DownloadManager(getApplicationContext(),false);
downManger.checkDownload();