传送门 ☞ 轮子的专栏 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229

今天我们学习如何实现Android应用的自动更新版本功能,这是在各种语言编写的应用中都会经常遇到的情景。当我们的应用检测到网络上有新版本发布时,系统会提示是否下载新版本应用,当新版本应用下载完毕后,系统会自动安装下载的新版本应用(或跳转到相关安装页面询问)。我们将下载的应用存放在sdcard中,由于整个流程涉及对sdcard的读写操作,所以要赋给我们应用读写外存的权限。下面给出该场景的案例:

一、案例技术要点

1.程序清单文件中需要配置如下权限:

访问网络

  1. <uses-permission android:name="android.permission.INTERNET"/>

读取sdcard

  1. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

写入sdcard

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2.创建一个HttpURLConnection连接,从网络下载新版本应用到本地

3.创建一个ProgressBar下载进度条,实时显示下载应用的进度

4.应用下载完毕后,构建Intent跳转至其安装页面,该Intent的配置如下:

Action:Intent.ACTION_VIEW

DataAndType:Uri.parse("file://" + appFile.toString()),"application/vnd.android.package-archive"

二、案例代码陈列

AndroidManifest.xml

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="cn.lynn.autoupdate"
  3. android:versionCode="1"
  4. android:versionName="1.0" >
  5. <uses-sdk
  6. android:minSdkVersion="8"
  7. android:targetSdkVersion="15" />
  8. <uses-permission android:name="android.permission.INTERNET"/>
  9. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  10. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  11. <application
  12. android:icon="@drawable/ic_launcher"
  13. android:label="@string/app_name" >
  14. <activity
  15. android:name=".AutoUpdateMainActivity"
  16. android:label="@string/app_name" >
  17. <intent-filter>
  18. <action android:name="android.intent.action.MAIN" />
  19. <category android:name="android.intent.category.LAUNCHER" />
  20. </intent-filter>
  21. </activity>
  22. </application>
  23. </manifest>

strings.xml

  1. <resources>
  2. <string name="app_name">Android实现应用自动更新</string>
  3. </resources>

main.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent" >
  4. <TextView
  5. android:layout_width="wrap_content"
  6. android:layout_height="wrap_content" />
  7. </LinearLayout>

下载进度条布局文件:progressBar.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content">
  5. <ProgressBar
  6. android:id="@+id/progressBar"
  7. style="?android:attr/progressBarStyleHorizontal"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content" />
  10. </LinearLayout>

AutoUpdateMainActivity.java

  1. package cn.lynn.autoupdate;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class AutoUpdateMainActivity extends Activity {
  5. private UpdateAppManager updateManager;
  6. @Override
  7. public void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10. updateManager = new UpdateAppManager(this);
  11. updateManager.checkUpdateInfo();
  12. }
  13. }

UpdateAppManager.java

  1. package cn.lynn.autoupdate;
  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.URL;
  8. import android.app.AlertDialog;
  9. import android.app.Dialog;
  10. import android.content.Context;
  11. import android.content.DialogInterface;
  12. import android.content.DialogInterface.OnClickListener;
  13. import android.content.Intent;
  14. import android.net.Uri;
  15. import android.os.Environment;
  16. import android.os.Handler;
  17. import android.os.Message;
  18. import android.view.LayoutInflater;
  19. import android.view.View;
  20. import android.widget.ProgressBar;
  21. public class UpdateAppManager {
  22. // 文件分隔符
  23. private static final String FILE_SEPARATOR = "/";
  24. // 外存sdcard存放路径
  25. private static final String FILE_PATH = Environment.getExternalStorageDirectory() + FILE_SEPARATOR +"autoupdate" + FILE_SEPARATOR;
  26. // 下载应用存放全路径
  27. private static final String FILE_NAME = FILE_PATH + "autoupdate.apk";
  28. // 更新应用版本标记
  29. private static final int UPDARE_TOKEN = 0x29;
  30. // 准备安装新版本应用标记
  31. private static final int INSTALL_TOKEN = 0x31;
  32. private Context context;
  33. private String message = "检测到本程序有新版本发布,建议您更新!";
  34. // 以华为天天聊hotalk.apk为例
  35. private String spec = "http://222.42.1.209:81/1Q2W3E4R5T6Y7U8I9O0P1Z2X3C4V5B/mt.hotalk.com:8080/release/hotalk1.9.17.0088.apk";
  36. // 下载应用的对话框
  37. private Dialog dialog;
  38. // 下载应用的进度条
  39. private ProgressBar progressBar;
  40. // 进度条的当前刻度值
  41. private int curProgress;
  42. // 用户是否取消下载
  43. private boolean isCancel;
  44. public UpdateAppManager(Context context) {
  45. this.context = context;
  46. }
  47. private final Handler handler = new Handler(){
  48. @Override
  49. public void handleMessage(Message msg) {
  50. switch (msg.what) {
  51. case UPDARE_TOKEN:
  52. progressBar.setProgress(curProgress);
  53. break;
  54. case INSTALL_TOKEN:
  55. installApp();
  56. break;
  57. }
  58. }
  59. };
  60. /**
  61. * 检测应用更新信息
  62. */
  63. public void checkUpdateInfo() {
  64. showNoticeDialog();
  65. }
  66. /**
  67. * 显示提示更新对话框
  68. */
  69. private void showNoticeDialog() {
  70. new AlertDialog.Builder(context)
  71. .setTitle("软件版本更新")
  72. .setMessage(message)
  73. .setPositiveButton("下载", new OnClickListener() {
  74. @Override
  75. public void onClick(DialogInterface dialog, int which) {
  76. dialog.dismiss();
  77. showDownloadDialog();
  78. }
  79. }).setNegativeButton("以后再说", new OnClickListener() {
  80. @Override
  81. public void onClick(DialogInterface dialog, int which) {
  82. dialog.dismiss();
  83. }
  84. }).create().show();
  85. }
  86. /**
  87. * 显示下载进度对话框
  88. */
  89. private void showDownloadDialog() {
  90. View view = LayoutInflater.from(context).inflate(R.layout.progressbar, null);
  91. progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
  92. AlertDialog.Builder builder = new AlertDialog.Builder(context);
  93. builder.setTitle("软件版本更新");
  94. builder.setView(view);
  95. builder.setNegativeButton("取消", new OnClickListener() {
  96. @Override
  97. public void onClick(DialogInterface dialog, int which) {
  98. dialog.dismiss();
  99. isCancel = true;
  100. }
  101. });
  102. dialog = builder.create();
  103. dialog.show();
  104. downloadApp();
  105. }
  106. /**
  107. * 下载新版本应用
  108. */
  109. private void downloadApp() {
  110. new Thread(new Runnable() {
  111. @Override
  112. public void run() {
  113. URL url = null;
  114. InputStream in = null;
  115. FileOutputStream out = null;
  116. HttpURLConnection conn = null;
  117. try {
  118. url = new URL(spec);
  119. conn = (HttpURLConnection) url.openConnection();
  120. conn.connect();
  121. long fileLength = conn.getContentLength();
  122. in = conn.getInputStream();
  123. File filePath = new File(FILE_PATH);
  124. if(!filePath.exists()) {
  125. filePath.mkdir();
  126. }
  127. out = new FileOutputStream(new File(FILE_NAME));
  128. byte[] buffer = new byte[1024];
  129. int len = 0;
  130. long readedLength = 0l;
  131. while((len = in.read(buffer)) != -1) {
  132. // 用户点击“取消”按钮,下载中断
  133. if(isCancel) {
  134. break;
  135. }
  136. out.write(buffer, 0, len);
  137. readedLength += len;
  138. curProgress = (int) (((float) readedLength / fileLength) * 100);
  139. handler.sendEmptyMessage(UPDARE_TOKEN);
  140. if(readedLength >= fileLength) {
  141. dialog.dismiss();
  142. // 下载完毕,通知安装
  143. handler.sendEmptyMessage(INSTALL_TOKEN);
  144. break;
  145. }
  146. }
  147. out.flush();
  148. } catch (Exception e) {
  149. e.printStackTrace();
  150. } finally {
  151. if(out != null) {
  152. try {
  153. out.close();
  154. } catch (IOException e) {
  155. e.printStackTrace();
  156. }
  157. }
  158. if(in != null) {
  159. try {
  160. in.close();
  161. } catch (IOException e) {
  162. e.printStackTrace();
  163. }
  164. }
  165. if(conn != null) {
  166. conn.disconnect();
  167. }
  168. }
  169. }
  170. }).start();
  171. }
  172. /**
  173. * 安装新版本应用
  174. */
  175. private void installApp() {
  176. File appFile = new File(FILE_NAME);
  177. if(!appFile.exists()) {
  178. return;
  179. }
  180. // 跳转到新版本应用安装页面
  181. Intent intent = new Intent(Intent.ACTION_VIEW);
  182. intent.setDataAndType(Uri.parse("file://" + appFile.toString()), "application/vnd.android.package-archive");
  183. context.startActivity(intent);
  184. }
  185. }

三、案例效果展示

  

新版本应用下载后,sdcard相关存放目录如下:

[置顶] Android应用开发之版本更新你莫愁的更多相关文章

  1. [置顶] Android 高级开发 源码 UI 缓存 网络

    1.Android 源码剖析 性能优化  开源代码 2.Android UI效果源码 3.http://mzh3344258.blog.51cto.com/1823534/d-3 4.微信公众平台开发 ...

  2. [置顶] Android开发笔记(成长轨迹)

    分类: 开发学习笔记2013-06-21 09:44 26043人阅读 评论(5) 收藏 Android开发笔记 1.控制台输出:called unimplemented OpenGL ES API ...

  3. [置顶] android ListView包含Checkbox滑动时状态改变

    题外话: 在xamarin android的开发中基本上所有人都会遇到这个小小的坎,的确有点麻烦,当时我也折腾了好一半天,如果你能看到这篇博客,说明你和我当初也是一样的焦灼,如果你想解决掉这个小小的坎 ...

  4. [置顶] Android开发实战记录(三)---HelloWorld

    1.新建Android项目,选择Android Project,然后Next 2.填写项目名称HelloWorld然后next,这里注意下,Java开发的命名规范 3.选择Android SDK版本, ...

  5. [置顶] Android开发百科全书

    友情提示根据目录 快速查找问题 %1$s %1$d Android string 1.整型,比如"我今年23岁了",这个23是整型的.在string.xml中可以这样写,<s ...

  6. [置顶] android开发之来电自动拒接并自动回复短信_上课模式app

    上课的时候老师说总是错过电话,对方打来没人接还一遍遍的打,觉得可以有个app在上课期间自动拒接电话,并自动回复短信过去. 当然了,需要权限的. 尝试做了个雏形出来. 界面如下: 主要代码如下: pac ...

  7. 【转】 [置顶] Android 通知栏Notification的整合 全面学习 (一个DEMO让你完全了解它)

    在Android的应用层中,涉及到很多应用框架,例如:Service框架,Activity管理机制,Broadcast机制,对话框框架,标题栏框架,状态栏框架,通知机制,ActionBar框架等等. ...

  8. [置顶] Android 适配真要命?

    原始尺寸场景 相信大家对上面也有所有耳闻另外就是如何计算屏幕的密度一般都是按照勾股定理例如中等屏幕密度 480^2+800^2开根号 然后除以当前屏幕尺寸3.5-4.2之间尺寸. 对于刚出来的那些An ...

  9. [置顶] Android 2016新技术

    版权声明:分享技术,传播快乐.如果本博客对你有帮助,请在我的博客首页为我打赏吧! 2016你需要了解Android有以下新兴的技术与框架,有些也许还不成熟,但是你应该去了解下,也许就是未来的方向. K ...

随机推荐

  1. 解决angular2页面刷新后报404错误

    如果你的angular项目部署到一个tomcat容器里面,localhost:8080是JavaWeb的主页,localhost:8080/driver/login是你angular2项目的登陆地址. ...

  2. ORCHARD中文文档(翻译)

    众所周知,Orchard是.net领域最好的开源CMS之一,他使用了微软最先进的技术,有一群先进理念的支持者,但是,所有的事情在国内总得加个但是,Orchard也不例外,中文资料相对比较少,官网提供的 ...

  3. (旧)子数涵数·PS ——素描效果

    一.准备素材(均为在百度上下载的) 二.打开ps,并在ps中打开第一张素材 三.复制图层(好习惯) 四.去色将图像变成黑白,图像->调整->去色,快捷键为Ctrl+Shift+U 五,复制 ...

  4. [C#]Hosting Process (vshost.exe)

    写在前面 最近在群里,有朋友问起这个vshost.exe进程到底是什么?当时确实不知道是个什么东东,给人的感觉是,经常看到它,就是在启动一个项目的时候,经常看到它,就是没细研究它是啥玩意儿.既然遇到了 ...

  5. 随堂练习——Rational rose

    管理员 学生

  6. 阿里面试回来,想和Java程序员谈一谈(转载)

    引言 其实本来真的没打算写这篇文章,主要是LZ得记忆力不是很好,不像一些记忆力强的人,面试完以后,几乎能把自己和面试官的对话都给记下来.LZ自己当初面试完以后,除了记住一些聊过的知识点以外,具体的内容 ...

  7. C基础--初学指针

    一.指针简介(任何指针变量在内存中占八个字节的存储空间) 1.定义指针变量(格式):变量类型 *变量名:--定义一个指向整形变量的指针  int *p; 2.作用:指针变量就是用来存储其他变量的地址: ...

  8. uploadfile上传文件时ie浏览器无法弹出窗口

    设置--->安全---->activeX筛选取消选择 更多.net.sqlserver.jquery资料欢迎访问 htttp://www.itservicecn.com    

  9. bootstrap table简洁扁平的表格

    使用方法 1.在html页面的head标签中引入Bootstrap库(假如你的项目还没使用)和bootstrap-table.css. <link rel="stylesheet&qu ...

  10. 【bzoj1853】 Scoi2010—幸运数字

    http://www.lydsy.com/JudgeOnline/problem.php?id=1853 (题目链接) 今天考试考了容斥,结果空知道结论却不会写→_→ 题意 求区间中不含6,8两个数字 ...