Android软件的自动更新【升级改革版】
?
1、之前写过一篇说版本升级的,用到广播。感觉乱用,搞的有点复杂,且混乱。现在又用到了版本升级功能,然后梳理下思路,使用回调接口重新写了个。
2、需求同http://aokunsang.iteye.com/blog/1487429,部分源码已上传。
3、增加了点小功能:
? ?1>、可以手动检查升级;
? ?2>、显示升级日志;
? ? 3>、修改上篇博客潜在问题:
? ? ? ? ? ? ? 问题:后台查询到更新,提示更新的AlertDialog只能在启动更新的页面弹出;如果离开此页面,抛异常。
? ? ? ? ? ? ? 解决:在app的所有页面顶层弹出,参考代码:
?
//设置dialogdialog.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);}@Overridepublic boolean onCancel() {return interceptFlag;}@Overridepublic void onChangeProgress(int progress) {progressView.setProgress(progress); //设置下载进度textView.setText("进度:"+progress+"%");}@Overridepublic void onCompleted(boolean success, String errorMsg) {if(downloadDialog!=null){downloadDialog.dismiss();}if(success){ //更新成功alearyUpdateSuccess();installApk();}else{Toast.makeText(mContext, errorMsg, Toast.LENGTH_SHORT).show();}}@Overridepublic 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(){@Overridepublic 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;}@Overrideprotected void onPreExecute() {downCallBack.onDownloadPreare();super.onPreExecute();}@Overrideprotected 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;}@Overrideprotected void onProgressUpdate(Integer... values) {downCallBack.onChangeProgress(values[0]);super.onProgressUpdate(values);}@Overrideprotected 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);}@Overrideprotected 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() {@Overridepublic 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(){@Overridepublic 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(){@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}});noticeDialog = builder.create();noticeDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); //设置最顶层AlertdialognoticeDialog.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();?