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 }
调用方法:
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 }