开发环境:android studio sdk 4.0及以上
场景:用户点击检查更新按钮进行检查服务器版本号,若有新版本则进行下载更新。异步检测版本号
1 package com.example.qurenwu.qurenwu_2.util; 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 import java.util.Map; 12 13 import android.app.AlertDialog; 14 import android.app.AlertDialog.Builder; 15 import android.app.Dialog; 16 import android.content.Context; 17 import android.content.DialogInterface; 18 import android.content.DialogInterface.OnClickListener; 19 import android.content.Intent; 20 import android.content.pm.PackageManager; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.net.Uri; 23 import android.os.Environment; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.util.Log; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.widget.ProgressBar; 30 import android.widget.Toast; 31 32 import com.example.qurenwu.qurenwu_2.R; 33 import com.example.qurenwu.qurenwu_2.custom.GotaskGlobalVal; 34 35 import org.w3c.dom.Document; 36 import org.w3c.dom.Element; 37 import org.w3c.dom.Node; 38 import org.w3c.dom.NodeList; 39 40 import javax.xml.parsers.DocumentBuilder; 41 import javax.xml.parsers.DocumentBuilderFactory; 42 43 /** 44 * Created by Administrator on 2014/12/3. 45 */ 46 public class UpdateManager { 47 GotaskGlobalVal globalVal; //全局 48 /* 下载中 */ 49 private static final int DOWNLOAD = 1; 50 /* 下载结束 */ 51 private static final int DOWNLOAD_FINISH = 2; 52 /*检查是否有更新*/ 53 private static final int CHECK_APK_VERSION= 3; 54 /* 保存解析的XML信息 */ 55 HashMap<String, String> mHashMap; 56 /* 下载保存路径 */ 57 private String mSavePath; 58 /* 记录进度条数量 */ 59 private int progress; 60 /* 是否取消更新 */ 61 private boolean cancelUpdate = false; 62 63 private Context mContext; 64 65 private int mFlag; // 0:自启动检测 1:手动检测,区别在于前者若无更新则不提示,后者若无更新需要提示 66 /* 更新进度条 */ 67 private ProgressBar mProgress; 68 private Dialog mDownloadDialog; 69 70 private Handler mHandler = new Handler() 71 { 72 public void handleMessage(Message msg) 73 { 74 switch (msg.what) 75 { 76 // 正在下载 77 case DOWNLOAD: 78 // 设置进度条位置 79 mProgress.setProgress(progress); 80 break; 81 case DOWNLOAD_FINISH: 82 // 安装文件 83 installApk(); 84 break; 85 case CHECK_APK_VERSION: //检查版本更新 86 mHashMap = (HashMap<String,String>)msg.obj; 87 // 获取当前软件版本 88 int versionCode = getVersionCode(mContext); 89 if (null != mHashMap) 90 { 91 int serviceCode = Integer.valueOf(mHashMap.get("version")); 92 // 版本判断 93 Log.v("isUpdate","serviceCode:"+serviceCode+";versionCode:"+versionCode); 94 if (serviceCode > versionCode) 95 { 96 //需要更新 97 showNoticeDialog(); 98 } 99 else 100 { 101 if(mFlag == 1) 102 {Toast.makeText(mContext, R.string.soft_update_no, Toast.LENGTH_LONG).show();} 103 } 104 } 105 break; 106 default: 107 break; 108 } 109 }; 110 }; 111 112 public UpdateManager(Context context) 113 { 114 this.mContext = context; 115 globalVal = (GotaskGlobalVal)context.getApplicationContext(); 116 mFlag = 0; 117 } 118 119 /** 120 * 检测软件更新 121 * @param flag 0:启动时检测 1:手动检测,区别在于前者若无更新则不提示,后者若无更新需要提示 122 */ 123 public void checkUpdate(int flag) 124 { 125 mFlag = flag; 126 CheckVersionHttpThread checkthread = new CheckVersionHttpThread(mHandler,CHECK_APK_VERSION); 127 new Thread(checkthread).start(); 128 } 129 /** 130 * 检查软件是否有更新版本 131 * 132 * @return 133 */ 134 private boolean isUpdate() 135 { 136 // 获取当前软件版本 137 int versionCode = getVersionCode(mContext); 138 // 把version.xml放到网络上,然后获取文件信息 139 //InputStream inStream = ParseXmlService.class.getClassLoader().getResourceAsStream("version.xml"); 140 // 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析 141 ParseXmlService service = new ParseXmlService(); 142 try 143 { 144 //String path = "http://192.168.1.146:8080/picweb/xml/version.xml"; 145 String path = globalVal.BASE_URL+"/xml/version.xml"; 146 URL url = new URL(path); 147 HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 148 conn.setReadTimeout(5*1000); 149 conn.setRequestMethod("GET"); 150 InputStream inStream = conn.getInputStream(); 151 mHashMap = service.parseXml(inStream); 152 } catch (Exception e) 153 { 154 e.printStackTrace(); 155 } 156 if (null != mHashMap) 157 { 158 int serviceCode = Integer.valueOf(mHashMap.get("version")); 159 // 版本判断 160 Log.v("isUpdate","serviceCode:"+serviceCode+";versionCode:"+versionCode); 161 if (serviceCode > versionCode) 162 { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 /** 170 * 获取软件版本号 171 * 172 * @param context 173 * @return 174 */ 175 private int getVersionCode(Context context) 176 { 177 int versionCode = 0; 178 try 179 { 180 // 获取软件版本号,对应AndroidManifest.xml下android:versionCode 181 versionCode = context.getPackageManager().getPackageInfo("com.example.qurenwu.qurenwu_2", 0).versionCode; 182 } catch (PackageManager.NameNotFoundException e) 183 { 184 e.printStackTrace(); 185 } 186 return versionCode; 187 } 188 189 /** 190 * 显示软件更新对话框 191 */ 192 private void showNoticeDialog() 193 { 194 // 构造对话框 195 AlertDialog.Builder builder = new Builder(mContext); 196 builder.setTitle(R.string.soft_update_title); 197 builder.setMessage(R.string.soft_update_info); 198 // 更新 199 builder.setPositiveButton(R.string.soft_update_updatebtn, new OnClickListener() 200 { 201 @Override 202 public void onClick(DialogInterface dialog, int which) 203 { 204 dialog.dismiss(); 205 // 显示下载对话框 206 showDownloadDialog(); 207 } 208 }); 209 // 稍后更新 210 builder.setNegativeButton(R.string.soft_update_later, new OnClickListener() 211 { 212 @Override 213 public void onClick(DialogInterface dialog, int which) 214 { 215 dialog.dismiss(); 216 } 217 }); 218 Dialog noticeDialog = builder.create(); 219 noticeDialog.show(); 220 } 221 222 /** 223 * 显示软件下载对话框 224 */ 225 private void showDownloadDialog() 226 { 227 // 构造软件下载对话框 228 AlertDialog.Builder builder = new Builder(mContext); 229 builder.setTitle(R.string.soft_updating); 230 // 给下载对话框增加进度条 231 final LayoutInflater inflater = LayoutInflater.from(mContext); 232 View v = inflater.inflate(R.layout.softupdate_progress, null); 233 mProgress = (ProgressBar) v.findViewById(R.id.update_progress); 234 builder.setView(v); 235 // 取消更新 236 builder.setNegativeButton(R.string.soft_update_cancel, new OnClickListener() 237 { 238 @Override 239 public void onClick(DialogInterface dialog, int which) 240 { 241 dialog.dismiss(); 242 // 设置取消状态 243 cancelUpdate = true; 244 } 245 }); 246 mDownloadDialog = builder.create(); 247 mDownloadDialog.show(); 248 // 现在文件 249 downloadApk(); 250 } 251 252 /** 253 * 下载apk文件 254 */ 255 private void downloadApk() 256 { 257 // 启动新线程下载软件 258 new downloadApkThread().start(); 259 } 260 261 /** 262 * 下载文件线程 263 */ 264 private class downloadApkThread extends Thread 265 { 266 @Override 267 public void run() 268 { 269 try 270 { 271 // 判断SD卡是否存在,并且是否具有读写权限 272 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) 273 { 274 // 获得存储卡的路径 275 String sdpath = Environment.getExternalStorageDirectory() + "/"; 276 mSavePath = sdpath + "download"; 277 URL url = new URL(mHashMap.get("url")); 278 // 创建连接 279 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 280 conn.connect(); 281 // 获取文件大小 282 int length = conn.getContentLength(); 283 // 创建输入流 284 InputStream is = conn.getInputStream(); 285 286 File file = new File(mSavePath); 287 // 判断文件目录是否存在 288 if (!file.exists()) 289 { 290 file.mkdir(); 291 } 292 File apkFile = new File(mSavePath, mHashMap.get("name")); 293 FileOutputStream fos = new FileOutputStream(apkFile); 294 int count = 0; 295 // 缓存 296 byte buf[] = new byte[1024]; 297 // 写入到文件中 298 do 299 { 300 int numread = is.read(buf); 301 count += numread; 302 // 计算进度条位置 303 progress = (int) (((float) count / length) * 100); 304 // 更新进度 305 mHandler.sendEmptyMessage(DOWNLOAD); 306 if (numread <= 0) 307 { 308 // 下载完成 309 mHandler.sendEmptyMessage(DOWNLOAD_FINISH); 310 break; 311 } 312 // 写入文件 313 fos.write(buf, 0, numread); 314 } while (!cancelUpdate);// 点击取消就停止下载. 315 fos.close(); 316 is.close(); 317 } 318 } catch (MalformedURLException e) 319 { 320 e.printStackTrace(); 321 } catch (IOException e) 322 { 323 e.printStackTrace(); 324 } 325 // 取消下载对话框显示 326 mDownloadDialog.dismiss(); 327 } 328 }; 329 330 /** 331 * 安装APK文件 332 */ 333 private void installApk() 334 { 335 File apkfile = new File(mSavePath, mHashMap.get("name")); 336 if (!apkfile.exists()) 337 { 338 return; 339 } 340 // 通过Intent安装APK文件 341 Intent i = new Intent(Intent.ACTION_VIEW); 342 i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); 343 mContext.startActivity(i); 344 } 345 class ParseXmlService 346 { 347 public HashMap<String, String> parseXml(InputStream inStream) throws Exception 348 { 349 HashMap<String, String> hashMap = new HashMap<String, String>(); 350 351 // 实例化一个文档构建器工厂 352 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 353 // 通过文档构建器工厂获取一个文档构建器 354 DocumentBuilder builder = factory.newDocumentBuilder(); 355 // 通过文档通过文档构建器构建一个文档实例 356 Document document = builder.parse(inStream); 357 //获取XML文件根节点 358 Element root = document.getDocumentElement(); 359 //获得所有子节点 360 NodeList childNodes = root.getChildNodes(); 361 for (int j = 0; j < childNodes.getLength(); j++) 362 { 363 //遍历子节点 364 Node childNode = (Node) childNodes.item(j); 365 if (childNode.getNodeType() == Node.ELEMENT_NODE) 366 { 367 Element childElement = (Element) childNode; 368 //版本号 369 if ("version".equals(childElement.getNodeName())) 370 { 371 hashMap.put("version",childElement.getFirstChild().getNodeValue()); 372 } 373 //软件名称 374 else if (("name".equals(childElement.getNodeName()))) 375 { 376 hashMap.put("name",childElement.getFirstChild().getNodeValue()); 377 } 378 //下载地址 379 else if (("url".equals(childElement.getNodeName()))) 380 { 381 hashMap.put("url",childElement.getFirstChild().getNodeValue()); 382 } 383 } 384 } 385 return hashMap; 386 } 387 } 388 /** 389 * 检测线程 390 */ 391 class CheckVersionHttpThread implements Runnable{ 392 private Handler handler; 393 int requestCode; 394 /** 395 * @param _handler 396 * @param _requestCode 若一个activity有多个此请求,那么activity中的handle的msg .what也不同,用该字段区分 397 */ 398 public CheckVersionHttpThread(Handler _handler,int _requestCode) 399 { 400 this.handler = _handler; 401 this.requestCode = _requestCode; 402 } 403 @Override 404 public void run() { 405 HashMap<String, String> result= null; 406 // 把version.xml放到网络上,然后获取文件信息 407 //InputStream inStream = ParseXmlService.class.getClassLoader().getResourceAsStream("version.xml"); 408 // 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析 409 ParseXmlService service = new ParseXmlService(); 410 try 411 { 412 //String path = "http://192.168.1.146:8080/picweb/xml/version.xml"; 413 String path = globalVal.BASE_URL+"/xml/version.xml"; 414 URL url = new URL(path); 415 HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 416 conn.setReadTimeout(5*1000); 417 conn.setRequestMethod("GET"); 418 InputStream inStream = conn.getInputStream(); 419 result = service.parseXml(inStream); 420 } catch (Exception e) 421 { 422 e.printStackTrace(); 423 } 424 Message msg = new Message(); 425 msg.what = requestCode; 426 msg.obj = result; 427 handler.sendMessage(msg); 428 } 429 } 430 }
主界面中调用:
//检测新版本 UpdateManager manager = new UpdateManager(MyActivity.this);// 检查软件更新 manager.checkUpdate(0); // 0:表示启动时检测 1:手动检测,区别在于前者若无更新则不提示,后者若无更新需要提示
mainfest.xml 中需要配置版本号
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.qurenwu.qurenwu_2" android:versionCode="1" android:versionName="1.0" >
string.xml需要增加字符串
<string name="soft_update_no">已经是最新版本</string> <string name="soft_update_title">软件更新</string> <string name="soft_update_info">检测到新版本,立即更新吗</string> <string name="soft_update_updatebtn">更新</string> <string name="soft_update_later">稍后更新</string> <string name="soft_updating">正在更新</string> <string name="soft_update_cancel">取消</string>
服务器端需要在目录如:http://192.168.11.1/xml/ 目录下增加文件 version.xml 内容如下:
<?xml version="1.0" encoding="UTF-8"?> <update> <version>1</version> <!-- 必须是整数 --> <name>gotask.1.0</name> <url>http://192.168.11.6/download/gotask.apk</url> </update>
ok! 大功告成!