首先配置服务器有关的地址:(如下)
public static String PREF_DB_NAME = "silent.preferences";
// 真机网址
public static String NAME_SPACE = "http://192.168.1.100:8080/PhoneSell/";
//模拟器网址
public static String NAME_SPACE_SIMULATION = "http://10.0.2.2:8080/PhoneSell/";
// 检测当前最新版本的URL
public static String CHECK_VERSION_URL = NAME_SPACE + "apk/version.txt";
//下载最新版本APK的URL
public static String DOWNLOAD_APK_URL = NAME_SPACE + "apk/PhoneSell_Client.apk";
// 远程服务器上的版本号
public static String serverApkVersion = null;
在web端上WebRoot下新建文件夹apk,下面包含最新的安装包和此安装包的版本号文件:
在客户端上加入以下方法:
/**
* 判断手机是否联网
*
* @param ctx 上下文环境
* @return true | false
*/
public static boolean isNetworkAvailable(Context ctx) {
ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return (info != null && info.isConnected());
}
/**
* 获取apk的路径
*
* @return "/sdcard/PhoneSell_Client.apk"
*/
private static String getApkPath() {
String apkPath = Environment.getExternalStorageDirectory() + "/PhoneSell_Client.apk";
return apkPath;
}
/**
* 更新apk程序版本
* <p>
* 1.判断网络连接是否正常 [是]
* </p>
* <p>
* 2.判断服务器上是否有新的版本 [是]
* <p>
* <p>
* 3.提示用户是否下载更新 [是]
* <p>
* <p>
* 4.判断SDCard是否存在且可读写 [是]
* <p>
* <p>
* 5.下载安装
* <p>
*
* @param ctx 上下文环境
* @param status 是否有新版本的检测状态
*/
public static void updateApkVersion(final Context ctx, int status) {
switch (status) {
case 1:
String tipInfo = ctx.getString(R.string.tipHasNewVersion);
tipInfo = String.format(tipInfo, getVersionName(ctx), serverApkVersion);
new AlertDialog.Builder(ctx).setTitle(R.string.titleTips).setMessage(tipInfo).setPositiveButton(R.string.btnTextDownloadUpdate, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// 显示加载提示框
final ProgressDialog progressDialog = ProgressDialog.show(ctx, null, ctx.getString(R.string.tipApkDownloading), true);
new Thread() {
public void run() {
// 下载apk
int downloadStatus = downloadApk(ctx);
// 销毁加载提示框
progressDialog.dismiss();
/**
* 线程中一般不要涉及UI操作,如果一定这样做, 需要调用Looper. prepare()和Looper.loop()方法 另一种解决办法:使用Handler,而不是Thread
*/
Looper.prepare();
installApk(ctx, downloadStatus);
Looper.loop();
}
}.start();
}
}).setNeutralButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
}).create().show();
break;
case 2:
// 网络连接失败
createDialog(ctx, R.string.titleTips, ctx.getString(R.string.contentNetworkError));
break;
case 3:
// 版本检测时发生异常
createDialog(ctx, R.string.titleTips, ctx.getString(R.string.tipVersionCheckError));
break;
case 4:
// 提示当前已是最新版本
createDialog(ctx, R.string.titleTips, ctx.getString(R.string.tipIsNewVersion));
break;
}
}
/**
* 判断是否有新的版本
*
* @param ctx 上下文环境
* @return 判断结果(1:有新版本 2:网络连接失败 3:版本检测时异常 4:当前版本已是最新 )
*/
public static int hasNewVersion(Context ctx) {
int result = 1;
// 网络连接正常
if (isNetworkAvailable(ctx)) {
// 检测服务器上的版本
String remoteInfo = getPageContent(CHECK_VERSION_URL);
// 检测当前使用的版本
String thisVersion = getVersionName(ctx);
if (null != remoteInfo && !"".equals(remoteInfo.trim())) {
// 如果两个版本号相同或服务器上的版本较高
if (thisVersion.compareTo(remoteInfo) >= 0) {
// 提示当前已是最新版本
result = 4;
} else {
// 服务器上有新版本,保存新版本号
serverApkVersion = remoteInfo;
}
} else {
// 版本检测时发生异常
result = 3;
}
} else {
// 网络连接失败
result = 2;
}
return result;
}
/**
* 安装apk
*
* @param ctx 上下文环境
* @param downloadStatus 下载状态
*/
private static void installApk(Context ctx, int downloadStatus) {
switch (downloadStatus) {
case 1:
installApk(ctx, getApkPath());
break;
case 2:
// 提示SDCard设备不存在或不可写入
createDialog(ctx, R.string.titleTips, ctx.getString(R.string.tipCheckPluginSDCard));
break;
case 3:
// 提示版本更新失败
createDialog(ctx, R.string.titleTips, ctx.getString(R.string.tipVersionUpdateFailed));
break;
}
}
/**
* 下载apk
*
* @param ctx 上下文环境
* @return 下载状态(1:成功 2:sdcard不存在或不可写 3:下载失败)
*/
private static int downloadApk(Context ctx) {
int result = 1;
// 判断手机的SDCard是否存在并且可读写
if (isSDCardExistAndReadable()) {
boolean flag = downloadApk(DOWNLOAD_APK_URL, getApkPath());
if (!flag) {
result = 3;
}
} else {
result = 2;
}
return result;
}
/**
* 判断手机的SDCard是否存在并且可读
*
* @return
*/
private static boolean isSDCardExistAndReadable() {
// 获取扩展卡设备状态
String sdcardState = Environment.getExternalStorageState();
// 检测SDCard是否存在并且可读写
if (sdcardState.equals(Environment.MEDIA_MOUNTED))
return true;
return false;
}
/**
* 从指定的url下载apk安装文件
*
* @param pageUrl apk资源的位置
* @param apkName 保存在手机上的文件名(含路径)
* @return 是否下载成功[true | false]
*/
private static boolean downloadApk(String pageUrl, String apkName) {
boolean result = false;
File file = null;
OutputStream output = null;
try {
URL url = new URL(pageUrl);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setDoInput(true);
// 连接到指定的url
urlConn.connect();
InputStream inputStream = urlConn.getInputStream();
// 如果SDCard上事先不存在该文件,先创建
file = new File(apkName);
if (!file.exists())
file.createNewFile();
output = new FileOutputStream(file);
byte buffer[] = new byte[4 * 1024];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, len);
}
output.flush();
// apk下载成功
result = true;
} catch (Exception e) {
Log.e("silent", e.getMessage());
result = false;
} finally {
try {
if (null != output)
output.close();
} catch (Exception e) {
Log.e("silent", e.getMessage());
}
}
// apk下载失败
return result;
}
/**
* 安装应用程序
*
* @param ctx 上下文环境
* @param apkPath apk的全路径
*/
private static void installApk(Context ctx, String apkPath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + apkPath), "application/vnd.android.package-archive");
ctx.startActivity(intent);
}
/**
* 获取指定url的内容
*
* @param pageUrl 网页url
* @return 网页内容
*/
private static String getPageContent(String pageUrl) {
StringBuffer sb = new StringBuffer();
String line = null;
BufferedReader buffer = null;
try {
// 创建一个URL对象
URL url = new URL(pageUrl);
// 创建一个Http连接
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(5000);
urlConn.setReadTimeout(5000);
// 使用IO流读取数据
buffer = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
while ((line = buffer.readLine()) != null) {
sb.append(line);
}
} catch (SocketTimeoutException e) {
Log.e("silent", "SocketTimeoutException");
} catch (Exception e) {
Log.e("silent", e.getMessage());
} finally {
try {
buffer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return sb.toString();
}
下面的代码就加在点击事件上,用于更新的:
versionPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
// 显示加载提示框
dialog = ProgressDialog.show(context, null, getString(R.string.alert_version_checking), true, true);
new Thread() {
public void run() {
// 检测服务器上是否有新版本
int result = SaleUtil.hasNewVersion(context);
// 销毁加载提示框
dialog.dismiss();
if (5 == result) {
SaleUtil.updateApkVersion(context, result);
// 向主线程发送条空消息
handler.sendEmptyMessage(0);
} else {
Looper.prepare();
SaleUtil.updateApkVersion(context, result);
Looper.loop();
}
}
}.start();
return false;
}
});
如果想在打开程序时就检测是否有新版本,那么就在MainActivity里加如下方法:
/**
* 检测新版本
*/
private void checkNewVersion() {
SharedPreferences setting = getSharedPreferences(SaleUtil.PREF_DB_NAME, 0);
// 上次检测时间
Long lastCheckTime = setting.getLong("CHECK_TIME", 0L);
// 当前时间
Long currentTime = System.currentTimeMillis();
// 相差的小时数
Long diff = (currentTime - lastCheckTime) / 1000 / 3600;
// 如果上次检测时间距离现在超过1小时将再次检测
if (diff >= 1L) {
new Thread() {
public void run() {
// 检测服务器上是否有新版本
int result = SaleUtil.hasNewVersion(context);
// 有新版本
if (1 == result) {
/**
* 线程中一般不要涉及UI操作,如果一定这样做,需要调用Looper.prepare()和Looper. loop()方法 另一种解决办法:使用Handler,而不是Thread
*/
Looper.prepare();
SaleUtil.updateApkVersion(context, result);
Looper.loop();
}
}
}.start();
Editor editor = setting.edit();
// 设置检测时间
editor.putLong("CHECK_TIME", currentTime);
// 提交设置
editor.commit();
}
}
最后在onCreate()方法里添加如下代码就行了:
// 每次启动应用程序时就连接一次服务器检查是否有更新的版本
checkNewVersion();