现在的位置: 首页 > 综合 > 正文

Android应用版本自动更新

2018年05月21日 ⁄ 综合 ⁄ 共 11085字 ⁄ 字号 评论关闭

珍惜生命,多做笔记奋斗

android应用如果是发布到豌豆荚/应用宝等平台上,是不需要自己做更新功能的,只需要在相关平台上更新就行.不过如果希望能够通过应用本身来检查和更新自己,就需要自己来开发相应的功能了.

很显然,为了完成这一功能,一个服务端是少不了的,我们需要下载服务端上面的新版本的apk文件来更新自己,同时为了检测版本信息,需要另一个文件来进行版本控制,这里我使用的是xml文件.

客户端先读取服务器上面配置的xml文件来比较自己和服务器上的版本号,如果有更高的版本,就可以下载服务器上面的apk文件了.

使用myeclipse新建一个工程,在WebRoot目录下新建一个xml文件,我命名为VersionInfo.xml,内容如下:

<pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>
<update>
        <version>8</version>
        <name>1.8</name>
	<url>http://106.2.199.70:7007/YDPlatform/Light.apk</url>
	<info> .版本号:1.8 \n .修复按下返回键没有退出的问题 \n.优化传感器算法 \n</info>
</update>


version标签内容为服务器上apk文件的版本,name标签为该版本的版本号,url为新的apk文件地址,域名也可以直接写为ip地址+端口号,我们需要在服务器上将这个apk文件放到WebRoot目录下.info标签写明了新版本的修改信息,其中\n用来换行.

Android应用程序通过Manifest.xml文件来配置应用版本信息:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="phy.light"
    android:versionCode="7"
    android:versionName="1.7" >

可以看到,我们在当前应用中的版本为7,而服务器上面配置的版本为8,有了更高的版本,我们就可以对应用进行更新了.

检测当前应用版本的方法如下:

<pre name="code" class="java">private int getVersion(){
		int version=0;
		try{
			version=context.getPackageManager().getPackageInfo("phy.light", 0).versionCode;
		
		}catch(Exception e){
			Log.d("NameNotFound","XX X");
			return 9999;
		}
		return version;		
	}

这里通过PackageManager来获取应用信息,"phy.light"为应用所在的包.需要注意的是异常的处理,如果没有检测到异常,必须要返回一个值,否则将会在后续流程中出现严重错误!这里我们直接返回9999,保证如果出现异常的话,应用仍然正常执行.因为正常情况下,应用的版本不会达到9999,程序将不会继续更新自己.

下面,我们需要到服务器上面读取新版本的信息,这里我直接下载了他的xml文件.

private InputStream DownXml(){
		URL url=null;
		InputStream stream=null;
		HttpURLConnection conn;
		try {			
			url=new URL("http://www.xxxxx.com/YDPlatform/VersionInfo.xml");
			conn=(HttpURLConnection) url.openConnection();
			stream=conn.getInputStream();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		return stream;
	}

这里仍然需要注意异常的处理,若不作任何处理,当产生异常的时候会导致应用崩溃,这里返回null即可.

接下来是解析读取的xml文件:

private void ParseXml(InputStream stream){
		HashMap<String,String> map=null;
		if(stream!=null){
			try{
				map=new HashMap<String,String>();
				DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
				DocumentBuilder builder=factory.newDocumentBuilder();
				Document document=builder.parse(stream);
				Element root=document.getDocumentElement();
				NodeList childs=root.getChildNodes();				
				for(int i=0;i<childs.getLength();i++){
					Node child=(Node)childs.item(i);
					if(child.getNodeType()==Node.ELEMENT_NODE){
						if("version".equals(child.getNodeName())){
							map.put("version", child.getFirstChild().getNodeValue());
						}else if("name".equals(child.getNodeName())){
							map.put("name", child.getFirstChild().getNodeValue());
						}else if("url".equals(child.getNodeName())){
							map.put("url", child.getFirstChild().getNodeValue());						
						}else if("info".equals(child.getNodeName())){
							map.put("info", child.getFirstChild().getNodeValue());
						};
					}					
				}
				stream.close();
				mHashMap=map;
				handler.sendEmptyMessage(READVERSION_FINISH);
			}catch(Exception e){}
		}		
	}	


也可以采用另一种方式,就是服务器端先自己读取VersionInfo.xml文件,然后使用servlet返回xml字符串,客户端再以Map的形式读取读取,不过这样就需要额外的使用 org.dom4j 的包.

 

 public static Map<String,String> parseXml(String xmlStr) throws DocumentException{
	    	Map<String,String> map=new HashMap<String,String>();
	    	Document dom=DocumentHelper.parseText(xmlStr);
	    	Element root=dom.getRootElement();
	    	List<Element> elementList=root.elements();
			for(Element e:elementList){
				map.put(e.getName(), e.getText());
			}
	    	return map;
	    }

注意网络通信需要使用后台线程来处理:

private class DownVersionInfo extends Thread{//读取版本信息		
		@Override
		public void run() {
			// TODO Auto-generated method stub			
			ParseXml(DownXml());			
		}		
	};

完成这些之后,就可以来比较版本信息了,如果有新版本,则弹出升级对话框,反对静默升级,流量也是RMB买的!

private int getNewVersion(HashMap<String,String> map){
		int version=0;
		if(null!=map){
			version=Integer.valueOf(map.get("version"));
		}
		return version;
	}
	
private void IsUpdate(int old_version,int new_version){//检查是否有新版本
		if(new_version>old_version){
			showDownloadDialog();
		}		
	}

创建升级对话框,这里发现"\n"居然无法使文字换行!经过多次试验(欲死欲活的折腾!),发现需要将其替换为"\\n":

private void showDownloadDialog(){//升级对话框
		AlertDialog.Builder builder=new Builder(context);
		builder.setTitle(context.getString(R.string.bt_update_title));
		String temp=mHashMap.get("info").toString().replace("\\n","\n");
		builder.setMessage(temp);
		builder.setPositiveButton(context.getString(R.string.bt_update1), new OnClickListener(){

			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub				
				t2=new DownLoadThread();
				t2.start();
				dialog.dismiss();
			}});
		builder.setNegativeButton(context.getString(R.string.bt_update2), new OnClickListener(){
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				dialog.dismiss();
			}});
		builder.create().show();
	}	
	

用户确认更新后,就可以愉快的下载apk了,这里产生的异常不必进行处理.

private class DownLoadThread extends Thread{//下载apk文件

		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			try{
				path=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+File.separator;				
				URL url=new URL(mHashMap.get("url"));
				HttpURLConnection conn=(HttpURLConnection)url.openConnection();
				conn.connect();
				int length=conn.getContentLength();
				InputStream stream=conn.getInputStream();
				File f=new File(path);
				if(!f.exists()){
					f.mkdirs();
				}
				File apk=new File(path,mHashMap.get("name"));			
				FileOutputStream fos=new FileOutputStream(apk);
				int count=0;
				byte buf[]=new byte[1024];
				do{
					if((count=stream.read(buf))!=-1){
						fos.write(buf,0,count);
						handler.sendEmptyMessage(DOWNLOAD);						
					}else{
						handler.sendEmptyMessage(DOWNLOAD_FINISH);
						break;
					}
				}while(!cancelUpdate);	
				stream.close();fos.close();
			}catch(Exception e){}
		}		
	}

这里因为文件比较小,没有做更多的处理,最好的做法是写一个通知,更新的时候显示出下载进度.下面是安装新的apk,旧的apk和新的apk签名必须一致,否则无法更新.

private void installAPK(){
		File apkfile=new File(path,mHashMap.get("name"));
		if(!apkfile.exists()){
			return;
		}	
		Intent i=new Intent(Intent.ACTION_VIEW);
		i.setDataAndType(Uri.parse("file://"+apkfile.toString()), "application/vnd.android.package-archive");
		context.startActivity(i);
	}

上面这个方法是从网络上找到的,经过查询,"application/vnd.android.package-archive"表示apk文件.

最后,通过一个handler来控制整个流程:

private Handler handler=new Handler(){

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			switch(msg.what){
			case DOWNLOAD:				
				break;
			case DOWNLOAD_FINISH:
				installAPK();
				break;
			case READVERSION_FINISH:
				IsUpdate(getVersion(), getNewVersion(mHashMap));
				break;
			}
		}
    	
    };  

当我们需要升级的时候,就可以初始化并启动一个DownVersionInfo线程了.

public void init(){
		t=new DownVersionInfo();
		t.start();		
	}

完整的应用端代码如下:

package phy.update;

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

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;

import phy.light.R;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class UpdateAPK {
	Context context;
	DownVersionInfo t;
	DownLoadThread t2;
	 /* 下载中 */
    private static final int DOWNLOAD = 1;
    /* 下载结束 */
    private static final int DOWNLOAD_FINISH = 2;
    private static final int READVERSION_FINISH = 3;  
    /* 保存解析的XML信息 */
    HashMap<String, String> mHashMap;
    /* 下载保存路径 */
    private String path;
    /* 是否取消更新 */
    private boolean cancelUpdate = false;
	
    private Handler handler=new Handler(){

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			switch(msg.what){
			case DOWNLOAD:				
				break;
			case DOWNLOAD_FINISH:
				installAPK();
				break;
			case READVERSION_FINISH:
				IsUpdate(getVersion(), getNewVersion(mHashMap));
				break;
			}
		}
    	
    };    
    
	public UpdateAPK(Context _context){
		context=_context;
	}	
	public void init(){
		t=new DownVersionInfo();
		t.start();		
	}
	
	private int getVersion(){
		int version=0;
		try{
			version=context.getPackageManager().getPackageInfo("phy.light", 0).versionCode;
		
		}catch(Exception e){
			Log.d("NameNotFound","XX X");
			return 9999;
		}
		return version;		
	}
	private int getNewVersion(HashMap<String,String> map){
		int version=0;
		if(null!=map){
			version=Integer.valueOf(map.get("version"));
		}
		return version;
	}
	private void IsUpdate(int old_version,int new_version){//检查是否有新版本
		if(new_version>old_version){
			showDownloadDialog();
		}		
	}
	
	private void showDownloadDialog(){//升级对话框
		AlertDialog.Builder builder=new Builder(context);
		builder.setTitle(context.getString(R.string.bt_update_title));
		String temp=mHashMap.get("info").toString().replace("\\n","\n");
		builder.setMessage(temp);
		builder.setPositiveButton(context.getString(R.string.bt_update1), new OnClickListener(){

			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub				
				t2=new DownLoadThread();
				t2.start();
				dialog.dismiss();
			}});
		builder.setNegativeButton(context.getString(R.string.bt_update2), new OnClickListener(){
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				dialog.dismiss();
			}});
		builder.create().show();
	}	
	
	
	
	private InputStream DownXml(){
		URL url=null;
		InputStream stream=null;
		HttpURLConnection conn;
		try {			
			url=new URL("http://www.xxxxx.com/YDPlatform/VersionInfo.xml");
			conn=(HttpURLConnection) url.openConnection();
			stream=conn.getInputStream();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		return stream;
	}
	
	private void ParseXml(InputStream stream){
		HashMap<String,String> map=null;
		if(stream!=null){
			try{
				map=new HashMap<String,String>();
				DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
				DocumentBuilder builder=factory.newDocumentBuilder();
				Document document=builder.parse(stream);
				Element root=document.getDocumentElement();
//				Log.d("root",""+root.getTagName());
				NodeList childs=root.getChildNodes();
				
				for(int i=0;i<childs.getLength();i++){
					Node child=(Node)childs.item(i);
					if(child.getNodeType()==Node.ELEMENT_NODE){
						if("version".equals(child.getNodeName())){
							map.put("version", child.getFirstChild().getNodeValue());
//							Log.d("version",""+child.getFirstChild().getNodeValue());
						}else if("name".equals(child.getNodeName())){
							map.put("name", child.getFirstChild().getNodeValue());
						}else if("url".equals(child.getNodeName())){
							map.put("url", child.getFirstChild().getNodeValue());						
						}else if("info".equals(child.getNodeName())){
							map.put("info", child.getFirstChild().getNodeValue());
						};
					}					
				}
				stream.close();
				mHashMap=map;
				handler.sendEmptyMessage(READVERSION_FINISH);
			}catch(Exception e){}
		}		
	}	
	private class DownVersionInfo extends Thread{//读取版本信息		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			ParseXml(DownXml());			
		}		
	};	
	private class DownLoadThread extends Thread{//下载apk文件

		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			try{
				path=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+File.separator;				
				URL url=new URL(mHashMap.get("url"));
				HttpURLConnection conn=(HttpURLConnection)url.openConnection();
				conn.connect();
				int length=conn.getContentLength();
				InputStream stream=conn.getInputStream();
				File f=new File(path);
				if(!f.exists()){
					f.mkdirs();
				}
				File apk=new File(path,mHashMap.get("name"));			
				FileOutputStream fos=new FileOutputStream(apk);
				int count=0;
				byte buf[]=new byte[1024];
				do{
					if((count=stream.read(buf))!=-1){
						fos.write(buf,0,count);
						handler.sendEmptyMessage(DOWNLOAD);						
					}else{
						handler.sendEmptyMessage(DOWNLOAD_FINISH);
						break;
					}
				}while(!cancelUpdate);	
				stream.close();fos.close();
			}catch(Exception e){}
		}		
	}
	private void installAPK(){
		File apkfile=new File(path,mHashMap.get("name"));
		if(!apkfile.exists()){
			return;
		}	
		Intent i=new Intent(Intent.ACTION_VIEW);
		i.setDataAndType(Uri.parse("file://"+apkfile.toString()), "application/vnd.android.package-archive");
		context.startActivity(i);
	}
	
}

抱歉!评论已关闭.