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

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

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

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

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

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

效果图

Android应用更新自动检测下载

Android应用更新自动检测下载

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

Android应用更新自动检测下载


参考代码下载:

 http://files.cnblogs.com/coolszy/UpdateSoftDemo.rar