Android软件检测更新

一个应用软件的诞生,必将带来的一个问题就是版本的更新问题。今天我就提供一个我个人的方法吧

1.问题分析

  版本的更新问题可从两个方面进行考虑:一. 如何获取版本号,与当前版本号进行比对的问题。二. 如何下载最近版本,并安装的问题。

2.解决之道

  解决一:我之前做的一个应用软件是通过 Web services 来进行数据的传输,当然版本号是数据也是通过这个来传输的。(本文主要介绍终端的功能实现)

我们在后台服务器段有一个固定网址放着我们的最新版本号(XML文件),然后我们通过网络,去获取这样版本号,并且与当前的版本号进行对比,从而得知是

否需要更新。说白了就是解析XML文件来获取版本号,本文采用了SAX解析器。

  解决二:当我们发现有最新版本的时候,我们就需要通过网络,远程下载我们的android应用程序到手机中,并打开 application installer 来安装软件。下

载的时候我们使用 java.net 的 URLConnection 对象来创建连接,通过 InputStream 将下载的文件写入存储卡内缓存,下载完毕之后。通过自定义的 openFile()

方法打开文件,然后启动内置的Installer 程序,开始安装程序。安装完毕后,通过自制的delFile()方法,删除存储卡内的临时文件。。大功告成!

3.程序实现

  主界面:

                                                                 

SAX解析版本号:

 

VesionXMLContent
package com.android.Update;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class VesionXMLContent extends DefaultHandler {
private String Version; // 获取版本号
private String preTag;// 前一个元素名称

public String getVersion() {
return Version;
}
@Override
public void startDocument() throws SAXException {
Version
=new String();
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {

String data
= new String(ch, start, length);
if ("string".equals(preTag)) {
Version
= data;
}
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes)
throws SAXException {
preTag
= localName;

}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
preTag
= null;

}
}
SAXGetVersionService
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;

public class SAXGetVersionService {
public static String readRssXml(InputSource inStream)throws Exception {
SAXParserFactory spf
= SAXParserFactory.newInstance();
SAXParser saxParser
= spf.newSAXParser(); // 创建解析器
VesionXMLContent handler = new VesionXMLContent();
saxParser.parse(inStream, handler);
return handler.getVersion();
}
}

UpdateActivity
package com.android.Update;

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.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.URLUtil;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
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;

public class UpdateActivity 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="http://gdown.baidu.com/data/wisegame/82272d43548dbe25/shuiguorenzhezhongwen.apk";

  
// 自己后台的一个版本号的 web 服务
   private String VersionUri ="http://192.168.68.120/VersionService.asmx/GetVersion";
  @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

}

@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_menu_delete);
// setIcon()方法为菜单设置图标,这里使用的是系统自带的图标,同学们留意一下,以
// android.R开头的资源是系统提供的,我们自己提供的资源是以R开头的
//downLoadFile("http://gdown.baidu.com/data/wisegame/82272d43548dbe25/shuiguorenzhezhongwen.apk");
return true;
}
   
   @Override
public boolean onOptionsItemSelected(MenuItem item) {

  
// 解析Version网页,获取版本号
  NowVersion =getVersionxml(VersionUri);
  
// 装载获取当前的版本号
  load ();
  
//当有最新版本的时候
  if(PastVersion != null&&!PastVersion.equals(NowVersion))
  {
  
//保存最新版本号
        save();
  Dialog dialog
= new AlertDialog.Builder(UpdateActivity.this).setTitle("系统更新")
 .setMessage(
"发现新版本,请更新!")// 设置内容
 .setPositiveButton("确定",// 设置确定按钮
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
pBar
= new ProgressDialog(UpdateActivity.this);
pBar.setTitle(
"正在下载");
pBar.setMessage(
"请稍候...");
pBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);
fileEx
= strURL.substring(strURL.lastIndexOf(".")+1,strURL.length()).toLowerCase();
    fileNa
= strURL.substring(strURL.lastIndexOf("/")+1,strURL.lastIndexOf("."));
    getFile(strURL);
}
}).setNegativeButton(
"取消",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
// 点击"取消"按钮之后退出程序
}
}).create();
// 创建
// 显示对话框
dialog.show();
  }
  
//当前为最新版本时
  else{
  Toast.makeText(
this, "当前为最新版本", Toast.LENGTH_LONG).show();
  }
  
return false;
}
`  
   
//处理下载URL文件自定义函数
   private void getFile(final String strPath)
{
   pBar.show();
  
try{
    
if (strPath.equals(currentFilePath) ){
      getDataSource(strPath);
  }
  currentFilePath
= strPath;
  Runnable r
= new Runnable() {
    
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("stream is null");
  }
  
//建立临时文件
  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);
  }
}
}

// 在手机上打开文件的method
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);
  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"))
  {
  }
  
else
  {
    type
+= "/*";
      }
 
return type;
}


//删除存储卡内的临时文件
private void delFile(String strFileName)
{
File myFile
= new File(strFileName);
if(myFile.exists())
{
myFile.delete();
}
}


//SAX解析resourceUrl 页面的内容
   public String getVersionxml(String resourceUrl)
   {
String db
= null;
URL url
= null;

try {
url
= new URL(resourceUrl);
}
catch (MalformedURLException e) {
e.printStackTrace();
}
InputSource is
=null;
InputStream stream
= null;
try {
is
= new InputSource(url.openStream());
is.setEncoding(
"UTF-8");
db
= SAXGetVersionService.readRssXml(is);
}
catch (Exception e) {
e.printStackTrace();
}
return db;
}
@Override
public void onOptionsMenuClosed(Menu menu) {
    
// Toast.makeText(this, "选项菜单关闭了", Toast.LENGTH_LONG).show();
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
   
// Toast.makeText(this,
// "选项菜单显示之前onPrepareOptionsMenu方法会被调用,你可以用此方法来根据打当时的情况调整菜单",
// Toast.LENGTH_LONG).show();

// 如果返回false,此方法就把用户点击menu的动作给消费了,onCreateOptionsMenu方法将不会被调用
return true;

}
//转载旧的版本号
boolean load ()
{
Properties properties
= new Properties();
try
{
FileInputStream stream
= this.openFileInput("Versionfile.cfg");
//读取文件内容
properties.load(stream);
}
catch (FileNotFoundException e)
{
return false;
}
catch(IOException e)
{
return false;
}
PastVersion
= String.valueOf(properties.get("Version").toString());
return true;
}

   
//保存最新版本号
boolean save()
{
Properties properties
= new Properties();
properties.put(
"Version", NowVersion);

try
{
FileOutputStream stream
= this.openFileOutput("Versionfile.cfg",Context.MODE_WORLD_WRITEABLE);
properties.store(stream,
"");
}
catch (FileNotFoundException e)
{
return false;
}
catch(IOException e)
{
return false;
}
return true;
}
}

特别注意: 此处必须要添加三个users-permission, 分别如下。
提供程序创建网络连接的权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
提供程序拥有安装程序的权限:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"></uses-permission>    
提供创建与文件的权限:                 
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>

这样就全部ok 了!!!特别注意: 此处必须要添加三个users-permission, 分别如下。

源码:http://download.csdn.net/source/3510681   (该代码只供学习,源码内需修改VersionUri 的web 服务地址,也可将版本识别去掉。直接下载。)

注:文章参加“Google 暑期大学生博客分享大赛 - 2011 Android 成长篇”。

posted on 2011-08-11 11:03  xtl  阅读(5099)  评论(2编辑  收藏  举报

导航