Android - 软件自动更新的实现

接触到一个很实用的技术,那就是软件自动更新。一般开发者是通过自行在应用平台添加更新版本的apk。这样做,如果是在一两个应用平台发布应用,那还说得过去,工作量还不是很大。但大家都知道,Android开发者是比较苦逼的。由于开源所致,出现了N多应用市场。如果想赚取更多的收入,那就要在各个应用市场进行更新。那就悲催咯。

比较出名的一些应用市场有如下:

 

                                                               

安卓自动更新 demo android 自动更新_java

那如何实现软件自动更新,下面是具体实例:

效果图:

   

安卓自动更新 demo android 自动更新_3c_02

    

具体步骤:

1. 在服务器上部署更新所用的xml文件:version.xml


<update>
	<version>2</version>
	<name>baiduxinwen.apk</name>
	<url>http://gdown.baidu.com/data/wisegame/e5f5c3b8e59401c8/baiduxinwen.apk</url>
</update>

 

2. 在客户端实现更新操作

  涉及到三个技术:

   1.xml文件的解析

   2.HttpURLConnection连接

   3.文件流I/O

 

这里创建一个解析xml文件的服务类:ParXmlService.java


package com.xiaowu.news.update;

import java.io.InputStream;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ParseXmlService {
	public HashMap<String, String> parseXml (InputStream inStream) throws Exception{
		HashMap<String, String> hashMap = new HashMap<String, String>();
		//创建DocumentBuilderFactory,该对象将创建DocumentBuilder。
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		//创建DocumentBuilder,DocumentBuilder将实际进行解析以创建Document对象
		DocumentBuilder builder = factory.newDocumentBuilder();
		//解析该文件以创建Document对象
		Document document = builder.parse(inStream);
		//获取XML文件根节点 
		Element root = document.getDocumentElement();
		//获得所有子节点
		NodeList childNodes = root.getChildNodes();
		for(int i = 0; i < childNodes.getLength(); i++) {
			Node childNode = (Node) childNodes.item(i);
			if(childNode.getNodeType() == Node.ELEMENT_NODE) {
				Element childElement = (Element) childNode;
				//版本号 
				if("version".equals(childElement.getNodeName())) {
					hashMap.put("version", childElement.getFirstChild().getNodeValue());
				//软件名称 
				} else if("name".equals(childElement.getNodeName())) {
					hashMap.put("name", childElement.getFirstChild().getNodeValue());
				//下载地址
				} else if("url".equals(childElement.getNodeName())) {
					hashMap.put("url", childElement.getFirstChild().getNodeValue());
				}
			}
			
		}
		return hashMap;
	}
}

实现更新操作的管理类:UpdateManager.java


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