在AndroidManifest.xml中可以看到项目的版本号versionCode和版本名称versionName。其中版本号是整型数字 是我们用来判断是否更新的依据,版本名称是字符串 用来显示给用户看的

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="包名"
    android:versionCode="2"
    android:versionName="1.0.2">

 

 

实现思路:在本地或者服务器上放一个version.xml文件,这里以放到网络上为例,由于xml文件比较小,因此用DOM解析,解析后获得版本号与从AndroidManifest.xml中获取的版本号进行比对。

 

实现方法:为了方便开发,将更新时需要用到的方法写了一个UndateManagerService的类

 

public class UpdateManagerService {
	/* 获取远程配置文件结束 */
	private static final int GETREMOTEXML_FINISH = 1;
	/* 获取远程配置文件失败 */
	private static final int GETREMOTEXML_FAIL = 2;
	/* 下载结束 */
	private static final int FIND_NEW_UPDATE = 3;
	/* 下载结束 */
	private static final int NO_NEW_UPDATE = 4;
	/* 下载中 */
	private static final int DOWNLOAD = 5;
	/* 下载结束 */
	private static final int DOWNLOAD_FINISH = 6;
	/* 保存解析的XML信息 */
	HashMap<String, String> mHashMap = null;
	/* 下载保存路径 */
	private String mSavePath;

	/* 是否取消更新 */
	private boolean cancelUpdate = false;

	private Context mContext;
	private ProgressDialog pBar;

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
				case GETREMOTEXML_FINISH:
					break;
				case GETREMOTEXML_FAIL:
					break;
				case FIND_NEW_UPDATE:
					break;
				case NO_NEW_UPDATE:
					PromptUtil.show(mContext, "当前已是最新版!");
					break;
				// 正在下载
				case DOWNLOAD:
					break;
				case DOWNLOAD_FINISH:
					// 安装文件
					installApk();
					break;
				default:
					break;
			}
		};
	};

	public UpdateManagerService(Context context) {
		this.mContext = context;
	}

	/**
	 * 检测软件更新
	 *
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public void checkUpdate_Active(){

		new Thread(new Runnable() {

			@Override
			public void run() {
				if(getRemoteXML()){
					if (isUpdate()) {
						Looper.prepare();
						showNoticeDialog(); //显示提示对话框
						Looper.loop();
						Log.d("消息", "有新版本");
					} else {
						Log.d("消息", "已是最新版本");
					}
				}else {
					Log.d("消息", "获取远程版本配置文件失败");
				}
			}
		}).start();
//		Looper.prepare();
//		new ParseXmlService().start();
//		Looper.loop();
	}

	/**
	 * 检测软件更新
	 *
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public void checkUpdate_Passive(){

		new Thread(new Runnable() {

			@Override
			public void run() {
				if(getRemoteXML()){
					if (isUpdate()) {
						Looper.prepare();
						showNoticeDialog(); //显示提示对话框
						Looper.loop();
						Log.d("消息", "有新版本");
					} else {
						mHandler.sendEmptyMessage(NO_NEW_UPDATE);
						Log.d("消息", "已是最新版本");
					}
				}else {
					mHandler.sendEmptyMessage(GETREMOTEXML_FAIL);
					Log.d("消息", "获取远程版本配置文件失败");
				}
			}
		}).start();
//		Looper.prepare();
//		new ParseXmlService().start();
//		Looper.loop();
	}

	private boolean getRemoteXML(){
		// 将version.xml放在apk本地,则使用以下语句
		// InputStream inStream = ParseXmlService.class.getClassLoader().getResourceAsStream("http://imp.ctone.net/Frame/version.xml");
		// 把version.xml放到网络上,然后获取文件信息
		// 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析
		ParseXmlService service = new ParseXmlService();
		try {
			URL url = new URL("http://..._version.xml");
			HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
			InputStream inStream = urlConn.getInputStream();
			mHashMap = service.parseXml(inStream);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return false;
		}catch (IOException e) {
			e.printStackTrace();
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		if(mHashMap != null && mHashMap.size() != 0){
			return true;
		} else {
			return false;
		}

	}

	/**
	 * 检查软件是否有更新版本
	 *
	 * @return
	 * @throws IOException
	 */
	private boolean isUpdate(){
		// 获取当前软件版本
		int versionCode = getVersionCode(mContext);

		if (null != mHashMap) {
			int serviceCode = Integer.valueOf(mHashMap.get("versionCode"));
			Log.i("-----serviceCode", "" + serviceCode);
			Log.i("-----versionCode", "" + versionCode);

			// 版本判断
			if (serviceCode > versionCode) {
				return true;
			}
		} else {
			Log.i("-----null == mHashMap", "null == mHashMap");
		}

		return false;
	}

	/**
	 * 获取软件版本号
	 *
	 * @param context
	 * @return
	 */
	private int getVersionCode(Context context) {
		int versionCode = 0;
		try {
			// 获取软件版本号,对应AndroidManifest.xml下android:versionCode
			versionCode = context.getPackageManager().getPackageInfo("cn.jkjndoctor", 0).versionCode;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		return versionCode;
	}

	/**
	 * 显示软件更新对话框
	 */
	private void showNoticeDialog() {
		// 构造对话框
		AlertDialog.Builder builder = new Builder(mContext);
		builder.setTitle("有新版本需要更新");
		builder.setMessage(mHashMap.get("versionMsg").replace("\\n","\n"));
		// 更新
		builder.setPositiveButton("立即更新",
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 显示下载对话框
						showDownloadDialog();
					}
				});
		// 稍后更新
		builder.setNegativeButton("稍后更新",
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						
					}
				});
		//禁止返回键 如不需要可以不加
		builder.setCancelable(false);
		builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
			@Override
			public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
			{
				if (keyCode == KeyEvent.KEYCODE_SEARCH)
				{
					return true;
				}
				else
				{
					return false; //默认返回 false
				}
			}
		});
		Dialog noticeDialog = builder.create();
		noticeDialog.show();
	}

	/**
	 * 显示软件下载对话框
	 */
	private void showDownloadDialog() {
		pBar = new ProgressDialog(mContext);    //进度条,在下载的时候实时更新进度,提高用户友好度
		pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		pBar.setTitle("正在下载");
		pBar.setMessage("请稍候...");
		pBar.setProgress(0);
		pBar.show();
		pBar.setCancelable(false);
		pBar.setOnKeyListener(new DialogInterface.OnKeyListener() {
			@Override
			public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
			{
				if (keyCode == KeyEvent.KEYCODE_SEARCH)
				{
					return true;
				}
				else
				{
					return false; //默认返回 false
				}
			}
		});
		// 下载文件
		downloadApk();
	}

	/**
	 * 下载apk文件
	 */
	private void downloadApk() {
		cancelUpdate = false;
		// 启动新线程下载软件
		new downloadApkThread().start();
	}

	/**
	 * 下载文件线程
	 */
	private class downloadApkThread extends Thread {
		@Override
		public void run() {
			try {
				// 判断SD卡是否存在,并且是否具有读写权限
				if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
					// 获得存储卡的路径
					String sdpath = Environment.getExternalStorageDirectory() + "/";
					mSavePath = sdpath + "download";
					URL url = new URL(mHashMap.get("url"));
					// 创建连接
					HttpURLConnection conn = (HttpURLConnection) url.openConnection();
					conn.connect();
					// 获取文件大小
					int length = conn.getContentLength();
					pBar.setMax(length);

					// 创建输入流
					InputStream is = conn.getInputStream();

					File file = new File(mSavePath);
					// 判断文件目录是否存在
					if (!file.exists()) {
						file.mkdir();
					}
					File apkFile = new File(mSavePath, mHashMap.get("name"));
					FileOutputStream fos = new FileOutputStream(apkFile);
					int count = 0;
					// 缓存
					byte buf[] = new byte[1024];
					// 写入到文件中
					do {
						int numread = is.read(buf);
						count += numread;
						pBar.setProgress(count);       //这里就是关键的实时更新进度了!

//						// 更新进度
//						mHandler.sendEmptyMessage(DOWNLOAD);

						if (numread <= 0) {
							// 下载完成
							mHandler.sendEmptyMessage(DOWNLOAD_FINISH);
							break;
						}
						// 写入文件
						fos.write(buf, 0, numread);
					} while (!cancelUpdate);// 点击取消就停止下载.
					fos.close();
					is.close();
				}
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 取消下载对话框显示
			pBar.cancel();
		}
	}

	/**
	 * 安装APK文件
	 */
	private void installApk() {
		File apkfile = new File(mSavePath, mHashMap.get("name"));
		if (!apkfile.exists()) {
			return;
		}
		// 通过Intent安装APK文件
		Intent i = new Intent(Intent.ACTION_VIEW);
		i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
		mContext.startActivity(i);
	}

	/**
	 * 获取远程版本配置文件
	 */
	public class ParseXmlService
	{
		public HashMap<String, String> parseXml(InputStream inStream) throws Exception
		{
			HashMap<String, String> hashMap = new HashMap<String, String>();

			String tmpStr;

			// 实例化一个文档构建器工厂
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			// 通过文档构建器工厂获取一个文档构建器
			DocumentBuilder builder = factory.newDocumentBuilder();
			// 通过文档通过文档构建器构建一个文档实例
			Document document = builder.parse(inStream);
			//获取XML文件根节点
			Element root = document.getDocumentElement();
			//获得所有子节点
			NodeList childNodes = root.getChildNodes();
			for (int j = 0; j < childNodes.getLength(); j++)
			{
				//遍历子节点
				Node childNode = (Node) childNodes.item(j);
				if (childNode.getNodeType() == Node.ELEMENT_NODE)
				{
					Element childElement = (Element) childNode;
					//版本号
					if ("versionCode".equals(childElement.getNodeName()))
					{
						hashMap.put("versionCode",childElement.getFirstChild().getNodeValue());
					}
					//软件版本名
					else if (("versionName".equals(childElement.getNodeName())))
					{
						hashMap.put("versionName",childElement.getFirstChild().getNodeValue());
					}
					//更新信息
					else if (("versionMsg".equals(childElement.getNodeName())))
					{
						tmpStr = childElement.getFirstChild().getNodeValue();
						hashMap.put("versionMsg",tmpStr==null?"":tmpStr);
					}
					//文件名
					else if (("name".equals(childElement.getNodeName())))
					{
						hashMap.put("name",childElement.getFirstChild().getNodeValue());
					}
					//下载地址
					else if (("url".equals(childElement.getNodeName())))
					{
						hashMap.put("url",childElement.getFirstChild().getNodeValue());
					}
				}
			}
			return hashMap;
		}
	}

}

 

在调用检查更新的方法时

 

private UpdateManagerService mUpdateManagerService;
mUpdateManagerService = new UpdateManagerService(this);
mUpdataManagerService.checkUpdate_Active();

 

附:..version.xml样板

 

<update>
<versionCode>2</versionCode>
<versionName>1.0.2</versionName>
<versionMsg>1:优化界面显示。\n2:修改已知BUG。</versionMsg>
<name>xx-release-v1.0.2.apk</name>
<url>
http://.../xx-release-v1.0.2.apk
</url>
</update>