版本更新逻辑
1、联网,从远程服务器获取最新的版本,然后跟本地的版本号比对,如果远程的版本号大于本地的版本号,弹出对话框,有新版本,如果相等,已经是最新版本,不需要更新,本地版本号不可能比远程版本好大,
1、立刻升级
点击立刻升级,把最新的apk下载下来,然后提示更新安装。
2、稍后再说
点击稍后再说,立即进入主界面。
3、版本名、版本号
1、versionCode:给程序员看的
2、versionName:给用户看的
3、版本名规则:eg。6.1.3
1、6.1.C:小版本更新,修改了小bug,做一些微调
2、6.B.3:中等版本更新,更改了比较重要的bug,添加新功能
3、A.1.3:大版本更新,很重要的更新,里程碑意义的更新。
实战
D:\Software-tools\Tomcat\webapps\ROOT\update.txt
里面新建文本update模拟远程版本,在update文本里面写上json格式的数据:
{"remoteVesion":"2"} //版本号为:2
访问成功:
异常处理的原则
1、如果有统一的解决方案,就在方法内部try catch掉
2、如果每次调用处理异常的方式都不同,就直接往外抛(谁调用,谁处理)
版本更新demo
SplashActivity
public class SplashActivity extends Activity {
private static final String TAG = "SplashActivity";
private static final int REQUEST_CODE = 100;
//错误码
protected static final int NET_ERROR = 1001;
protected static final int NET_KEEP = 1002;
private TextView tv_version;
private ProgressDialog progressDialog;
Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case NET_ERROR:
Toast.makeText(getApplicationContext(), "网络错误!", Toast.LENGTH_SHORT).show();
enterHome();
break;
case NET_KEEP:
Toast.makeText(getApplicationContext(), "服务器正在维护!", Toast.LENGTH_SHORT).show();
enterHome();
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
tv_version = (TextView) findViewById(R.id.splash_tv_version);
// 设置版本名
initVersionName();
//版本更新
updateVersion();
}
private void updateVersion() {
final String url = "http://192.168.39.50:8080/update.txt";
//1.访问服务器,获取最新版本信息
new Thread(new Runnable() {
@Override
public void run() {
//01. 定义okhttp
// OkHttpClient okHttpClient_get = new OkHttpClient();
//设置连接超时时间
OkHttpClient okHttpClient_get = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS).build();
//02.请求体
Request request = new Request.Builder()
.get()//get请求方式
.url(url)//网址
.build();
//03.执行okhttp
Response response;
try {
response = okHttpClient_get.newCall(request).execute();
/**
* 打印数据,注意一次请求,一次响应,读取一次就没了,要注释掉
*/
// System.out.println(response.body().string());
String json = response.body().string();
//001.解析
//{"remoteVesion":"2","desc":"1.添加了摇一摇的功能\n2.优化了用户的体验","apkUrl":"http://192.168.39.50:8080/safenet.apk"}
JSONObject jsonObject = new JSONObject(json);
int remoteVesion = jsonObject.getInt("remoteVesion");
String desc = jsonObject.getString("desc");
String url = jsonObject.getString("apkUrl");
final UpdateInfo updateInfo = new UpdateInfo();
updateInfo.remoteVesion = remoteVesion;
updateInfo.desc = desc;
updateInfo.apkUrl = url;
//2.判断最新版本与本地是否一致
int localVersion = PackageUtil.getVersionCode(getApplicationContext());
if (remoteVesion > localVersion) {
//3.弹出提示dialog
//不能在子线程中更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
showUpdateDialog(updateInfo);
}
});
}else{
//跳转到主页
enterHome();
}
} catch (IOException e) {
Message message = mHandler.obtainMessage();
message.what = NET_ERROR;
message.sendToTarget();
e.printStackTrace();
} catch (JSONException e) {
Message message = mHandler.obtainMessage();
message.what = NET_KEEP;
message.sendToTarget();
e.printStackTrace();
}
}
}).start();
}
//进入主页
protected void enterHome() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplashActivity.this, HomeActivity.class);
startActivity(intent);
}
}, 2000);
}
private void showUpdateDialog(final UpdateInfo info) {
AlertDialog.Builder builder = new Builder(SplashActivity.this);
builder.setTitle("版本更新提示");
builder.setCancelable(false);//点击旁边不能消失
builder.setMessage(info.desc);
//4.是否升级
builder.setPositiveButton("立刻升级", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
downApk(info);
}
});
builder.setNegativeButton("稍后再说", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//回到主页
enterHome();
}
});
builder.show();
}
protected void downApk(UpdateInfo info) {
//5.下载最新APK
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
progressDialog = new ProgressDialog(this);
progressDialog.setCancelable(false);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
new Thread(new DownTask(info.apkUrl)).start();
}else{
//回到主页
Toast.makeText(this, "没有SD卡!", Toast.LENGTH_SHORT).show();
enterHome();
}
}
class DownTask implements Runnable{
String mUrl;
public DownTask(String apkUrl) {
this.mUrl = apkUrl;
}
@Override
public void run() {
InputStream is = null;
FileOutputStream fos = null;
try {
Response response = HttpUtil.get(mUrl);
is = response.body().byteStream();
long contentLength = response.body().contentLength();
//01.设置进度条最大值
progressDialog.setMax((int) contentLength);
//路径:mnt/sdcard/
File file = new File(Environment.getExternalStorageDirectory(), "safe.apk");
fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = -1;
int progress = 0;
while((len = is.read(buffer)) != -1){
fos.write(buffer, 0, len);
// SystemClock.sleep(10);
progress += len;
//02.设置进度
progressDialog.setProgress(progress);
}
fos.flush();
//03.进度条消失
progressDialog.dismiss();
//6.提示用户安装
installApk(file);
} catch (IOException e) {
Message message = mHandler.obtainMessage();
message.what = NET_ERROR;
message.sendToTarget();
e.printStackTrace();
} finally{
closeIo(fos);
closeIo(is);
// closeIo2(fos,is);
}
}
}
public void closeIo(Closeable io){
if (io != null) {
try {
io.close();
io = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void installApk(File file) {
// <intent-filter>
// <action android:name="android.intent.action.VIEW" />
// <category android:name="android.intent.category.DEFAULT" />
// <data android:scheme="content" />
// <data android:scheme="file" />
// <data android:mimeType="application/vnd.android.package-archive" />
// </intent-filter>
//7.安装APK
Intent intent = new Intent();
intent.addCategory("android.intent.category.DEFAULT");
intent.setAction("android.intent.action.VIEW");
//获取文件的uri
Uri data = Uri.fromFile(file);
intent.setDataAndType(data, "application/vnd.android.package-archive");
// startActivity(intent);
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
//系统默认的用户行为只有两种:yes no
switch (resultCode) {
case Activity.RESULT_OK:
//***被覆盖安装了,所以不会出日志
Log.d(TAG, "RESULT_OK");
break;
case Activity.RESULT_CANCELED:
Log.d(TAG, "RESULT_CANCELED");
//进入到主页
enterHome();
break;
default:
break;
}
}
}
public void closeIo2(Closeable... io){
for (int i = 0; i < io.length; i++) {
if(io[i] != null){
try {
io[i].close();
io[i] = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void initVersionName() {
// PackageManager pm = this.getPackageManager();
// /**
// * flags:标志位,0标志包的基本信息
// */
// try {
// PackageInfo info = pm.getPackageInfo(getPackageName(), 0);
// String versionName = info.versionName;
//
// tv_version.setText(versionName);
// } catch (NameNotFoundException e) {
// e.printStackTrace();
// }
String versionName = PackageUtil.getVersionName(this);
tv_version.setText(versionName);
}
}
UpdateInfo
public class UpdateInfo {
public int remoteVesion;//远程版本号
public String desc;//版本描述
public String apkUrl;//apk的下载地址
}
HttpUtil
public class HttpUtil {
public static Response get(String url) throws IOException{
//01. 定义okhttp
OkHttpClient okHttpClient_get = new OkHttpClient();
//02.请求体
Request request = new Request.Builder()
.get()//get请求方式
.url(url)//网址
.build();
//03.执行okhttp
Response response = okHttpClient_get.newCall(request).execute();
return response;
}
}
public class PackageUtil {
public static String getVersionName(Context context){
PackageManager pm = context.getPackageManager();
/**
* flags:标志位,0标志包的基本信息
*/
try {
PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
String versionName = info.versionName;
return versionName;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "未知版本名";
}
public static int getVersionCode(Context context){
PackageManager pm = context.getPackageManager();
/**
* flags:标志位,0标志包的基本信息
*/
try {
PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
int versionCode = info.versionCode;
return versionCode;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 1;
}
}