Android软件检测更新
一个应用软件的诞生,必将带来的一个问题就是版本的更新问题。今天我就提供一个我个人的方法吧
1.问题分析
版本的更新问题可从两个方面进行考虑:一. 如何获取版本号,与当前版本号进行比对的问题。二. 如何下载最近版本,并安装的问题。
2.解决之道
解决一:我之前做的一个应用软件是通过 Web services 来进行数据的传输,当然版本号是数据也是通过这个来传输的。(本文主要介绍终端的功能实现)
我们在后台服务器段有一个固定网址放着我们的最新版本号(XML文件),然后我们通过网络,去获取这样版本号,并且与当前的版本号进行对比,从而得知是
否需要更新。说白了就是解析XML文件来获取版本号,本文采用了SAX解析器。
解决二:当我们发现有最新版本的时候,我们就需要通过网络,远程下载我们的android应用程序到手机中,并打开 application installer 来安装软件。下
载的时候我们使用 java.net 的 URLConnection 对象来创建连接,通过 InputStream 将下载的文件写入存储卡内缓存,下载完毕之后。通过自定义的 openFile()
方法打开文件,然后启动内置的Installer 程序,开始安装程序。安装完毕后,通过自制的delFile()方法,删除存储卡内的临时文件。。大功告成!
3.程序实现
主界面:
SAX解析版本号:
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;
}
}
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();
}
}
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 服务地址,也可将版本识别去掉。直接下载。)