在进入splash界面的时候后台启动线程,联网检查是否有更新版本可用,如果没有新版本则splash界面显示后进入主界面,否则显示升级对话框让用户选择是否进行更新。
1、权限设置
因为需要联网,所以AndroidManifest.xml文件需要添加访问网络的权限:
<uses-permission android:name="android.permission.INTERNET" />
本人就是由于忘记添加该权限,在后面的运行中出现错误:
12-23 03:24:26.999 1528-1541/com.example.mobilesafe W/System.err﹕ java.net.SocketException: socket failed: EACCES (Permission denied)
12-23 03:24:26.999 1528-1541/com.example.mobilesafe W/System.err﹕ at libcore.io.IoBridge.socket(IoBridge.java:576)
12-23 03:24:27.009 1528-1541/com.example.mobilesafe W/System.err﹕ at java.net.PlainSocketImpl.create(PlainSocketImpl.java:201)
12-23 03:24:27.009 1528-1541/com.example.mobilesafe W/System.err﹕ at java.net.Socket.checkOpenAndCreate(Socket.java:664)
12-23 03:24:27.009 1528-1541/com.example.mobilesafe W/System.err﹕ at java.net.Socket.connect(Socket.java:808)
12-23 03:24:27.009 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.Platform.connectSocket(Platform.java:131)
12-23 03:24:27.009 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.Connection.connect(Connection.java:101)
12-23 03:24:27.009 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
12-23 03:24:27.019 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
12-23 03:24:27.019 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
12-23 03:24:27.029 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
12-23 03:24:27.029 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
12-23 03:24:27.029 1528-1541/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503)
12-23 03:24:27.029 1528-1541/com.example.mobilesafe W/System.err﹕ at com.example.mobilesafe.SplashActivity$checkUpdate.run(SplashActivity.java:146)
12-23 03:24:27.029 1528-1541/com.example.mobilesafe W/System.err﹕ at java.lang.Thread.run(Thread.java:841)
12-23 03:24:27.029 1528-1541/com.example.mobilesafe W/System.err﹕ Caused by: libcore.io.ErrnoException: socket failed: EACCES (Permission denied)
12-23 03:24:27.079 1528-1541/com.example.mobilesafe W/System.err﹕ at libcore.io.Posix.socket(Native Method)
12-23 03:24:27.079 1528-1541/com.example.mobilesafe W/System.err﹕ at libcore.io.BlockGuardOs.socket(BlockGuardOs.java:181)
12-23 03:24:27.079 1528-1541/com.example.mobilesafe W/System.err﹕ at libcore.io.IoBridge.socket(IoBridge.java:561)
12-23 03:24:27.079 1528-1541/com.example.mobilesafe W/System.err﹕ ... 13 more
2、服务器更新配置
这里使用wampserver搭建本地服务器,更新配置文件放在X:\wamp\www\app\mobilesafe目录下,配置内容为:
<?xml version="1.0" encoding="utf-8"?>
<info>
<ver>2.0</ver>
<desc>亲,有新版本了,赶紧更新吧!</desc>
<apkurl>http://10.0.2.2/app/mobilesafe/mobilesafe.apk</apkurl>
</info>
因为服务器地址属于常量字符串,这里添加到strings.xml中:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">mobilesafe</string>
<string name="serverurl">http://10.0.2.2/app/mobilesafe/update.xml</string>
</resources>
这里是使用本机进行测试的,注意Android访问本机的IP地址是:10.0.2.2,如果使用127.0.0.1会出现如下错误:
12-23 03:29:15.189 1573-1586/com.example.mobilesafe W/System.err﹕ java.net.ConnectException: failed to connect to /127.0.0.1 (port 80) after 5000ms: isConnected failed: ECONNREFUSED (Connection refused)
12-23 03:29:15.189 1573-1586/com.example.mobilesafe W/System.err﹕ at libcore.io.IoBridge.isConnected(IoBridge.java:223)
12-23 03:29:15.189 1573-1586/com.example.mobilesafe W/System.err﹕ at libcore.io.IoBridge.connectErrno(IoBridge.java:161)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at libcore.io.IoBridge.connect(IoBridge.java:112)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at java.net.Socket.connect(Socket.java:843)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.Platform.connectSocket(Platform.java:131)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.Connection.connect(Connection.java:101)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at com.example.mobilesafe.SplashActivity$checkUpdate.run(SplashActivity.java:146)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at java.lang.Thread.run(Thread.java:841)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ Caused by: libcore.io.ErrnoException: isConnected failed: ECONNREFUSED (Connection refused)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ at libcore.io.IoBridge.isConnected(IoBridge.java:208)
12-23 03:29:15.199 1573-1586/com.example.mobilesafe W/System.err﹕ ... 15 more
相关参考: http://www.myexception.cn/operating-system/754403.html
4、更新信息结构体
按照服务器更新配置,更新信息共有三个字段:版本号,更新提示文本,apk下载地址:
package com.example.mobilesafe;
/**
* Created by sing on 13-12-23.
*/
public class UpdateInfo {
//服务器端版本号
private String version;
//服务器端升级提示
private String desc;
//服务器端apk下载地址
private String apkurl;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getApkurl() {
return apkurl;
}
public void setApkurl(String apkurl) {
this.apkurl = apkurl;
}
}
5、XML更新配置解析类
注意XmlPullParser解析XML的nextText存在bug:
12-23 03:46:07.439 1729-1748/com.example.mobilesafe W/System.err﹕ org.xmlpull.v1.XmlPullParserException: END_TAG expected (position:START_TAG <version>@3:14 in java.io.InputStreamReader@b2ef2718)
12-23 03:46:07.449 1729-1748/com.example.mobilesafe W/System.err﹕ at org.kxml2.io.KXmlParser.nextText(KXmlParser.java:2077)
12-23 03:46:07.479 1729-1748/com.example.mobilesafe W/System.err﹕ at com.example.mobilesafe.UpdateInfoParser.getUpdateInfo(UpdateInfoParser.java:33)
12-23 03:46:07.479 1729-1748/com.example.mobilesafe W/System.err﹕ at com.example.mobilesafe.SplashActivity$checkUpdate.run(SplashActivity.java:153)
12-23 03:46:07.499 1729-1748/com.example.mobilesafe W/System.err﹕ at java.lang.Thread.run(Thread.java:841)
参考解决:http://blog.csdn.net/nxh_love/article/details/7109762,
这里使用的方法是:当前的位置不是END_TAG时,在调用nextText()之后再调用nextTag(),完整代码:
package com.example.mobilesafe;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by sing on 13-12-23.
*/
public class UpdateInfoParser {
/**
*解析一个utf-8格式的xml输入流,返回一个UpdateInfo对象
* XmlPullParser解析bug参考解决:http://blog.csdn.net/nxh_love/article/details/7109762
* @param is
* @return UpdateInfo对象
* @throws XmlPullParserException
* @throws IOException
*/
public static UpdateInfo getUpdateInfo(InputStream is) throws XmlPullParserException, IOException {
UpdateInfo info = new UpdateInfo();
//获取一个pull解析的实例
XmlPullParser parser = Xml.newPullParser();
//解析xml
parser.setInput(is, "UTF-8");
parser.nextTag();
int type = parser.getEventType();
while (parser.nextTag() == XmlPullParser.START_TAG) {
String tag = parser.getName();
String text = parser.nextText();
if (parser.getEventType() != XmlPullParser.END_TAG) {
parser.nextTag();
}
if (tag.equals("ver")) {
info.setVersion(text);
} else if (tag.equals("desc")) {
info.setDesc(text);
} else if (tag.equals("apkurl")) {
info.setApkurl(text);
}
}
return info;
}
}
6、SplashActivity::OnCreate创建检查更新的线程
//检查更新
new Thread(new checkUpdate()) {
}.start();
/**
* 检查更新
*/
private class checkUpdate implements Runnable {
public void run() {
long startTime = System.currentTimeMillis();
long endTime = startTime;
Message msg = Message.obtain();
try {
//获取服务器更新地址
String serverurl = getResources().getString(R.string.serverurl);
URL url = new URL(serverurl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
int code = conn.getResponseCode();
if (code == 200) {
//success
InputStream is = conn.getInputStream();
//解析出更新信息
updateinfo = UpdateInfoParser.getUpdateInfo(is);
endTime = System.currentTimeMillis();
long time = endTime - startTime;
if (time < 2000) {
try {
Thread.sleep(2000 - time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
msg.what = GET_INFO_SUCCESS;
handler.sendMessage(msg);
} else {
//服务器错误
msg.what = SERVER_ERROR;
handler.sendMessage(msg);
endTime = System.currentTimeMillis();
long time = endTime - startTime;
if (time < 2000) {
try {
Thread.sleep(2000 - time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (MalformedURLException e) {
msg.what = SERVER_URL_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (ProtocolException e) {
msg.what = PROTOCOL_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (IOException e) {
msg.what = IO_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (XmlPullParserException e) {
msg.what = XML_PARSER_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (Exception e) {
msg.what = UNKNOWN_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
}
}
}
7、添加消息处理函数,在界面中统一显示
/**
* 消息处理器
*/
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case GET_INFO_SUCCESS:
String serverVersion = updateinfo.getVersion();
String currentVersion = getVersion();
if (currentVersion.equals(serverVersion)) {
Log.i(TAG, "版本号相同无需升级,直接进入主界面");
}else {
Log.i(TAG, "版本号不同需升级,显示升级对话框");
showUpdateDialog();
}
break;
case SERVER_ERROR:
Toast.makeText(getApplicationContext(), "服务器内部异常", 1).show();
break;
case SERVER_URL_ERROR:
Toast.makeText(getApplicationContext(), "服务器路径错误", 1).show();
break;
case PROTOCOL_ERROR:
Toast.makeText(getApplicationContext(), "协议错误", 1).show();
break;
case XML_PARSER_ERROR:
Toast.makeText(getApplicationContext(), "XML解析错误", 1).show();
break;
case IO_ERROR:
Toast.makeText(getApplicationContext(), "I/O错误", 1).show();
break;
case UNKNOWN_ERROR:
Toast.makeText(getApplicationContext(), "未知错误", 1).show();
break;
}
}
};
8、当本地版本号与服务器端获取的版本号不同时提示升级对话框
/**
* 显示升级提示对话框
*/
protected void showUpdateDialog() {
//创建对话框构造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//设置对话框图标
builder.setIcon(getResources().getDrawable(R.drawable.notification));
//设置对话框标题
builder.setTitle("升级提示");
//设置更新信息为对话框提示内容
builder.setMessage(updateinfo.getDesc());
//设置升级按钮及响应事件
builder.setPositiveButton("升级", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//设置取消按钮及响应事件
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//创建并显示对话框
builder.create().show();
}
9、SplashActivity完整代码
package com.example.mobilesafe;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import android.os.Handler;
public class SplashActivity extends Activity {
//常量定义
public static final int UNKNOWN_ERROR = 99;
public static final int GET_INFO_SUCCESS = 100;
public static final int SERVER_ERROR = 101;
public static final int SERVER_URL_ERROR = 102;
public static final int PROTOCOL_ERROR = 103;
public static final int IO_ERROR = 104;
public static final int XML_PARSER_ERROR = 105;
public static final String TAG = "SplashActivity";
//获取的服务器端的更新信息
UpdateInfo updateinfo;
//显示版本号的tv控件
private TextView tv_splash_version;
//布局控件
private RelativeLayout r1_splash;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置无标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
//设置全屏模式
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置局部,显示版本号
setContentView(R.layout.activity_splash);
r1_splash = (RelativeLayout)findViewById(R.id.r1_splash);
tv_splash_version = (TextView)findViewById(R.id.tv_splash_version);
tv_splash_version.setText("版本号:" + getVersion());
//显示渐进动画
AlphaAnimation aa = new AlphaAnimation(0.3f, 1.0f);
aa.setDuration(2000);
r1_splash.startAnimation(aa);
//检查更新
new Thread(new checkUpdate()) {
}.start();
}
//获取当前应用程序的版本号
private String getVersion() {
String version = "";
//获取系统包管理器
PackageManager pm = this.getPackageManager();
try {
PackageInfo info = pm.getPackageInfo(getPackageName(), 0);
version = info.versionName;
} catch (Exception e) {
e.printStackTrace();
}
return version;
}
/**
* 消息处理器
*/
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case GET_INFO_SUCCESS:
String serverVersion = updateinfo.getVersion();
String currentVersion = getVersion();
if (currentVersion.equals(serverVersion)) {
Log.i(TAG, "版本号相同无需升级,直接进入主界面");
}else {
Log.i(TAG, "版本号不同需升级,显示升级对话框");
showUpdateDialog();
}
break;
case SERVER_ERROR:
Toast.makeText(getApplicationContext(), "服务器内部异常", 1).show();
break;
case SERVER_URL_ERROR:
Toast.makeText(getApplicationContext(), "服务器路径错误", 1).show();
break;
case PROTOCOL_ERROR:
Toast.makeText(getApplicationContext(), "协议错误", 1).show();
break;
case XML_PARSER_ERROR:
Toast.makeText(getApplicationContext(), "XML解析错误", 1).show();
break;
case IO_ERROR:
Toast.makeText(getApplicationContext(), "I/O错误", 1).show();
break;
case UNKNOWN_ERROR:
Toast.makeText(getApplicationContext(), "未知错误", 1).show();
break;
}
}
};
/**
* 检查更新
*/
private class checkUpdate implements Runnable {
public void run() {
long startTime = System.currentTimeMillis();
long endTime = startTime;
Message msg = Message.obtain();
try {
//获取服务器更新地址
String serverurl = getResources().getString(R.string.serverurl);
URL url = new URL(serverurl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
int code = conn.getResponseCode();
if (code == 200) {
//success
InputStream is = conn.getInputStream();
//解析出更新信息
updateinfo = UpdateInfoParser.getUpdateInfo(is);
endTime = System.currentTimeMillis();
long time = endTime - startTime;
if (time < 2000) {
try {
Thread.sleep(2000 - time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
msg.what = GET_INFO_SUCCESS;
handler.sendMessage(msg);
} else {
//服务器错误
msg.what = SERVER_ERROR;
handler.sendMessage(msg);
endTime = System.currentTimeMillis();
long time = endTime - startTime;
if (time < 2000) {
try {
Thread.sleep(2000 - time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (MalformedURLException e) {
msg.what = SERVER_URL_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (ProtocolException e) {
msg.what = PROTOCOL_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (IOException e) {
msg.what = IO_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (XmlPullParserException e) {
msg.what = XML_PARSER_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
} catch (Exception e) {
msg.what = UNKNOWN_ERROR;
handler.sendMessage(msg);
e.printStackTrace();
}
}
}
/**
* 显示升级提示对话框
*/
protected void showUpdateDialog() {
//创建对话框构造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//设置对话框图标
builder.setIcon(getResources().getDrawable(R.drawable.notification));
//设置对话框标题
builder.setTitle("升级提示");
//设置更新信息为对话框提示内容
builder.setMessage(updateinfo.getDesc());
//设置升级按钮及响应事件
builder.setPositiveButton("升级", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//设置取消按钮及响应事件
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//创建并显示对话框
builder.create().show();
}
}
10、运行效果图
本地版本号与服务器版本号相同时直接进入主界面:
12-23 05:43:33.279 2125-2125/com.example.mobilesafe I/SplashActivity﹕ 版本号相同无需升级,直接进入主界面
本地版本号与服务器版本号不同时显示提示升级对话框:
12-23 05:44:37.349 2125-2125/com.example.mobilesafe I/SplashActivity﹕ 版本号不同需升级,显示升级对话框