算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事 情,也一直没做,今天刚好有空,交流群还有人请教,就寻思着把一些相关性的原理和基础操作写下来,虽然我 络这一块还是不怎么扎实,嘿嘿,还记得我们怎么搭建的服务器吗?
地址:[Android服务器——TomCat服务器的搭建](
http://blog.csdn.net/qq_26787115/article/details/50575915)
我们新建一个项目TomCatVersion
这边先来说一下原理,我们做的小例子也是十分的简单,一个首页,我们用Handler实现,然后同步检测当前版本 和系统的版本 对比,如果有升级则弹出提示框提示升级,点击确定开始下载apk,同时显示进度,等下载完成之后启动新下载的APK进行安装,如果点击取消,进入主页,如果没有升级,则直接进入主页,思路应该很清晰吧!那好,我们开始!
一,准备工作
1.搭建TomCat服务器
我们没有服务器,所以使用TomCat服务器模拟,不会搭建的请看[Android服务器——TomCat服务器的搭建](
http://blog.csdn.net/qq_26787115/article/details/50575915)
2.新建一个IndexActivity类,并且在AndroidManifest.xml里注册并且设置为主入口
<activity android_name=”com.lgl.tomcatversion.IndexActivity” >
<intent-filter>
<action android_name=”android.intent.action.MAIN” />
<category android_name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
3.准备一张首页的图片
可有可无,这里作为演示就去 上下载了一张
4.版本更新接口
也就是服务器的地址,我们这里也就直接自己写一段简单的json了
{
“versionName”: “2.0”,
“versionCode”: 1,
“content”: “修复多项bug!”,
“url”: “http://localhost:192.168.1.101/lgl/TomCatVersion.apk”
}
我们把他放在服务器里面】
乱码请无视,浏览器的锅
5. 络权限
<uses-permission android_name=”android.permission.INTERNET”/>
二.layout_index.xml
布局就没什么内容了,一个进度,一个文本
<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout xmlns_android=”http://schemas.android.com/apk/res/android”
android_layout_width=”match_parent”
android_layout_height=”match_parent”
android_background=”@drawable/index”
android_gravity=”center_horizontal”>
<TextView
android_gravity=”center”
android_layout_alignParentBottom=”true”
android_id=”@+id/tv_version”
android_layout_width=”match_parent”
android_layout_height=”wrap_content”
android_layout_marginBottom=”20dp”
android_textColor=”@android:color/white”
android_textSize=”20sp” />
<ProgressBar
android_layout_centerInParent=”true”
android_layout_alignParentBottom=”true”
android_id=”@+id/progressBar”
android_layout_width=”wrap_content”
android_layout_height=”wrap_content”
android_layout_marginBottom=”60dp” />
</RelativeLayout>
三.逻辑分析
1.获取版本
首先你的首页的文本上需要获取当前应用的版本 吧
/**
* 获取APP版本
*
* @return
*/
private String getAppVersion() {
try {
//PackageManager管理器
PackageManager pm = getPackageManager();
//获取相关信息
packageInfo = pm.getPackageInfo(getPackageName(), 0);
//版本名称
String name = packageInfo.versionName;
//版本
int version = packageInfo.versionCode;
Log.i(“版本信息”, “版本名称:”+name+”版本 ”+version);
return name;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//如果出现异常抛出null
return null;
}
我们打印出来的Log
我们需要的就是这个name(版本名称)
2.解析JSON
这段json还是十分的简单的,我们直接就用原生的方式解析了,本来想用Volley的,但是演示的话,希望各位自己根据需求使用
/**
* 解析JSON
*/
private void getJSON() {
// 子线程访问,耗时操作
new Thread() {
public void run() {
try {
// JSON地址
HttpURLConnection conn = (HttpURLConnection) new URL(
//模拟器一般有一个预留IP:10.0.2.2
“http://192.168.1.103:8080/lgl/update.json”)
.openConnection();
//请求方式GRT
conn.setRequestMethod(“GET”);
//连接超时
conn.setConnectTimeout(5000);
//响应超时
conn.setReadTimeout(3000);
//连接
conn.connect();
//获取请求码
int responseCode = conn.getResponseCode();
//等于200说明请求成功
if(responseCode == 200){
//拿到他的输入流
InputStream in = conn.getInputStream();
String stream = Utils.toStream(in);
Log.i(“JSON”, stream);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
这里我们写了一个Utils来转换流
package com.lgl.tomcatversion;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 流转换Stream的工具栏
*
* @author LGL
*
*/
public class Utils {
// 对外发放
public static String toStream(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// 长度
int length = 0;
byte[] buffer = new byte[1024];
// -1代表读完了
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
// 读完关闭
in.close();
out.close();
// 我们把返回的数据转换成String
return out.toString();
}
}
这样我们就可以把Json打印出来了
既然获取到了,那我们就开始解析JSON吧
在Log后面继续写代码
// 解析
JSONObject jsonObject = new JSONObject(stream);
// 获取版本名
String versionName = jsonObject.getString(“versionName”);
// 获取版本
int versionCode = jsonObject.getInt(“versionCode”);
// 获取更新内容
String content = jsonObject.getString(“content”);
// 获取下载地址
String url = jsonObject.getString(“url”);
这样我们就解析完成了
3.版本比较以及提示更新
这里我们可以根据name或者code的比较来判断是否有更新,有更新的话,弹出提示框,点确定再更新,我这里就比较Code了,先写以恶搞获取code的方法
/**
* 获取versionCode
*/
private int getCode() {
// PackageManager管理器
PackageManager pm = getPackageManager();
// 获取相关信息
try {
packageInfo = pm.getPackageInfo(getPackageName(), 0);
// 版本
int version = packageInfo.versionCode;
return version;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
>然后我们接着刚才解析完JSON的地方比对,我们先把服务器的JSON数据改了
{
“versionName”: “2.0”,
“versionCode”: 2,
“content”: “修复多项bug!”,
“url”: “http://192.168.1.103:8080/lgl/TomCatVersion.apk”
}
>这样就会提示更新了,我们检修
// 版本判断
if (versionCode > getCode()) {
// 提示更新
msg.what = UPDATE_YES;
} else {
// 不更新,跳转到主页
msg.what = UPDATE_NO;
}
>子线程中我们是不能弹框的,所以我们用Handler,当我们发送UPDATE_YES的时候就弹框,也就是执行我们弹框的方法
/**
* 升级弹框
*/
private void showUpdateDialog() {
updateDialog = new CustomDialog(this, 0, 0, R.layout.dialog_update,
R.style.Theme_dialog, Gravity.CENTER, 0);
// 更新内容
dialog_update_content = (TextView) updateDialog
.findViewById(R.id.dialog_update_content);
dialog_update_content.setText(content);
// 确定更新
dialog_confrim = (TextView) updateDialog
.findViewById(R.id.dialog_confrim);
dialog_confrim.setOnClickListener(this);
// 取消更新
dialog_cancel = (TextView) updateDialog
.findViewById(R.id.dialog_cancel);
dialog_cancel.setOnClickListener(this);
updateDialog.show();
}
>这里我用了一个自定义的Dialog’
CustomDialog
package com.lgl.tomcatversion;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
public class CustomDialog extends Dialog {
public CustomDialog(Context context, int layout, int style) {
this(context, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT, layout, style,
Gravity.CENTER);
}
public CustomDialog(Context context, int width, int height, int layout,
int style, int gravity, int anim) {
super(context, style);
setContentView(layout);
// set window params
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
// set width,height by density and gravity
// float density = getDensity(context);
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.gravity = gravity;
window.setAttributes(params);
window.setWindowAnimations(anim);
}
public CustomDialog(Context context, int width, int height, int layout,
int style, int gravity) {
this(context, width, height, layout, style, gravity,
R.style.pop_anim_style);
}
}
他需要用到一些资源
dialog_update.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout xmlns_android=”http://schemas.android.com/apk/res/android”
android_id=”@+id/main_layout”
android_layout_width=”match_parent”
android_layout_height=”150dip”
android_layout_marginEnd=”20dp”
android_layout_marginStart=”20dp”
android_background=”@drawable/dialog_bg”
android_orientation=”vertical” >
<LinearLayout
android_id=”@+id/dialog_notifly_bottom”
android_layout_width=”match_parent”
android_layout_height=”45dp”
android_layout_alignParentBottom=”true”
android_layout_gravity=”bottom”
android_orientation=”horizontal” >
<TextView
android_id=”@+id/dialog_cancel”
android_layout_width=”0dp”
android_layout_height=”match_parent”
android_layout_weight=”1″
android_gravity=”center”
android_singleLine=”true”
android_text=”暂不更新”
android_textSize=”18sp” />
<View
android_layout_width=”0.2dp”
android_layout_height=”match_parent”
android_background=”#AAAAAA” />
<TextView
android_id=”@+id/dialog_confrim”
android_layout_width=”0dp”
android_layout_height=”match_parent”
android_layout_weight=”1″
android_gravity=”center”
android_singleLine=”true”
android_text=”立即下载”
android_textSize=”18sp” />
</LinearLayout>
<View
android_id=”@+id/dialog_notifly_line”
android_layout_width=”match_parent”
android_layout_height=”0.2dp”
android_layout_above=”@id/dialog_notifly_bottom”
android_background=”#AAAAAA” />
<TextView
android_id=”@+id/dialog_update_content”
android_layout_width=”match_parent”
android_layout_height=”wrap_content”
android_layout_above=”@+id/dialog_notifly_line”
android_layout_below=”@+id/TextView01″
android_layout_marginBottom=”10dip”
android_layout_marginLeft=”10dip”
android_layout_marginRight=”10dip”
android_gravity=”center”
android_singleLine=”true”
android_textSize=”18sp” />
<TextView
android_layout_width=”match_parent”
android_layout_height=”wrap_content”
android_layout_alignParentLeft=”true”
android_layout_alignParentTop=”true”
android_layout_marginTop=”20dp”
android_gravity=”center”
android_singleLine=”true”
android_text=”检测到有新版本,是否下载?”
android_textSize=”18sp” />
</RelativeLayout>
styles.xml
<style name=”pop_anim_style”>
<item name=”android:windowEnterAnimation”>@anim/pop_in</item>
<item name=”android:windowExitAnimation”>@anim/pop_out</item>
</style>
<style name=”Theme_dialog” parent=”@android:style/Theme.Dialog”>
<item name=”android:windowBackground”>@android:color/transparent</item>
<item name=”android:windowNoTitle”>true</item>
</style>
以及用到的动画
pop_in.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<set xmlns_android=”http://schemas.android.com/apk/res/android” >
<translate
android_duration=”@android:integer/config_shortAnimTime”
android_fromXDelta=”0″
android_fromYDelta=”100%”
android_toXDelta=”0″
android_toYDelta=”0″ />
</set>
pop_out.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<set xmlns_android=”http://schemas.android.com/apk/res/android” >
<translate
android_duration=”@android:integer/config_shortAnimTime”
android_fromXDelta=”0″
android_fromYDelta=”0″
android_toXDelta=”0″
android_toYDelta=”100%” />
</set>
接着我们再执行一下
我们现在再来处理一下没有更新的逻辑,没有更新的话直接跳主页面,我们写一个方法
/**
* 跳转主页面
*/
private void goHome() {
startActivity(new Intent(this, MainActivity.class));
finish();
}
但是这样还是有个问题,他没有更新一下子就跳过去了,所以我们在发消息的时候先让线程睡一会儿
try {
//停留三秒钟
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 全部走完发消息
handler.sendMessage(msg);
但是这里又出现了一个新的问题,毕竟是 络问题,他是耗时的,这样的话,万一等太久了用户体验也上不去啊,所以我们这里要做一个优化
我们在开始 络请求的时间记录一个时间
// 开始访问 络的时间
long startTime = System.currentTimeMillis();
然后再 络请求结束的时候去计算时间并且计算一共用了多少时间
// 络访问结束的时间
long endTime = System.currentTimeMillis();
// 计算 络用了多少时间
long time = endTime – startTime;
try {
if (time < 3000) {
// 停留三秒钟
Thread.sleep(3000 – time);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
4.下载更新以及下载进度
涉及到下载,这里你可以使用很多的开源框架,我这里使用的是xutils
地址:
https://github.com/wyouflf/xUtils
我们下载之后拷贝在libs里就可以用了,用起来也很简单,使用之前先加个权限
<uses-permission android_name=”android.permission.WRITE_EXTERNAL_STORAGE” />
然后开始下载
/**
* 下载更新
*/
private void downloadAPK() {
tv_pro.setVisibility(View.VISIBLE);
// 判断是否有SD卡
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
// 获取手机根目录
String path = Environment.getExternalStorageDirectory()
.getAbsolutePath() + “/TomCatVersion.apk”;
HttpUtils httpUtils = new HttpUtils();
/**
* 1. 络地址 2.存放地址 3.回调
*/
httpUtils.download(url, path, new RequestCallBack<File>() {
// 下载进度
@Override
public void onLoading(long total, long current,
boolean isUploading) {
// TODO Auto-generated method stub
super.onLoading(total, current, isUploading);
// 显示进度
tv_pro.setText(100 * current / total + “%”);
}
// 成功
@Override
public void onSuccess(ResponseInfo<File> responseInfo) {
}
// 失败
@Override
public void onFailure(HttpException error, String msg) {
Log.i(“error”, msg);
}
});
} else {
Toast.makeText(getApplicationContext(), “未找到SD卡”, Toast.LENGTH_LONG)
.show();
}
}
这里的代码逻辑也是十分的简单的,多吧,我们来看一下效果‘
更新的包体积有点小,所以一下子就百分之百了,我们去SD卡更目录看一下
确定是下载完成了,但是下完完之后啥也没发生啊,这就要我们再次优化了
下载完之后软起动
下载完之后自动进入安装界面,这才是真正的体验,我们在onSuccess()方法中
// 跳转系统安装页面
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setDataAndType(Uri.fromFile(new File(path)),”application/vnd.android.package-archive”);
startActivity(i);
我们来看看效果
大致的一个逻辑思路就是这样了完整代码
IndexActivity
package com.lgl.tomcatversion;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import android.widget.Toast;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
/**
* 首页
*
* @author LGL
*
*/
public class IndexActivity extends Activity implements OnClickListener {
// 更新
private static final int UPDATE_YES = 1;
// 不更新
private static final int UPDATE_NO = 2;
// URL错误
private static final int URL_ERROR = 3;
// 没有 络
private static final int IO_ERROR = 4;
// 数据异常
private static final int JSON_ERROR = 5;
private TextView tv_version;
private PackageInfo packageInfo;
private JSONObject jsonObject;
private String versionName;
private int versionCode;
private String content;
private String url;
private TextView tv_pro;
// 升级提示框
private CustomDialog updateDialog;
private TextView dialog_update_content;
private TextView dialog_confrim;
private TextView dialog_cancel;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!