Android应用的版本升级
这是一个Android应用的自动更新功能,其中的版本控制使用的是XML文档,在服务器放一个版本控制文档version.xml从服务器获取版本内容,与本地的版本进行对比,更新使用的是Android系统提供的DownloadManager使用比较简单
首先是服务器的version.xml的格式
<?xml version="1.0" encoding="utf-8" ?>
<info>
<appName></appName>
<versionCode></versionCode>
<versionName></versionName>
<downUrl></downUrl>
<description></description>
</info>
其中的appName、versionCode、versionName不用多说,description中放的内容是新版本的提示信息如果有多条的话可以用英文的“;”做分隔符。
然后是读取服务器的版本信息
public class ReadServerVersion {
private String url;
private UpdateInfo updateInfo;
private static ReadServerVersion instance;
private ReadServerVersion(String url) {
this.url = url;
readVersionInfo();
}
public static ReadServerVersion getInstance(String url) {
if (instance == null) {
instance = new ReadServerVersion(url);
}
return instance;
}
/**
* @描述:读取服务器的版本内容
*/
public void readVersionInfo() {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
HttpURLConnection urlConn;
try {
urlConn = (HttpURLConnection) new URL(url).openConnection();
urlConn.setDoInput(true);
urlConn.setUseCaches(false);
urlConn.setConnectTimeout(3000);// 设置连接超时
InputStream in = urlConn.getInputStream();
updateInfo = ParseXml.parseXml(in);
urlConn.disconnect();
in.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
/**
* @描述:获取服务器的版本号
* @return
*/
public int getVersionCode() {
return updateInfo == null ? null : updateInfo.getVersionCode();
}
/**
* @描述:获取服务器的版本名
* @return
*/
public String getVersionName() {
return updateInfo == null ? null : updateInfo.getVersionName();
}
/**
* @描述:获取更新提示信息
* @return
*/
public StringBuilder getTipsInfo() {
return updateInfo == null ? null : updateInfo.getDescription();
}
/**
* @描述:获取下载地址
* @return
*/
public String getDownUrl() {
return updateInfo == null ? null : updateInfo.getDownUrl();
}
/**
* @描述:判断服务器是否有新版本
* @param context
* @return
*/
public boolean hasNewVersion(Context context) {
boolean hasNewVersion = false;
if (updateInfo == null) {
return hasNewVersion;
}
return updateInfo.getVersionCode() > getVersionCode(context) ? true
: false;
}
/**
* @描述:获取应用的版本信号
* @param context
* @return
*/
private int getVersionCode(Context context) {
int versionCode = 0;
try {
versionCode = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return versionCode;
}
}
UpdateInfo.java
public class UpdateInfo {
/** 应用名称 **/
private String appName;
/** 版本号 **/
private int versionCode;
/** 版本名 **/
private String versionName;
/** 更新地址 **/
private String downUrl;
/** 更新描述 **/
private String description;
/**
* @param versionCode
* @param versionName
* @param downUrl
* @param description
*/
public UpdateInfo(String appName, int versionCode, String versionName,
String downUrl, String description) {
super();
this.appName = appName;
this.versionCode = versionCode;
this.versionName = versionName;
this.downUrl = downUrl;
this.description = description;
}
/**
*
*/
public UpdateInfo() {
super();
}
public int getVersionCode() {
return versionCode;
}
public void setVersionCode(int versionCode) {
this.versionCode = versionCode;
}
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public String getDownUrl() {
return downUrl;
}
public void setDownUrl(String downUrl) {
this.downUrl = downUrl;
}
public StringBuilder getDescription() {
String[] tips = description.split(";");
StringBuilder updateContent = new StringBuilder();
for (int i = 0; i < tips.length; i++) {
if (i != tips.length - 1) {
updateContent.append(tips[i] + "\n");
} else {
updateContent.append(tips[i]);
}
}
return updateContent;
}
public void setDescription(String description) {
this.description = description;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
}
解析服从服务器读取的版本信息
public class ParseXml {
/**
* @描述:解析从服务器上读取到的版本文档,由于XML文件比较小,因此使用DOM方式进行解析
* @param in
* @return
* @throws Exception
*/
public static UpdateInfo parseXml(InputStream in) throws Exception {
UpdateInfo updateInfo = null;
// 实例化一个文档构建器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 通过文档构建器工厂获取一个文档构建器
DocumentBuilder builder = factory.newDocumentBuilder();
// 通过文档通过文档构建器构建一个文档实例
Document document = builder.parse(in);
// 获取XML文件根节点
Element root = document.getDocumentElement();
// 获得所有子节点
NodeList childNodes = root.getChildNodes();
String appName = null;
int versionCode = -1;
String versionName = null;
String downUrl = null;
String description = null;
for (int j = 0; j < childNodes.getLength(); j++) {
Node childNode = childNodes.item(j);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) childNode;
if ("appName".equals(childElement.getNodeName())) {
appName = childElement.getFirstChild().getNodeValue();
} else if ("versionCode".equals(childElement.getNodeName())) {
versionCode = Integer.parseInt(childElement.getFirstChild()
.getNodeValue());
} else if (("versionName".equals(childElement.getNodeName()))) {
versionName = childNode.getFirstChild().getNodeValue();
} else if (("downUrl".equals(childElement.getNodeName()))) {
downUrl = childElement.getFirstChild().getNodeValue();
} else if ("description".equals(childElement.getNodeName())) {
description = childElement.getFirstChild().getNodeValue();
}
}
}
updateInfo = new UpdateInfo(appName, versionCode, versionName, downUrl,
description);
return updateInfo;
}
}
UpdateUtils.java
public class UpdateUtils {
public static String PREFERENCE_NAME = "VersionUpdate";
/**
* put long preferences
*
* @param context
* @param key The name of the preference to modify
* @param value The new value for the preference
* @return True if the new values were successfully written to persistent storage.
*/
public static boolean putLong(Context context, String key, long value) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(key, value);
return editor.commit();
}
/**
* get long preferences
*
* @param context
* @param key The name of the preference to retrieve
* @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
* name that is not a long
* @see #getLong(Context, String, long)
*/
public static long getLong(Context context, String key) {
return getLong(context, key, -1);
}
/**
* get long preferences
*
* @param context
* @param key The name of the preference to retrieve
* @param defaultValue Value to return if this preference does not exist
* @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
* this name that is not a long
*/
public static long getLong(Context context, String key, long defaultValue) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getLong(key, defaultValue);
}
/**
* remove obj in preferences
* @param context
* @param key
* @return
*/
public static boolean removeSharedPreferenceByKey(Context context, String key){
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.remove(key);
return editor.commit();
}
}
更新的管理类
public class UpdateManager {
private static final String VERSIONURL = "这里是服务器的version.xml的路径";
private static UpdateManager manager;
private ReadServerVersion serverVersion;
private UpdateManager(){
}
public static UpdateManager getInstance(){
if(manager == null){
manager = new UpdateManager();
}
return manager;
}
public void init(){
serverVersion = ReadServerVersion.getInstance(VERSIONURL);
}
public void Update(Context context){
if(serverVersion.hasNewVersion(context)){
showDialog(context);
}
}
private void showDialog(final Context context){
Dialog dialog = new AlertDialog.Builder(context)
// .setIcon(R.drawable.ic_launcher)
.setTitle("更新提示")
.setMessage(serverVersion.getTipsInfo())
.setPositiveButton("后台更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
//下载应用
new ApkDownLoad(context, serverVersion.getDownUrl(), "应用名", "版本升级").execute();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
arg0.cancel();
}
})
.create();
dialog.show();
}
}
使用系统的DownloadManager下载apk文件
public class ApkDownLoad {
public static final String DOWNLOAD_FOLDER_NAME = "Download";
public static final String DOWNLOAD_FILE_NAME = "存放的文件名.apk";
public static final String APK_DOWNLOAD_ID = "apkDownloadId";
private Context context;
private String url;
private String notificationTitle;
private String notificationDescription;
private DownloadManager downloadManager;
private CompleteReceiver completeReceiver;
/**
* @param context
* @param url 下载apk的url
* @param notificationTitle 通知栏标题
* @param notificationDescription 通知栏描述
*/
public ApkDownLoad(Context context, String url, String notificationTitle,
String notificationDescription) {
super();
this.context = context;
this.url = url;
this.notificationTitle = notificationTitle;
this.notificationDescription = notificationDescription;
downloadManager = (DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE);
completeReceiver = new CompleteReceiver();
/** register download success broadcast **/
context.registerReceiver(completeReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
public void execute() {
//清除已下载的内容重新下载
long downloadId = UpdateUtils.getLong(context, APK_DOWNLOAD_ID);
if(downloadId != -1){
downloadManager.remove(downloadId);
UpdateUtils.removeSharedPreferenceByKey(context, APK_DOWNLOAD_ID);
}
Request request = new Request(Uri.parse(url));
//设置Notification中显示的文字
request.setTitle(notificationTitle);
request.setDescription(notificationDescription);
//设置可用的网络类型
request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);
//设置状态栏中显示Notification
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//不显示下载界面
request.setVisibleInDownloadsUi(false);
//设置下载后文件存放的位置
File folder = Environment.getExternalStoragePublicDirectory(DOWNLOAD_FOLDER_NAME);
if (!folder.exists() || !folder.isDirectory()) {
folder.mkdirs();
}
//设置下载文件的保存路径
request.setDestinationInExternalPublicDir(DOWNLOAD_FOLDER_NAME, DOWNLOAD_FILE_NAME);
//设置文件类型
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url));
request.setMimeType(mimeString);
//保存返回唯一的downloadId
UpdateUtils.putLong(context, APK_DOWNLOAD_ID, downloadManager.enqueue(request));
}
class CompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/**
* get the id of download which have download success, if the id is my id and it's status is successful,
* then install it
**/
long completeDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
long downloadId = UpdateUtils.getLong(context, APK_DOWNLOAD_ID);
if (completeDownloadId == downloadId) {
// if download successful
if (queryDownloadStatus(downloadManager, downloadId) == DownloadManager.STATUS_SUCCESSFUL) {
//clear downloadId
UpdateUtils.removeSharedPreferenceByKey(context, APK_DOWNLOAD_ID);
//unregisterReceiver
context.unregisterReceiver(completeReceiver);
//install apk
String apkFilePath = new StringBuilder(Environment.getExternalStorageDirectory().getAbsolutePath())
.append(File.separator).append(DOWNLOAD_FOLDER_NAME).append(File.separator)
.append(DOWNLOAD_FILE_NAME).toString();
install(context, apkFilePath);
}
}
}
};
/** 查询下载状态 */
public static int queryDownloadStatus(DownloadManager downloadManager, long downloadId){
int result = -1;
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = null;
try {
c = downloadManager.query(query);
if (c != null && c.moveToFirst()) {
result = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (c != null) {
c.close();
}
}
return result;
}
/**
* install app
*
* @param context
* @param filePath
* @return whether apk exist
*/
public static boolean install(Context context, String filePath) {
Intent i = new Intent(Intent.ACTION_VIEW);
File file = new File(filePath);
if (file != null && file.length() > 0 && file.exists() && file.isFile()) {
i.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
return true;
}
return false;
}
}
在使用的过程中首先需要在application中调用UpdateManger的init方法,然后在需要进行检测的地方调用UpdateManger的update方法即可。