Android应用自动更新功能的代码实现(转)

由于Android项目开源所致,市面上出现了N多安卓软件市场。为了让我们开发的软件有更多的用户使用,我们需要向N多市场发布,软件升级后,我们也必须到安卓市场上进行更新,给我们增加了工作量。因此我们有必要给我们的Android应用增加自动更新的功能。

既然实现自动更新,我们首先必须让我们的应用知道是否存在新版本的软件,因此我们可以在自己的网站上放置配置文件,存放软件的版本信息:

  1. <update>
  2. <version>2</version>
  3. <name>baidu_xinwen_1.1.0</name>
  4. <url>http://gdown.baidu.com/data/wisegame/f98d235e39e29031/baiduxinwen.apk</url>
  5. </update>

在这里我使用的是XML文件,方便读取。由于XML文件内容比较少,因此可通过DOM方式进行文件的解析:

  1. public class ParseXmlService
  2. {
  3. public HashMap<String, String> parseXml(InputStream inStream) throws Exception
  4. {
  5. HashMap<String, String> hashMap = new HashMap<String, String>();
  6. // 实例化一个文档构建器工厂
  7. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  8. // 通过文档构建器工厂获取一个文档构建器
  9. DocumentBuilder builder = factory.newDocumentBuilder();
  10. // 通过文档通过文档构建器构建一个文档实例
  11. Document document = builder.parse(inStream);
  12. //获取XML文件根节点
  13. Element root = document.getDocumentElement();
  14. //获得所有子节点
  15. NodeList childNodes = root.getChildNodes();
  16. for (int j = 0; j < childNodes.getLength(); j++)
  17. {
  18. //遍历子节点
  19. Node childNode = (Node) childNodes.item(j);
  20. if (childNode.getNodeType() == Node.ELEMENT_NODE)
  21. {
  22. Element childElement = (Element) childNode;
  23. //版本号
  24. if ("version".equals(childElement.getNodeName()))
  25. {
  26. hashMap.put("version",childElement.getFirstChild().getNodeValue());
  27. }
  28. //软件名称
  29. else if (("name".equals(childElement.getNodeName())))
  30. {
  31. hashMap.put("name",childElement.getFirstChild().getNodeValue());
  32. }
  33. //下载地址
  34. else if (("url".equals(childElement.getNodeName())))
  35. {
  36. hashMap.put("url",childElement.getFirstChild().getNodeValue());
  37. }
  38. }
  39. }
  40. return hashMap;
  41. }
  42. }

通过parseXml()方法,我们可以获取服务器上应用的版本、文件名以及下载地址。紧接着我们就需要获取到我们手机上应用的版本信息:

  1. /**
  2. * 获取软件版本号
  3. *
  4. * @param context
  5. * @return
  6. */
  7. private int getVersionCode(Context context)
  8. {
  9. int versionCode = 0;
  10. try
  11. {
  12. // 获取软件版本号,
  13. versionCode = context.getPackageManager().getPackageInfo("com.szy.update", 0).versionCode;
  14. } catch (NameNotFoundException e)
  15. {
  16. e.printStackTrace();
  17. }
  18. return versionCode;
  19. }

通过该方法我们获取到的versionCode对应AndroidManifest.xml下android:versionCode。android:versionCode和android:versionName两个属性分别表示版本号,版本名称。versionCode是整数型,而versionName是字符串。由于versionName是给用户看的,不太容易比较大小,升级检查时,就可以检查versionCode。把获取到的手机上应用版本与服务器端的版本进行比较,应用就可以判断处是否需要更新软件。

处理流程

Android应用自动更新功能的代码实现(转) 
处理代码

  1. package com.szy.update;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.net.HttpURLConnection;
  7. import java.net.MalformedURLException;
  8. import java.net.URL;
  9. import java.util.HashMap;
  10. import android.app.AlertDialog;
  11. import android.app.Dialog;
  12. import android.app.AlertDialog.Builder;
  13. import android.content.Context;
  14. import android.content.DialogInterface;
  15. import android.content.Intent;
  16. import android.content.DialogInterface.OnClickListener;
  17. import android.content.pm.PackageManager.NameNotFoundException;
  18. import android.net.Uri;
  19. import android.os.Environment;
  20. import android.os.Handler;
  21. import android.os.Message;
  22. import android.view.LayoutInflater;
  23. import android.view.View;
  24. import android.widget.ProgressBar;
  25. import android.widget.Toast;
  26. /**
  27. *@author coolszy
  28. *@date 2012-4-26
  29. *@blog http://blog.92coding.com
  30. */
  31. public class UpdateManager
  32. {
  33. /* 下载中 */
  34. private static final int DOWNLOAD = 1;
  35. /* 下载结束 */
  36. private static final int DOWNLOAD_FINISH = 2;
  37. /* 保存解析的XML信息 */
  38. HashMap<String, String> mHashMap;
  39. /* 下载保存路径 */
  40. private String mSavePath;
  41. /* 记录进度条数量 */
  42. private int progress;
  43. /* 是否取消更新 */
  44. private boolean cancelUpdate = false;
  45. private Context mContext;
  46. /* 更新进度条 */
  47. private ProgressBar mProgress;
  48. private Dialog mDownloadDialog;
  49. private Handler mHandler = new Handler()
  50. {
  51. public void handleMessage(Message msg)
  52. {
  53. switch (msg.what)
  54. {
  55. // 正在下载
  56. case DOWNLOAD:
  57. // 设置进度条位置
  58. mProgress.setProgress(progress);
  59. break;
  60. case DOWNLOAD_FINISH:
  61. // 安装文件
  62. installApk();
  63. break;
  64. default:
  65. break;
  66. }
  67. };
  68. };
  69. public UpdateManager(Context context)
  70. {
  71. this.mContext = context;
  72. }
  73. /**
  74. * 检测软件更新
  75. */
  76. public void checkUpdate()
  77. {
  78. if (isUpdate())
  79. {
  80. // 显示提示对话框
  81. showNoticeDialog();
  82. } else
  83. {
  84. Toast.makeText(mContext, R.string.soft_update_no, Toast.LENGTH_LONG).show();
  85. }
  86. }
  87. /**
  88. * 检查软件是否有更新版本
  89. *
  90. * @return
  91. */
  92. private boolean isUpdate()
  93. {
  94. // 获取当前软件版本
  95. int versionCode = getVersionCode(mContext);
  96. // 把version.xml放到网络上,然后获取文件信息
  97. InputStream inStream = ParseXmlService.class.getClassLoader().getResourceAsStream("version.xml");
  98. // 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析
  99. ParseXmlService service = new ParseXmlService();
  100. try
  101. {
  102. mHashMap = service.parseXml(inStream);
  103. } catch (Exception e)
  104. {
  105. e.printStackTrace();
  106. }
  107. if (null != mHashMap)
  108. {
  109. int serviceCode = Integer.valueOf(mHashMap.get("version"));
  110. // 版本判断
  111. if (serviceCode > versionCode)
  112. {
  113. return true;
  114. }
  115. }
  116. return false;
  117. }
  118. /**
  119. * 获取软件版本号
  120. *
  121. * @param context
  122. * @return
  123. */
  124. private int getVersionCode(Context context)
  125. {
  126. int versionCode = 0;
  127. try
  128. {
  129. // 获取软件版本号,对应AndroidManifest.xml下android:versionCode
  130. versionCode = context.getPackageManager().getPackageInfo("com.szy.update", 0).versionCode;
  131. } catch (NameNotFoundException e)
  132. {
  133. e.printStackTrace();
  134. }
  135. return versionCode;
  136. }
  137. /**
  138. * 显示软件更新对话框
  139. */
  140. private void showNoticeDialog()
  141. {
  142. // 构造对话框
  143. AlertDialog.Builder builder = new Builder(mContext);
  144. builder.setTitle(R.string.soft_update_title);
  145. builder.setMessage(R.string.soft_update_info);
  146. // 更新
  147. builder.setPositiveButton(R.string.soft_update_updatebtn, new OnClickListener()
  148. {
  149. @Override
  150. public void onClick(DialogInterface dialog, int which)
  151. {
  152. dialog.dismiss();
  153. // 显示下载对话框
  154. showDownloadDialog();
  155. }
  156. });
  157. // 稍后更新
  158. builder.setNegativeButton(R.string.soft_update_later, new OnClickListener()
  159. {
  160. @Override
  161. public void onClick(DialogInterface dialog, int which)
  162. {
  163. dialog.dismiss();
  164. }
  165. });
  166. Dialog noticeDialog = builder.create();
  167. noticeDialog.show();
  168. }
  169. /**
  170. * 显示软件下载对话框
  171. */
  172. private void showDownloadDialog()
  173. {
  174. // 构造软件下载对话框
  175. AlertDialog.Builder builder = new Builder(mContext);
  176. builder.setTitle(R.string.soft_updating);
  177. // 给下载对话框增加进度条
  178. final LayoutInflater inflater = LayoutInflater.from(mContext);
  179. View v = inflater.inflate(R.layout.softupdate_progress, null);
  180. mProgress = (ProgressBar) v.findViewById(R.id.update_progress);
  181. builder.setView(v);
  182. // 取消更新
  183. builder.setNegativeButton(R.string.soft_update_cancel, new OnClickListener()
  184. {
  185. @Override
  186. public void onClick(DialogInterface dialog, int which)
  187. {
  188. dialog.dismiss();
  189. // 设置取消状态
  190. cancelUpdate = true;
  191. }
  192. });
  193. mDownloadDialog = builder.create();
  194. mDownloadDialog.show();
  195. // 现在文件
  196. downloadApk();
  197. }
  198. /**
  199. * 下载apk文件
  200. */
  201. private void downloadApk()
  202. {
  203. // 启动新线程下载软件
  204. new downloadApkThread().start();
  205. }
  206. /**
  207. * 下载文件线程
  208. *
  209. * @author coolszy
  210. *@date 2012-4-26
  211. *@blog http://blog.92coding.com
  212. */
  213. private class downloadApkThread extends Thread
  214. {
  215. @Override
  216. public void run()
  217. {
  218. try
  219. {
  220. // 判断SD卡是否存在,并且是否具有读写权限
  221. if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
  222. {
  223. // 获得存储卡的路径
  224. String sdpath = Environment.getExternalStorageDirectory() + "/";
  225. mSavePath = sdpath + "download";
  226. URL url = new URL(mHashMap.get("url"));
  227. // 创建连接
  228. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  229. conn.connect();
  230. // 获取文件大小
  231. int length = conn.getContentLength();
  232. // 创建输入流
  233. InputStream is = conn.getInputStream();
  234. File file = new File(mSavePath);
  235. // 判断文件目录是否存在
  236. if (!file.exists())
  237. {
  238. file.mkdir();
  239. }
  240. File apkFile = new File(mSavePath, mHashMap.get("name"));
  241. FileOutputStream fos = new FileOutputStream(apkFile);
  242. int count = 0;
  243. // 缓存
  244. byte buf[] = new byte[1024];
  245. // 写入到文件中
  246. do
  247. {
  248. int numread = is.read(buf);
  249. count += numread;
  250. // 计算进度条位置
  251. progress = (int) (((float) count / length) * 100);
  252. // 更新进度
  253. mHandler.sendEmptyMessage(DOWNLOAD);
  254. if (numread <= 0)
  255. {
  256. // 下载完成
  257. mHandler.sendEmptyMessage(DOWNLOAD_FINISH);
  258. break;
  259. }
  260. // 写入文件
  261. fos.write(buf, 0, numread);
  262. } while (!cancelUpdate);// 点击取消就停止下载.
  263. fos.close();
  264. is.close();
  265. }
  266. } catch (MalformedURLException e)
  267. {
  268. e.printStackTrace();
  269. } catch (IOException e)
  270. {
  271. e.printStackTrace();
  272. }
  273. // 取消下载对话框显示
  274. mDownloadDialog.dismiss();
  275. }
  276. };
  277. /**
  278. * 安装APK文件
  279. */
  280. private void installApk()
  281. {
  282. File apkfile = new File(mSavePath, mHashMap.get("name"));
  283. if (!apkfile.exists())
  284. {
  285. return;
  286. }
  287. // 通过Intent安装APK文件
  288. Intent i = new Intent(Intent.ACTION_VIEW);
  289. i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
  290. mContext.startActivity(i);
  291. }
  292. }

效果图

Android应用自动更新功能的代码实现(转)

Android应用自动更新功能的代码实现(转)

检查模拟器SDCARD是否存在下载文件:

Android应用自动更新功能的代码实现(转)

上一篇:2.3. 实体(Core Data 应用程序实践指南)


下一篇:Android 7.1.1 又出幺蛾子了 —— 再谈 Android 上的 Wifi 连接