实现android应用自动更新功能

在android应用开发中,需要考虑到以后新的应用更新,所以需要实现一个自动更新功能。当用户打开应用的时候,就获得服务器上的更新文件(版本号,以及下载地址…),然后和现在使用的APP比较,如果版本低了,就提示是否下载更新,如果版本一样就直接跳过。

实现步骤:

第一步:创建一个com.jhonse.update包,此包下创建一个UpdateManager.java类。

20141211212616

UpdateManager类的代码如下:

package com.jhonse.update;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.jhonse.aaa515.R;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;

public class UpdateManager {
	/* 下载中 */
	private static final int DOWNLOAD = 1;
	/* 下载结束 */
	private static final int DOWNLOAD_FINISH = 2;
	/* 保存解析的XML信息 */
	HashMap<String, String> mHashMap;
	/* 下载保存路径 */
	private String mSavePath;
	/* 记录进度条数量 */
	private int progress;
	/* 是否取消更新 */
	private boolean cancelUpdate = false;

	private Context mContext;
	/* 更新进度条 */
	private ProgressBar mProgress;
	private Dialog mDownloadDialog;

	private String jURL = "http://blog.jhonse.com/version.xml";

	@SuppressLint("HandlerLeak")
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			// 正在下载
			case DOWNLOAD:
				// 设置进度条位置
				mProgress.setProgress(progress);
				break;
			case DOWNLOAD_FINISH:
				// 安装文件
				installApk();
				break;
			default:
				break;
			}
		};
	};

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

	/**
	 * 检测软件更新
	 */
	public void checkUpdate() {

		if (isUpdate()) {
			// 显示提示对话框
			showNoticeDialog();
		} 
	}

	/**
	 * 检查软件是否有更新版本
	 * 
	 * @return
	 * 
	 */
	private boolean isUpdate() {
		// 获取当前软件版本
		int versionCode = getVersionCode(mContext);
		// 把version.xml放到网络上,然后获取文件信息

		/*
		 * InputStream inStream = ParseXmlService.class.getClassLoader()
		 * .getResourceAsStream("version.xml");
		 * 
		 * // 解析网络xml进行更新软件 /*URL url = null; try { url = new URL(jURL); } catch
		 * (MalformedURLException e1) { e1.printStackTrace(); }
		 * HttpURLConnection conn = null; try { conn = (HttpURLConnection)
		 * url.openConnection(); } catch (IOException e1) {
		 * e1.printStackTrace(); } conn.setConnectTimeout(5000);
		 * conn.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
		 * try { conn.setRequestMethod("GET"); } catch (ProtocolException e3) {
		 * e3.printStackTrace(); } InputStream is = null; try { is =
		 * conn.getInputStream(); } catch (IOException e1) {
		 * e1.printStackTrace(); }
		 */
		// 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析

		InputStream inStream = null;

		try {

			HttpGet urlGet = new HttpGet(jURL); // 远程http地址

			HttpResponse httpResponse = new DefaultHttpClient().execute(urlGet);

			inStream = httpResponse.getEntity().getContent();

		} catch (IOException e) {
			e.printStackTrace();
		}

		ParseXmlService service = new ParseXmlService();
		try {
			mHashMap = service.parseXml(inStream);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (null != mHashMap) {
			int serviceCode = Integer.valueOf(mHashMap.get("version"));
			// 版本判断
			if (serviceCode > versionCode) {
				return true;
			}
		}
		return false;
	}

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

	/**
	 * 显示软件更新对话框
	 */
	private void showNoticeDialog() {
		// 构造对话框
		AlertDialog.Builder builder = new Builder(mContext);
		builder.setTitle(R.string.soft_update_title);
		builder.setMessage(R.string.soft_update_info);
		// 更新
		builder.setPositiveButton(R.string.soft_update_updatebtn,
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 显示下载对话框
						showDownloadDialog();
					}
				});
		// 稍后更新
		builder.setNegativeButton(R.string.soft_update_later,
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
					}
				});
		Dialog noticeDialog = builder.create();
		noticeDialog.show();
	}

	/**
	 * 显示软件下载对话框
	 */
	private void showDownloadDialog() {
		// 构造软件下载对话框
		AlertDialog.Builder builder = new Builder(mContext);
		builder.setTitle(R.string.soft_updating);
		// 给下载对话框增加进度条
		final LayoutInflater inflater = LayoutInflater.from(mContext);
		View v = inflater.inflate(R.layout.softupdate_progress, null);
		mProgress = (ProgressBar) v.findViewById(R.id.update_progress);
		builder.setView(v);
		// 取消更新
		builder.setNegativeButton(R.string.soft_update_cancel,
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 设置取消状态
						cancelUpdate = true;
					}
				});
		mDownloadDialog = builder.create();
		mDownloadDialog.show();
		// 现在文件
		downloadApk();
	}

	/**
	 * 下载apk文件
	 */
	private void downloadApk() {
		// 启动新线程下载软件
		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();
					// 创建输入流
					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;
						// 计算进度条位置
						progress = (int) (((float) count / length) * 100);
						// 更新进度
						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();
			}
			// 取消下载对话框显示
			mDownloadDialog.dismiss();
		}
	};

	/**
	 * 安装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);
	}

}

 第二步: 创建xml解析类ParseXmlService.java

因为从服务器获取的是xml文件数据,所以需要对xml数据进行解析。

代码如下:

package com.jhonse.update;

import java.io.InputStream;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ParseXmlService
{
	public HashMap<String, String> parseXml(InputStream inStream) throws Exception
	{
		HashMap<String, String> hashMap = new HashMap<String, String>();
		
		// 实例化一个文档构建器工厂
		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 ("version".equals(childElement.getNodeName()))
				{
					hashMap.put("version",childElement.getFirstChild().getNodeValue());
				}
				//软件名称
				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;
	}
}

 第三步: 在应用初始完之后,来检查是否需要更新

UpdateManager updateManager = new UpdateManager(aaa515.this);
updateManager.checkUpdate();

在此调用中,可能会报错,提示线程问题。解决方法如下:

if (android.os.Build.VERSION.SDK_INT > 9) {
	StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
	StrictMode.setThreadPolicy(policy);
}
UpdateManager updateManager = new UpdateManager(aaa515.this);
updateManager.checkUpdate();

第四步:添加程序所用的资源与权限

创建layout文件softupdate_progress.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">
	<ProgressBar
		android:id="@id/update_progress"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		style="?android:attr/progressBarStyleHorizontal" />
</LinearLayout>

创建string文件strings.xml,代码如下:

<?xml version='1.0' encoding='utf-8'?>
<resources>
    <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>
    <string name="loading">加载中</string>
</resources>

创建ID文件ids.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="update_progress"/>
</resources>

添加网络访问权限

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

 第五步: 在服务器上创建xml文件version.xml,提供给andriod应用访问。

<update>
	<version>版本号</version>
	<name>****.apk</name>
	<url>http://blog.jhonse.com/****.apk</url>
</update>

 第六步: 效果

20141211215732

20141211215750

Author: jhonse

Jhonse技术博客: http://blog.jhonse.com

Leave a Reply

Your email address will not be published. Required fields are marked *