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。把获取到的手机上应用版本与服务器端的版本进行比较,应用就可以判断处是否需要更新软件。

处理流程


处理代码

  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. }

 

效果图

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

Tagged:

Comments are closed.