Android程序的版本检测与更新



做个网站的安卓客户端,用户安装到自己手机上,如果我出了新版本怎么办呢?要有版本更新功能。

本来版本检测最好可以自动进行。但如果每次开启程序,都要先检测一轮,是一种浪费,毕竟版本更新是小概率的事情。或许可以程序开启的时候,判断一下时间,单日就检测,双日就不检测,或者随机什么的,降低一下检测的频率?

我采取的做法是将检测功能做到了菜单上,用户有需要,就手动打开自己检测一下。反正我们这个是网站客户端,有版本更新,在网站上发个通告就行了。

版本检测与更新有以下几个关键步骤:

1、检测有无新版本

2、下载新版本

3、安装替换新版本

我处理的方案是

1、在assets文件夹新增一个文件:ver.cfg,记录版本信息,纯文本格式,内容只有一句话:

  1. Version=1.0  
Version=1.0

这个会随安装包装到用户的手机上

然后在网站里面,设置一XML文件ver_apk.xml,内容也只有这么一点:

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <string>1.0</string>  
<?xml version="1.0" encoding="utf-8" ?>
<string>1.0</string>

检测的时候,就先访问网站的这个XML,得到最新版本号,然后与手机上的ver.cfg文件里记录的进行比对,不同的话就可以认为存在新版本,提示进行更新。

2、下载的话就是直接下载的,我还不知道怎么弄断点续传

3、安装替换,关键在于签名。就是每个版本的签名要保持一致。否则新的无法替换旧的,提示安装未完成。

------------------- 天气太冷,咯咯咯 ------------------------------------

这个功能做在菜单上,触发代码如下:

  1. //==========================================================================  
  2. // 菜单  
  3. //==========================================================================      
  4. private static final String urlApk = "http://3g.***.com/tool/***.apk";  
  5. private static final String urlVer = "http://3g.***.com/tool/ver_apk.xml";      
  6. @Override  
  7. public boolean onCreateOptionsMenu(Menu menu) {  
  8.     menu.add(Menu.NONE, Menu.FIRST + 15"检测更新").setIcon(  
  9.     android.R.drawable.ic_menu_upload);  
  10.     menu.add(Menu.NONE,Menu.FIRST+2,4,"退出").setIcon(android.R.drawable.ic_lock_power_off);  
  11.     return true;  
  12. }  
  13. @Override  
  14. public boolean onOptionsItemSelected(MenuItem item) {  
  15.     switch (item.getItemId()) {  
  16.         case Menu.FIRST + 1:  
  17.             Toast.makeText(this"正在检测版本", Toast.LENGTH_LONG).show();  
  18.             UpdateVer uv = new UpdateVer(urlApk,urlVer,MainActivity.this);  
  19.             uv.checkVer();  
  20.             break;  
  21.         case Menu.FIRST + 2:  
  22.             confirmExit();  
  23.             break;  
  24.     }  
  25.     return false;  
  26. }  
//==========================================================================
// 菜单
//==========================================================================    
private static final String urlApk = "http://3g.***.com/tool/***.apk";
private static final String urlVer = "http://3g.***.com/tool/ver_apk.xml";    
@Override
public boolean onCreateOptionsMenu(Menu menu) {
	menu.add(Menu.NONE, Menu.FIRST + 1, 5, "检测更新").setIcon(
	android.R.drawable.ic_menu_upload);
	menu.add(Menu.NONE,Menu.FIRST+2,4,"退出").setIcon(android.R.drawable.ic_lock_power_off);
	return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
	switch (item.getItemId()) {
        case Menu.FIRST + 1:
            Toast.makeText(this, "正在检测版本", Toast.LENGTH_LONG).show();
        	UpdateVer uv = new UpdateVer(urlApk,urlVer,MainActivity.this);
        	uv.checkVer();
            break;
        case Menu.FIRST + 2:
            confirmExit();
            break;
    }
    return false;
}

检测更新因为代码比较多,写成一个类进行封装

UpdateVer.java

  1. package android.***;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.AlertDialog;  
  5. import android.app.Dialog;  
  6. import android.app.ProgressDialog;  
  7. import android.content.Context;  
  8. import android.content.DialogInterface;  
  9. import android.content.Intent;  
  10. import android.net.Uri;  
  11. import android.os.AsyncTask;  
  12. import android.util.Log;  
  13. import android.webkit.URLUtil;  
  14. import android.widget.Toast;  
  15. import java.io.File;  
  16. import java.io.FileNotFoundException;  
  17. import java.io.FileOutputStream;  
  18. import java.io.IOException;  
  19. import java.io.InputStream;  
  20. import java.net.MalformedURLException;  
  21. import java.net.URL;  
  22. import java.net.URLConnection;  
  23. import java.util.Properties;  
  24. import org.xml.sax.InputSource;  
  25. import java.text.SimpleDateFormat;  
  26. import java.util.Date;  
  27.   
  28. public class UpdateVer extends Activity{  
  29.     private static final String TAG = "DOWNLOADAPK";  
  30.     private String PastVersion;  
  31.     private String NowVersion;  
  32.     public  ProgressDialog pBar;   
  33.     private String currentFilePath = "";   
  34.     private String fileEx="";  
  35.     private String fileNa="";  
  36.     private String strURL="";  
  37.     private String VersionUri ="";  
  38.     private Context mContext;  
  39.     private final String fileVer = "ver.cfg";  
  40.   
  41.     public UpdateVer(String urlapk,String urlver,final Context context){  
  42.         SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");  
  43.         String ver = "?ver=" + df.format(new Date());//主要是避开手机的缓存  
  44.         strURL = urlapk + ver;  
  45.         VersionUri = urlver + ver;  
  46.         mContext = context;  
  47.     }  
  48.     public void checkVer() {  
  49.         // 解析Version网页,获取版本号  
  50.         getVersionxml(VersionUri);  
  51.     }  
  52.   
  53.     private void compareVer()   {  
  54.         load();  
  55.           
  56.         //当有最新版本的时候  
  57.         if(PastVersion != null && !PastVersion.equals(NowVersion)){   
  58.             Dialog dialog = new AlertDialog.Builder(mContext).setTitle("系统更新")         
  59.             .setMessage(String.format("发现新版本%s,目前版本为%s,请更新!",NowVersion,PastVersion))// 设置内容  
  60.             // 设置确定按钮  
  61.             .setPositiveButton("确定"  
  62.                 ,new DialogInterface.OnClickListener() {         
  63.                     @Override  
  64.                     public void onClick(DialogInterface dialog,         
  65.                                  int which) {         
  66.                                     pBar = new ProgressDialog(mContext);  
  67.                                     pBar.setTitle("正在下载");         
  68.                                     pBar.setMessage("请稍候...");  
  69.                                     pBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);              
  70.                                     fileEx = strURL.substring(strURL.lastIndexOf(".") + 1,strURL.length()).toLowerCase();  
  71.                                     fileEx = fileEx.substring(0,fileEx.lastIndexOf("?"));  
  72.                                     fileNa = strURL.substring(strURL.lastIndexOf("/") + 1,strURL.lastIndexOf("."));  
  73.                                     getFile(strURL);       
  74.                                 }  
  75.                     }).setNegativeButton("取消",           
  76.                             new DialogInterface.OnClickListener() {  
  77.                                 @Override  
  78.                                 public void onClick(DialogInterface dialog,         
  79.                                         int whichButton) {  
  80.                                             // 点击"取消"按钮之后退出程序           
  81.                                         }  
  82.                             }).create();// 创建  
  83.              // 显示对话框  
  84.              dialog.show();  
  85.         }  
  86.         else{  
  87.             Toast.makeText(mContext, String.format("当前为最新版本%s",PastVersion), Toast.LENGTH_LONG).show();  
  88.         }  
  89.     }  
  90.     private void getFile(final String strPath)   
  91.     {   
  92.         pBar.show();  
  93.         try{   
  94.             if (strPath.equals(currentFilePath) ){   
  95.                 getDataSource(strPath);    
  96.             }  
  97.             currentFilePath = strPath;        
  98.             Runnable r = new Runnable(){     
  99.                 @Override  
  100.                 public void run()   
  101.                 {     
  102.                     try{   
  103.                         getDataSource(strPath);   
  104.                     }   
  105.                     catch (Exception e){  
  106.                         Log.e(TAG, e.getMessage(), e);   
  107.                     }   
  108.                 }   
  109.             };     
  110.             new Thread(r).start();   
  111.         }   
  112.         catch(Exception e){   
  113.             e.printStackTrace();   
  114.         }  
  115.     }  
  116.     /*取得远程文件*/   
  117.     private void getDataSource(String strPath) throws Exception {  
  118.         if (!URLUtil.isNetworkUrl(strPath)) {  
  119.             Log.d("Tag","error");  
  120.         }   
  121.         else {   
  122.             /*取得URL*/  
  123.             URL myURL = new URL(strPath);   
  124.             /*建立联机*/  
  125.             URLConnection conn = myURL.openConnection();     
  126.             conn.connect();     
  127.             /*InputStream 下载文件*/  
  128.             InputStream is = conn.getInputStream();     
  129.             if (is == null) {    
  130.                 Log.d("tag","error");  
  131.                 throw new RuntimeException("没有读取到文件内容");   
  132.             }   
  133.             /*建立临时文件*/   
  134.             File myTempFile = File.createTempFile(fileNa, "." + fileEx);   
  135.             myTempFile.getAbsolutePath();   
  136.             /*将文件写入临时盘*/   
  137.             FileOutputStream fos = new FileOutputStream(myTempFile);   
  138.             byte buf[] = new byte[128];     
  139.             do{     
  140.                 int numread = is.read(buf);     
  141.                 if (numread <= 0) {   
  142.                     break;   
  143.                 }   
  144.                 fos.write(buf, 0, numread);     
  145.             }while (true);    
  146.               
  147.             /*打开文件进行安装*/  
  148.             openFile(myTempFile);  
  149.             try {   
  150.                 is.close();   
  151.             }  
  152.             catch (Exception ex){   
  153.                 Log.d("Tag","error");  
  154.                 Log.e(TAG, "error: " + ex.getMessage(), ex);   
  155.             }  
  156.         }  
  157.     }   
  158.     /* 在手机上打开文件 */  
  159.     private void openFile(File f) {  
  160.         pBar.cancel();  
  161.         Intent intent = new Intent();  
  162.         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  163.         intent.setAction(android.content.Intent.ACTION_VIEW);  
  164.       
  165.         /* 调用getMIMEType()来取得MimeType */  
  166.         String type = getMIMEType(f);  
  167.         /* 设定intent的file与MimeType */  
  168.         intent.setDataAndType(Uri.fromFile(f),type);  
  169.         mContext.startActivity(intent);   
  170.     }  
  171.     /* 判断文件MimeType的method */  
  172.     private String getMIMEType(File f) {   
  173.         String type = "";  
  174.         String fName = f.getName();  
  175.         /* 取得扩展名 */  
  176.         String end = fName.substring(fName.lastIndexOf(".")+1,fName.length()).toLowerCase();   
  177.       
  178.         /* 按扩展名的类型决定MimeType */  
  179.         if(end.equals("m4a")  
  180.                 || end.equals("mp3")  
  181.                 || end.equals("mid")  
  182.                 || end.equals("xmf")  
  183.                 || end.equals("ogg")  
  184.                 || end.equals("wav")){  
  185.             type = "audio";   
  186.         }  
  187.         else if(end.equals("3gp") || end.equals("mp4")){  
  188.             type = "video";  
  189.         }  
  190.         else if(end.equals("jpg")  
  191.                 || end.equals("gif")  
  192.                 || end.equals("png")  
  193.                 || end.equals("jpeg")  
  194.                 || end.equals("bmp")){  
  195.             type = "image";  
  196.         }  
  197.         else if(end.equals("apk")){   
  198.             /* android.permission.INSTALL_PACKAGES */   
  199.             type = "application/vnd.android.package-archive";   
  200.         }   
  201.         else{  
  202.             type = "*";  
  203.         }  
  204.         /*如果无法直接打开,就跳出软件清单给使用者选择 */  
  205.         if(!end.equals("apk")){   
  206.             type += "/*";    
  207.         }   
  208.         return type;    
  209.     }  
  210.     private void getVersionxml(String resourceUrl){  
  211.         GetVer gv = new GetVer();  
  212.         gv.execute(resourceUrl);  
  213.     }  
  214.     private boolean load(){  
  215.         Properties properties = new Properties();  
  216.         try{  
  217.             InputStream stream = mContext.getAssets().open(fileVer);  
  218.             //FileInputStream stream = mContext.openFileInput(fileVer);  
  219.             //读取文件内容  
  220.             properties.load(stream);      
  221.         }  
  222.         catch (FileNotFoundException e){  
  223.             return false;  
  224.         }  
  225.         catch(IOException e){  
  226.             return false;  
  227.         }  
  228.         catch(Exception e){  
  229.             return false;  
  230.         }  
  231.         PastVersion = String.valueOf(properties.get("Version").toString());   
  232.         return true;  
  233.     }  
  234.       
  235.     //==========================================================================  
  236.     // GetVer  
  237.     //==========================================================================      
  238.     class GetVer extends AsyncTask<String, Integer, String> {  
  239.         @Override    
  240.         protected String doInBackground(String... urlVer) {    
  241.             String db = null;  
  242.             URL url = null;  
  243.                   
  244.             try {  
  245.                 url = new URL(urlVer[0]);  
  246.             }   
  247.             catch (MalformedURLException e) {  
  248.                 e.printStackTrace();  
  249.             }    
  250.             InputSource is = null;   
  251.             try {   
  252.                 is = new InputSource(url.openStream());   
  253.                 is.setEncoding("UTF-8");     
  254.                 db = SAXGetVersionService.readRssXml(is);  
  255.             }   
  256.             catch (Exception e) {  
  257.                 e.printStackTrace();  
  258.             }   
  259.             return db;        
  260.         }    
  261.         @Override    
  262.         protected void onCancelled() {    
  263.             super.onCancelled();    
  264.         }    
  265.         @Override    
  266.         protected void onPostExecute(String result) {   
  267.             NowVersion = result;  
  268.             compareVer();  
  269.         }    
  270.     }     
  271. }  
package android.***;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import android.webkit.URLUtil;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Properties;
import org.xml.sax.InputSource;
import java.text.SimpleDateFormat;
import java.util.Date;

public class UpdateVer extends Activity{
    private static final String TAG = "DOWNLOADAPK";
    private String PastVersion;
    private String NowVersion;
	public 	ProgressDialog pBar; 
	private String currentFilePath = ""; 
	private String fileEx="";
	private String fileNa="";
	private String strURL="";
	private String VersionUri ="";
	private Context mContext;
	private final String fileVer = "ver.cfg";

	public UpdateVer(String urlapk,String urlver,final Context context){
		SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
		String ver = "?ver=" + df.format(new Date());//主要是避开手机的缓存
		strURL = urlapk + ver;
		VersionUri = urlver + ver;
		mContext = context;
	}
	public void checkVer() {
		// 解析Version网页,获取版本号
		getVersionxml(VersionUri);
	}

	private void compareVer()	{
		load();
		
		//当有最新版本的时候
		if(PastVersion != null && !PastVersion.equals(NowVersion)){	
			Dialog dialog = new AlertDialog.Builder(mContext).setTitle("系统更新")       
	        .setMessage(String.format("发现新版本%s,目前版本为%s,请更新!",NowVersion,PastVersion))// 设置内容
	        // 设置确定按钮
	        .setPositiveButton("确定"
        		,new DialogInterface.OnClickListener() {       
                	@Override
					public void onClick(DialogInterface dialog,       
                                 int which) {       
		                            pBar = new ProgressDialog(mContext);
		                            pBar.setTitle("正在下载");       
		                            pBar.setMessage("请稍候...");
		                            pBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);            
		                            fileEx = strURL.substring(strURL.lastIndexOf(".") + 1,strURL.length()).toLowerCase();
		                            fileEx = fileEx.substring(0,fileEx.lastIndexOf("?"));
		                            fileNa = strURL.substring(strURL.lastIndexOf("/") + 1,strURL.lastIndexOf("."));
		                            getFile(strURL);     
                         		}
                    }).setNegativeButton("取消",         
                    		new DialogInterface.OnClickListener() {
                    			@Override
								public void onClick(DialogInterface dialog,       
                    					int whichButton) {
                    						// 点击"取消"按钮之后退出程序         
                    					}
                    		}).create();// 创建
			 // 显示对话框
			 dialog.show();
		}
		else{
			Toast.makeText(mContext, String.format("当前为最新版本%s",PastVersion), Toast.LENGTH_LONG).show();
		}
	}
	private void getFile(final String strPath) 
	{ 
		pBar.show();
	    try{ 
	    	if (strPath.equals(currentFilePath) ){ 
	    		getDataSource(strPath);  
	    	}
	    	currentFilePath = strPath;      
	    	Runnable r = new Runnable(){   
	    		@Override
				public void run() 
	    		{   
	    			try{ 
	    				getDataSource(strPath); 
	    			} 
	    			catch (Exception e){
	    				Log.e(TAG, e.getMessage(), e); 
	    			} 
	    		} 
	    	};   
	    	new Thread(r).start(); 
	    } 
	    catch(Exception e){ 
	    	e.printStackTrace(); 
	    }
	}
	/*取得远程文件*/ 
	private void getDataSource(String strPath) throws Exception {
		if (!URLUtil.isNetworkUrl(strPath))	{
			Log.d("Tag","error");
		} 
		else { 
		    /*取得URL*/
			URL myURL = new URL(strPath); 
			/*建立联机*/
			URLConnection conn = myURL.openConnection();   
			conn.connect();   
			/*InputStream 下载文件*/
			InputStream is = conn.getInputStream();   
			if (is == null) {  
				Log.d("tag","error");
				throw new RuntimeException("没有读取到文件内容"); 
			} 
			/*建立临时文件*/ 
			File myTempFile = File.createTempFile(fileNa, "." + fileEx); 
			myTempFile.getAbsolutePath(); 
			/*将文件写入临时盘*/ 
			FileOutputStream fos = new FileOutputStream(myTempFile); 
			byte buf[] = new byte[128];   
			do{   
				int numread = is.read(buf);   
				if (numread <= 0) { 
					break; 
				} 
				fos.write(buf, 0, numread);   
			}while (true);  
			
			/*打开文件进行安装*/
			openFile(myTempFile);
			try	{ 
				is.close(); 
			}
			catch (Exception ex){ 
				Log.d("Tag","error");
				Log.e(TAG, "error: " + ex.getMessage(), ex); 
			}
		}
	} 
	/* 在手机上打开文件 */
	private void openFile(File f) {
		pBar.cancel();
		Intent intent = new Intent();
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.setAction(android.content.Intent.ACTION_VIEW);
    
		/* 调用getMIMEType()来取得MimeType */
		String type = getMIMEType(f);
		/* 设定intent的file与MimeType */
		intent.setDataAndType(Uri.fromFile(f),type);
		mContext.startActivity(intent); 
	}
	/* 判断文件MimeType的method */
	private String getMIMEType(File f) { 
	    String type = "";
	    String fName = f.getName();
	    /* 取得扩展名 */
	    String end = fName.substring(fName.lastIndexOf(".")+1,fName.length()).toLowerCase(); 
	
	    /* 按扩展名的类型决定MimeType */
	    if(end.equals("m4a")
	    		|| end.equals("mp3")
	    		|| end.equals("mid")
	    		|| end.equals("xmf")
	    		|| end.equals("ogg")
	    		|| end.equals("wav")){
	    	type = "audio"; 
	    }
	    else if(end.equals("3gp") || end.equals("mp4")){
	    	type = "video";
	    }
	    else if(end.equals("jpg")
	    		|| end.equals("gif")
	    		|| end.equals("png")
	    		|| end.equals("jpeg")
	    		|| end.equals("bmp")){
	    	type = "image";
	    }
	    else if(end.equals("apk")){ 
	    	/* android.permission.INSTALL_PACKAGES */ 
	    	type = "application/vnd.android.package-archive"; 
	    } 
	    else{
	    	type = "*";
	    }
	    /*如果无法直接打开,就跳出软件清单给使用者选择 */
	    if(!end.equals("apk")){ 
	    	type += "/*";  
	    } 
	    return type;  
	}
	private void getVersionxml(String resourceUrl){
		GetVer gv = new GetVer();
		gv.execute(resourceUrl);
 	}
	private boolean load(){
		Properties properties = new Properties();
		try{
			InputStream stream = mContext.getAssets().open(fileVer);
			//FileInputStream stream = mContext.openFileInput(fileVer);
			//读取文件内容
			properties.load(stream);	
		}
		catch (FileNotFoundException e){
			return false;
		}
		catch(IOException e){
			return false;
		}
		catch(Exception e){
			return false;
		}
		PastVersion = String.valueOf(properties.get("Version").toString());	
		return true;
	}
	
	//==========================================================================
	// GetVer
	//==========================================================================	
	class GetVer extends AsyncTask<String, Integer, String> {
		@Override  
	    protected String doInBackground(String... urlVer) {  
			String db = null;
		    URL url = null;
		    	
			try {
				url = new URL(urlVer[0]);
			} 
			catch (MalformedURLException e) {
				e.printStackTrace();
			}  
			InputSource is = null; 
			try { 
				is = new InputSource(url.openStream()); 
				is.setEncoding("UTF-8");   
				db = SAXGetVersionService.readRssXml(is);
			} 
			catch (Exception e) {
				e.printStackTrace();
			} 
			return db;		
	    }  
	    @Override  
	    protected void onCancelled() {  
	        super.onCancelled();  
	    }  
	    @Override  
	    protected void onPostExecute(String result) { 
			NowVersion = result;
	    	compareVer();
	    }  
	}	
}


AndroidManifest.xml要加上几句

  1.    <uses-permission android:name="android.permission.INTERNET" />  
  2.    <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>  
  3. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>  
  4. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
  5.    <uses-permission android:name="android.permission.RESTART_PACKAGES" />  
  6.    <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
  7.    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  8.    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />  
  9.    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值