android 版本检测 Android程序的版本检测与更新实现介绍
程序员文章站
2023-12-14 08:23:28
做个网站的安卓客户端,用户安装到自己手机上,如果我出了新版本怎么办呢?要有版本更新功能。 本来版本检测最好可以自动进行。但如果每次开启程序,都要先检测一轮,是一种浪费,毕竟...
做个网站的安卓客户端,用户安装到自己手机上,如果我出了新版本怎么办呢?要有版本更新功能。
本来版本检测最好可以自动进行。但如果每次开启程序,都要先检测一轮,是一种浪费,毕竟版本更新是小概率的事情。或许可以程序开启的时候,判断一下时间,单日就检测,双日就不检测,或者随机什么的,降低一下检测的频率?
我采取的做法是将检测功能做到了菜单上,用户有需要,就手动打开自己检测一下。反正我们这个是网站客户端,有版本更新,在网站上发个通告就行了。
版本检测与更新有以下几个关键步骤:
1、检测有无新版本
2、下载新版本
3、安装替换新版本
我处理的方案是
1、在assets文件夹新增一个文件:ver.cfg,记录版本信息,纯文本格式,内容只有一句话:
version=1.0
这个会随安装包装到用户的手机上
然后在网站里面,设置一xml文件ver_apk.xml,内容也只有这么一点:
<?xml version="1.0" encoding="utf-8" ?>
<string>1.0</string>
检测的时候,就先访问网站的这个xml,得到最新版本号,然后与手机上的ver.cfg文件里记录的进行比对,不同的话就可以认为存在新版本,提示进行更新。
2、下载的话就是直接下载的,我还不知道怎么弄断点续传
3、安装替换,关键在于签名。就是每个版本的签名要保持一致。否则新的无法替换旧的,提示安装未完成。
------------------- 天气太冷,咯咯咯 ------------------------------------
这个功能做在菜单上,触发代码如下:
//==========================================================================
// 菜单
//==========================================================================
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
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要加上几句
<uses-permission android:name="android.permission.internet" />
<uses-permission android:name="android.permission.install_packages"/>
<uses-permission android:name="android.permission.mount_unmount_filesystems"/>
<uses-permission android:name="android.permission.write_external_storage"/>
<uses-permission android:name="android.permission.restart_packages" />
<uses-permission android:name="android.permission.read_phone_state" />
<uses-permission android:name="android.permission.access_network_state" />
<uses-permission android:name="android.permission.access_coarse_location" />
<uses-permission android:name="android.permission.access_wifi_state" />
本来版本检测最好可以自动进行。但如果每次开启程序,都要先检测一轮,是一种浪费,毕竟版本更新是小概率的事情。或许可以程序开启的时候,判断一下时间,单日就检测,双日就不检测,或者随机什么的,降低一下检测的频率?
我采取的做法是将检测功能做到了菜单上,用户有需要,就手动打开自己检测一下。反正我们这个是网站客户端,有版本更新,在网站上发个通告就行了。
版本检测与更新有以下几个关键步骤:
1、检测有无新版本
2、下载新版本
3、安装替换新版本
我处理的方案是
1、在assets文件夹新增一个文件:ver.cfg,记录版本信息,纯文本格式,内容只有一句话:
复制代码 代码如下:
version=1.0
这个会随安装包装到用户的手机上
然后在网站里面,设置一xml文件ver_apk.xml,内容也只有这么一点:
复制代码 代码如下:
<?xml version="1.0" encoding="utf-8" ?>
<string>1.0</string>
检测的时候,就先访问网站的这个xml,得到最新版本号,然后与手机上的ver.cfg文件里记录的进行比对,不同的话就可以认为存在新版本,提示进行更新。
2、下载的话就是直接下载的,我还不知道怎么弄断点续传
3、安装替换,关键在于签名。就是每个版本的签名要保持一致。否则新的无法替换旧的,提示安装未完成。
------------------- 天气太冷,咯咯咯 ------------------------------------
这个功能做在菜单上,触发代码如下:
复制代码 代码如下:
//==========================================================================
// 菜单
//==========================================================================
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
复制代码 代码如下:
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要加上几句
复制代码 代码如下:
<uses-permission android:name="android.permission.internet" />
<uses-permission android:name="android.permission.install_packages"/>
<uses-permission android:name="android.permission.mount_unmount_filesystems"/>
<uses-permission android:name="android.permission.write_external_storage"/>
<uses-permission android:name="android.permission.restart_packages" />
<uses-permission android:name="android.permission.read_phone_state" />
<uses-permission android:name="android.permission.access_network_state" />
<uses-permission android:name="android.permission.access_coarse_location" />
<uses-permission android:name="android.permission.access_wifi_state" />