Android 实现从后台下载apk文件,保存到本地sd卡,使用系统安装apk,完成版本更新功能

LoadAppUtil.java

  1. import java.io.File;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.net.HttpURLConnection;
  6. import java.net.URL;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9.  
  10. /**
  11. * Depiction:
  12. * Author: kuzan
  13. * Date: 2017/01/14 9:42
  14. */
  15. public class LoadAppUtil {
  16.  
  17. private static List<String> list = new ArrayList<>();
  18. private static final int TIMEOUT = 10 * 60 * 1000; // 超时
  19.  
  20. public static final int LOAD_PROCESS = 1;
  21. public static final int LOAD_SUCCEED = 2;
  22. public static final int LOAD_ERROR = 3;
  23.  
  24. /**
  25. * 下载apk
  26. * @param context
  27. * @param packageName 保存apk的名称
  28. * @param url apk后台下载的地址
  29. */
  30. public static void loadApp(Context context, final String packageName, final String url) {
  31.  
  32. final String path = getLoadAppPath(context, packageName);
  33.  
  34. if (StringUtils.isEmptyString(path) || StringUtils.isEmptyString(url)) {
  35. loadError(packageName);
  36. return;
  37. }
  38.  
  39. // 判断是否在下载
  40. if (list.contains(packageName)) {
  41. return;
  42. }
  43.  
  44. list.add(packageName);
  45.  
  46. new Thread(new Runnable() {
  47. @Override
  48. public void run() {
  49. downloadUpdateFile(packageName, url, path);
  50. }
  51. }).start();
  52. }
  53.  
  54. /**
  55. * 文件下载
  56. * @param packageName 保存apk的名称
  57. * @param down_url apk后台下载的地址
  58. * @param file 保存apk的文件路径
  59. * */
  60. public static void downloadUpdateFile(String packageName, String down_url, String file) {
  61.  
  62. int totalSize = 0; // 文件总大小
  63. InputStream in = null;
  64. FileOutputStream fos = null;
  65.  
  66. FileUtils.deleteFile(file);
  67. FileUtils.createFile(file);
  68.  
  69. URL url;
  70. try {
  71. url = new URL(down_url);
  72.  
  73. HttpURLConnection httpURLConnection = (HttpURLConnection) url
  74. .openConnection();
  75.  
  76. httpURLConnection.setConnectTimeout(TIMEOUT);
  77.  
  78. httpURLConnection.setReadTimeout(TIMEOUT);
  79.  
  80. // 获取下载文件的size
  81. totalSize = httpURLConnection.getContentLength();
  82. Log.e("LoadAppUtil", "totalSize = " + totalSize);
  83.  
  84. if (httpURLConnection.getResponseCode() == 404) {
  85. loadError(packageName);
  86. return;
  87. }
  88.  
  89. in = httpURLConnection.getInputStream();
  90. fos = new FileOutputStream(file, false);
  91. } catch (IOException e) {
  92. e.printStackTrace();
  93. loadError(packageName);
  94. return;
  95. }
  96.  
  97. int downloadCount = 0; // 已下载大小
  98. int updateProgress = 0; // 更新进度
  99.  
  100. byte buffer[] = new byte[64];
  101. int readsize = 0;
  102.  
  103. try {
  104. LoadBean bean = new LoadBean(packageName,file,0);
  105. post(LOAD_PROCESS,bean);
  106. while ((readsize = in.read(buffer)) != -1) {
  107.  
  108. fos.write(buffer, 0, readsize);
  109.  
  110. // 计算已下载到的大小
  111. downloadCount += readsize;
  112.  
  113. // 先计算已下载的百分比,然后跟上次比较是否有增加,有则更新通知进度
  114. int now = downloadCount * 100 / totalSize;
  115. // Log.e(TAG, "now = " + now) ;
  116. if (updateProgress < now) {
  117. updateProgress = now;
  118. // 发送下载进度
  119. bean.setProcess(now);
  120. post(LOAD_PROCESS,bean);
  121. }
  122. }
  123. } catch (Exception e) {
  124. e.printStackTrace();
  125. loadError(packageName);
  126. return;
  127. } finally {
  128. try {
  129. fos.close();
  130. in.close();
  131. } catch (IOException e) {
  132. e.printStackTrace();
  133. }
  134. }
  135.  
  136. // 发送下载完成消息
  137. if(list.contains(packageName)){
  138. list.remove(packageName);
  139. LoadBean bean = new LoadBean(packageName,file,100);
  140. post(LOAD_SUCCEED,bean);
  141. }
  142.  
  143. }
  144.  
  145. private static void loadError(String packageName) {
  146. if (list.contains(packageName)) {
  147. list.remove(packageName);
  148. }
  149. // 发送下载错误消息
  150. LoadBean bean = new LoadBean(packageName,"",0);
  151. post(LOAD_ERROR,bean);
  152. }
  153.  
  154. /**
  155. * 获取保存apk的文件路径
  156. *
  157. * @param context
  158. * @return
  159. */
  160. public static String getLoadAppPath(Context context, String packageName) {
  161. if (FileUtils.isSdCardExist()) {
  162. return context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + File.separator + packageName + ".apk";
  163. }
  164. return "";
  165. }
  166.  
  167. /**
  168. * 判断文件是否存在
  169. * @param path 文件路径
  170. * @return
  171. */
  172. public static boolean isFileExists(String path) {
  173. if (TextUtils.isEmpty(path)) {
  174. return false;
  175. }
  176. try {
  177. File f=new File(path);
  178. if(!f.exists()) {
  179. return false;
  180. }
  181. }
  182. catch (Exception e) {
  183. return false;
  184. }
  185.  
  186. return true;
  187. }
  188.  
  189. /**
  190. * 通知UI
  191. * */
  192. public static void post(int clickId,LoadBean bean){
  193. RxBus.getDefault().post(RxBusEvent.newBuilder(R.id.load_apk).clickId(clickId).setObj(bean).build());
  194. }
  195. }

调用方法:

  1. LoadAppUtil.loadApp(UpdateActivity.this, newVersionName, UrlConstants.UPLOAD_URL);

信息读取:(使用的消息通知是android RxBus)

  1. mySubscriptions.add(RxBus.getDefault().toObserverable(RxBusEvent.class).subscribe(new Action1<RxBusEvent>() {
  2. @Override
  3. public void call(final RxBusEvent rxBusEvent) {
  4. if (rxBusEvent.getType() == R.id.load_apk) {
  5. switch (rxBusEvent.getClickId()) {
  6. case LoadAppUtil.LOAD_SUCCEED:
  7. runOnUiThread(new Runnable() {
  8. @Override
  9. public void run() {
  10. // 下载完成
  11. Log.e("LOAD", "LOAD_SUCCEED");
  12. AppUtils.installApp(UpdateActivity.this, (LoadBean) rxBusEvent.getObj().getPath());
  13. isLoadFinish = true;
  14. }
  15. });
  16. break;
  17.  
  18. case LoadAppUtil.LOAD_PROCESS:
  19. runOnUiThread(new Runnable() {
  20. @Override
  21. public void run() {
  22. // 下载进度
  23. isLoadFinish = false;
  24. updateProgress((LoadBean) rxBusEvent.getObj());
  25. }
  26. });
  27. break;
  28.  
  29. case LoadAppUtil.LOAD_ERROR:
  30. runOnUiThread(new Runnable() {
  31. @Override
  32. public void run() {
  33. // 下载出错
  34. Log.e("upload", "err");
  35. isLoadFinish = false;
  36. ToastUtils.ToastMessage(UpdateActivity.this, "下载失败");
  37. }
  38. });
  39. break;
  40. }
  41. }
  42. }
  43. }));

LoadBean.java

  1. public class LoadBean {
  2.  
  3. private String packageName;
  4. private String path;
  5. private int process;
  6.  
  7. public LoadBean(String packageName, String path, int process) {
  8. this.packageName = packageName;
  9. this.path = path;
  10. this.process = process;
  11. }
  12.  
  13. public String getPackageName() {
  14. return packageName;
  15. }
  16.  
  17. public void setPackageName(String packageName) {
  18. this.packageName = packageName;
  19. }
  20.  
  21. public String getPath() {
  22. return path;
  23. }
  24.  
  25. public void setPath(String path) {
  26. this.path = path;
  27. }
  28.  
  29. public int getProcess() {
  30. return process;
  31. }
  32.  
  33. public void setProcess(int process) {
  34. this.process = process;
  35. }
  36. }

Android实现版本更新的更多相关文章

  1. android应用版本更新功能---完整版

    源码下载地址:csdn下载地址:http://download.csdn.net/download/csdn576038874/9526085 博客园下载地址:http://files.cnblogs ...

  2. Android程序版本更新--通知栏更新下载安装(转)

    Android应用检查版本更新后,在通知栏下载,更新下载进度,下载完成自动安装,效果图如下: 检查当前版本号 AndroidManifest文件中的versionCode用来标识版本,在服务器放一个新 ...

  3. Android软件版本更新

     转的:适合新手学习,但在实际项目中不可这么做. 以下是我转的内容: ================================================================= ...

  4. android检测版本更新

    原理就是从服务器获取版本号和本得apk的版本号对比更新: //检查更新        Activity activity = this;        while(activity.getParent ...

  5. Android提示版本更新的实现

    一.首先,参考了以下文章<Android自动检测版本及自动升级> http://www.linuxidc.com/Linux/2011-10/45718p2.htm: 步骤: .检测当前版 ...

  6. Android关于版本更新下载安装之踩坑记录(针对7.0以上)

    最近刚刚把古老的项目targetSdk版本升级到26,升级之前是19(非常非常古老了).那么升级后一些问题开始出现. Android 8.0 (Android O)为了针对一些流氓软件引导用户安装其他 ...

  7. Android 全局弹出版本更新 Dialog 思考和解决办法

    Android 针对版本更新,需要做全局的弹出(需求:版本更新只需要在 App 内全局弹出就可以),思路是使用 AlertDialog ,然后设置 setType 为 TYPE_ALERT_WINDO ...

  8. Android其它新控件 (转)

    原文出处:http://blog.csdn.net/lavor_zl/article/details/51312715 Android其它新控件是指非Android大版本更新时提出的新控件,也非谷歌I ...

  9. 从 Eclipse 迁移至 Android Studio

    从 Eclipse 迁移至 Android Studio 本文内容 Android Studio 基础知识 迁移先决条件 将项目导入 Android Studio 后续步骤 将项目迁移至 Androi ...

随机推荐

  1. regress_partition.sql

    --ENV --UAT @/test/change/env/env_test_uat.sql set echo on time on timing on set feedback on set pag ...

  2. 51nod 修改数组

    修改数组 给出一个整数数组A,你可以将任何一个数修改为任意一个正整数,最终使得整个数组是严格递增的且均为正整数.问最少需要修改几个数? Input 第1行:一个数N表示序列的长度(1 <= N  ...

  3. PAT (Advanced Level) 1058. A+B in Hogwarts (20)

    简单题. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #in ...

  4. WebDriver(Selenium2) 判断页面是否刷新的方法

    http://uniquepig.iteye.com/blog/1568208 public static boolean waitPageRefresh(WebElement trigger) { ...

  5. base库插件---form

    $().extend('serialize', function () { for (var i = 0; i < this.elements.length; i ++) { var form ...

  6. ZBUS = MQ + RPC

    http://git.oschina.net/rushmore/zbus http://my.oschina.net/sbz/blog  Readme.md 18.02 KB ZBUS = MQ + ...

  7. jquery全选框的实现

    函数实现的话: head里加入函数实现 <script language="javascript" type="text/javascript"> ...

  8. Android init.rc文件浅析

    Android init.rc文件浅析 分类: Android2012-04-13 18:00 13149人阅读 评论(2) 收藏 举报 androidservicepathactionsocketc ...

  9. linux shell 之if-------用if做判断

    综合网络,略有修改, 一 简介 1 字符串判断 str1 = str2 当两个串有相同内容.长度时为真  str1 != str2 当串str1和str2不等时为真  -n str1 当串的长度大于0 ...

  10. CORBA技术及实例

    CORBA技术及实例 CORBA是一种规范,它定义了分布式对象如何实现互操作.在WorldWideWeb盛行之前,非凡是java编程语言风靡之前,C++开发者基本将CORBA作为其高端分布式对象的解决 ...