在平时开发中,很常见的功能就是一个版本发布了,在进入主页面之前会对本地版本和服务器版本对比,发现新版本就会直接弹出对话框然用户选择是否要更新,实现逻辑代码如下,建立一个splash.xml,这个页面主要是作用是检测版本异同
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_splash_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="280dip"
android:text="版本号"
android:textColor="#FF01b6f8"
android:textSize="20sp" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dip" />
</LinearLayout>
建立一个SplashActivity,这个类的主要作用是检测版本更新,请求服务器并解析返回结果,把版本抽取成一个对象
public class Info {
<span style="white-space:pre"> </span>//省去set/get和构造
private String version;
private String desc;
private String apkurl;
}
public class SplashActivity extends Activity {
private LinearLayout ll_main;
private TextView tv_splash_version;
private ProgressDialog pd;
private String versiontext;
private Info info;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (isNeedUpdate(versiontext)) {
showUpdataDialog();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.splash);
ll_main = (LinearLayout) findViewById(R.id.ll_main);
tv_splash_version = (TextView) findViewById(R.id.tv_splash_version);
pd = new ProgressDialog(this);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage("正在下载...");
versiontext = getVersion();
new Thread() {
public void run() {
//延迟2秒
SystemClock.sleep(2000);
//发送消息给handler
handler.sendEmptyMessage(0);
};
}.start();
tv_splash_version.setText(versiontext);
AlphaAnimation aa = new AlphaAnimation(0.0f, 1.0f);
aa.setDuration(2000);
ll_main.startAnimation(aa);
// 全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
// 更新提示框
private void showUpdataDialog() {
AlertDialog.Builder builder = new Builder(this);
builder.setTitle("升级提示");
builder.setMessage(info.getDesc());
builder.setCancelable(false);
builder.setPositiveButton("下载", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 判断SD卡是否可用
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
DownloadFileThreadTask task = new DownloadFileThreadTask(
info.getApkurl(), Environment
.getExternalStorageDirectory() + "/new.apk");
pd.show();
//开启下载APK线程
new Thread(task).start();
} else {
Toast.makeText(getApplicationContext(), "sd卡不可用", 1).show();
loadMainUI();
}
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//点击取消,也进入主页面
loadMainUI();
}
});
builder.create().show();
}
// 下载文件
private class DownloadFileThreadTask implements Runnable {
private String path;
private String filePath;
public DownloadFileThreadTask(String path, String filePath) {
this.path = path;
this.filePath = filePath;
}
@Override
public void run() {
try {
//获取路径和文件和dialog
File file = DownloadFileTask.getFile(path, filePath, pd);
pd.dismiss();
//下载完成就安装
installAPK(file);
} catch (Exception e) {
pd.dismiss();
e.printStackTrace();
Toast.makeText(getApplicationContext(), "文件下载失败", 0).show();
loadMainUI();
}
}
}
// 检测本地版本和服务器是否一致
private boolean isNeedUpdate(String textversion) {
HttpTools httpTools = new HttpTools(this);
// 从服务器获取最新版本信息
try {
info = httpTools.getServerVersion(R.string.apkupdateurl);
String version = info.getVersion();
if (textversion.equals(version)) {
loadMainUI();
return false;
} else {
return true;
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "获取版本异常", 0).show();
loadMainUI();
return false;
}
};
// 加载主页面
private void loadMainUI() {
startActivity(new Intent(SplashActivity.this, MainActivity.class));
finish();
}
// 静默安装
public void installAPK(File file) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
finish();
startActivity(intent);
}
// 获取版本号
public String getVersion() {
PackageManager packageManager = getPackageManager();
try {
PackageInfo info = packageManager.getPackageInfo(getPackageName(),
0);
return info.versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return "版本异常";
}
}
在SplashActivity中,使用到了请求服务器数据并解析数据的HttpTools这个类,其中使用到了android内部提供的httpClient来解析服务器返回的json数据
public class HttpTools {
private Context context;
public HttpTools(Context context) {
this.context = context;
}
// 根据配置文件中的url获取json地址
public Info getServerVersion(int uid) {
Info info = null;
<span style="white-space:pre"> </span>//使用外部配置文件中的地址
String path = context.getResources().getString(uid);
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(path);
HttpResponse httpResponse;
JSONObject jsonObject;
try {
httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
// {version:'2.0',desc:'fuck',apkurl:'http://xxx'}
String json = EntityUtils.toString(httpResponse.getEntity(),
"UTF-8");
try {
jsonObject = new JSONObject(json);
for (int i = 0; i < jsonObject.length(); i++) {
String version = jsonObject.getString("version");
String apkurl = jsonObject.getString("apkurl");
String desc = jsonObject.getString("desc");
info = new Info(version, desc, apkurl);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return info;
}
}
好了,基于上面的代码,大体功能已经完成一半了;继续ing....。完成上面代码之后,在SplashActivity中使用到了一个DownloadFileTask这个类,这个类主要是负责下载文件和更新进度条,写完代码之后才发现,冗余的代码很多,想用AsyncHtttpClient这个开源项目简化一下
public class DownloadFileTask {
public static File getFile(String path, String filePath, ProgressDialog pd)
throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
if (conn.getResponseCode() == 200) {
int total = conn.getContentLength();
pd.setMax(total);
InputStream is = conn.getInputStream();
File file = new File(filePath);
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = 0;
int process = 0;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
process += len;
pd.setProgress(process);
Thread.sleep(50);
}
fos.flush();
fos.close();
is.close();
return file;
}
return null;
}
}
到了这里,客户端的代码完成差不多了,最后别忘了一点,在values下见了一个config.xml文件,在这个文件里面动态的添加访问服务器的路径
<resources>
<string name="apkupdateurl">http://192.168.4.112:8080/Server/Server</string>
</resources>
=====================我是分割线==============================
编写服务端的代码,在服务端用一个servlet来编写代码如下:
@WebServlet("/Server")
public class ServerServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("UTF-8");
Info info = new Info();
info.setVersion("2.0");
info.setDesc("发现新版本,请更新哦(*^__^*) ");
info.setApkurl("http://192.168.4.112:8080/Demo.apk");
String json = JSONObject.toJSONString(info);
response.getWriter().write(json);
}
}
把需要添加的权限加入:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
最后,记得把最新的版本放到服务器上,并且确保本地客户端的清单文件中的版本和服务器上的版本不同,才能弹出更新对话框和更新并且安装。