一、更新软件的准备 
在线更新软件的话需要我们有签名的应用,我们需要把签过名之后的软件放入到服务器中,我的如下: 
 
其中apk是有签名的更新版本! 
updateinfo.html代码如下:

{"version":"2.0","description":"有全新版本,请下载!","apkurl":"hhtp://172.23.252.89:8080/MobileSafe2.0.apk"}
  • 1

二、具体客户端软件的实现

项目结构如下:

主要的业务逻辑在这里SplashActivity.java

package com.xuliugen.mobilesafe;

import java.io.ByteArrayOutputStream;
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 net.tsz.afinal.FinalHttp;
import net.tsz.afinal.http.AjaxCallBack; import org.json.JSONException;
import org.json.JSONObject; import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
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.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.TextView;
import android.widget.Toast; /**
* splash界面的作用
* 1、用来展现产品的Logo;
* 2、应用程序初始化的操作;
* 3、检查应用程序的版本;
* 4、检查当前应用程序是否合法注册;
*
*
* 更新安装的话要使用签名
* @author xuliugen
*
*/
public class SplashActivity extends Activity { protected static final int SHOW_UPDATE_DIALOG = 0;
protected static final int ENTER_HOME = 1;
protected static final int URL_ERROR = 2;
protected static final int NETWORK_ERROR = 3;
protected static final int JSON_ERROR = 40; private TextView tv_splash_version;
private TextView tv_update_info;//升级进度 private String description;// 版本信息的描述
private String apkurl;// 版本更新的地址 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash); tv_splash_version = (TextView) this.findViewById(R.id.tv_splash_version);
tv_update_info = (TextView) findViewById(R.id.tv_update_info);
tv_splash_version.setText("版本号:" + getVersionName()); // 检查更新
checkUpdate(); // 设置动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0.2f, 1.0f);
alphaAnimation.setDuration(3000);// 设置动画的时长 //执行动画
findViewById(R.id.rl_root_splash).startAnimation(alphaAnimation);
} /**
* 由于更新界面是在主线程中操作
*
* 所以可以使用handler,当子线程中运行结束的时候们可以通知主线程进行相关的操作
*/
private Handler handler = new Handler(){ @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//得到handler发送的message消息进行处理
switch (msg.what) {
case SHOW_UPDATE_DIALOG:// 显示升级的对话框
showUpdateDialog();
break;
case ENTER_HOME:// 进入主页面
enterHome();
break; case URL_ERROR:// URL错误
enterHome();
Toast.makeText(getApplicationContext(), "URL错误", 0).show(); break; case NETWORK_ERROR:// 网络异常
enterHome();
Toast.makeText(SplashActivity.this, "网络异常", 0).show();
break; case JSON_ERROR:// JSON解析出错
enterHome();
Toast.makeText(SplashActivity.this, "JSON解析出错", 0).show();
break; default:
break;
}
} };
/**
* 检查是否有新版本
*
* 需要请求网络,一般在子线程中使用
*/
private void checkUpdate() {
new Thread() {
public void run() {
// URL:http://172.23.252.89:8080/updateinfo.json Message message = Message.obtain();// 得到一个存在的信息,用于存放更新的信息 long startTime = System.currentTimeMillis();
try {
URL url = new URL(getString(R.string.serverurl));
//URL url = new URL("http://172.23.252.89:8080/updateinfo.json");
// 联网操作
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");//设置请求方式
httpURLConnection.setConnectTimeout(4000);//设置超时时间 int code = httpURLConnection.getResponseCode();// 获得响应码
if (code == 200) {// 成功
InputStream inputStream = httpURLConnection.getInputStream(); //把流转换为一个String类型
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len = inputStream.read(buffer))!=-1){
baos.write(buffer, 0, len);
}
inputStream.close();
String result = baos.toString();//得到string类型的数据
baos.close(); //因为得到的数据是一个json的string所以要json解析
JSONObject jsonObj = new JSONObject(result);
//得到服务器的版本信息
String version = (String) jsonObj.get("version");
description = (String) jsonObj.get("description");
apkurl = (String) jsonObj.get("apkurl"); //校验是否有新版本
if (getVersionName().equals(version)) {// 版本一致,进入主界面
message.what = ENTER_HOME;
} else {// 有新版本,弹出一个升级对话框
message.what = SHOW_UPDATE_DIALOG;
} }
} catch (MalformedURLException e) {
message.what = URL_ERROR;
e.printStackTrace();
} catch (IOException e) {
message.what = NETWORK_ERROR;
e.printStackTrace();
} catch (JSONException e) {
message.what = JSON_ERROR;
e.printStackTrace();
} finally { long endTime = System.currentTimeMillis(); long dTime = endTime-startTime;//花费的时间
//在界面中停留3秒
if(dTime < 3000){
try {
Thread.sleep(3000-dTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
} handler.sendMessage(message);// 将消息发送出去
}
}
}.start();
} /**
* 得到应用层序的版本名称
*
* @return
*/
private String getVersionName() { // 用于管理安装的apk和未安装的apk
PackageManager packageManager = getPackageManager(); try {
// 得到apk的功能清单文件:为了防止出错直接使用getPackageName()方法获得包名
// packageManager.getPackageInfo("com.xuliugen.mobilesafe", 0);
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0); //返回版本名称
return packageInfo.versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
return "";
}
} /**
* 弹出升级对话框
*/
protected void showUpdateDialog() {
AlertDialog.Builder builder = new Builder(this);
builder.setTitle("提示升级");
// builder.setCancelable(false);//强制升级:就是不让用户取消
builder.setMessage(description);//为dialog设置信息 builder.setOnCancelListener(new OnCancelListener() { // @Override
public void onCancel(DialogInterface dialog) {
// 进入主页面
enterHome();
dialog.dismiss(); // 取消显示对话框 }
}); builder.setNegativeButton("下次再说", new OnClickListener() {// 取消 @Override
public void onClick(DialogInterface dialog, int which) {
// 进入主页面
enterHome();
dialog.dismiss(); // 取消显示对话框
}
}); builder.setPositiveButton("立刻升级", new OnClickListener() {//确认 @Override
public void onClick(DialogInterface dialog, int which) {
// 下载APK,并且替换安装
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {//得到sd卡的状态
// sdcard存在
// 使用afnal下载apk文件
FinalHttp finalhttp = new FinalHttp();
// 得到sdcard路径
String SDCARD_PATH = Environment
.getExternalStorageDirectory()
.getAbsolutePath()
+ "/mobilesafe2.0.apk";
finalhttp.download(apkurl, SDCARD_PATH,
new AjaxCallBack<File>() { @Override
public void onFailure(Throwable t, int errorNo,
String strMsg) {
t.printStackTrace();
Toast.makeText(getApplicationContext(), "下载失败", 1).show();
super.onFailure(t, errorNo, strMsg);
}
/**
* count:为总的大小
* current:为当前下载的大小
*/
@Override
public void onLoading(long count, long current) {//正在下载
super.onLoading(count, current);
tv_update_info.setVisibility(View.VISIBLE);
//当前下载百分比
int progress = (int) (current * 100 / count);
tv_update_info.setText("下载进度:"+progress+"%");
} /**
* file t:表示文件的路径
*/
@Override
public void onSuccess(File t) {
super.onSuccess(t); //成功的时候要安装apk
installAPK(t);
} /**
* 安装APK
* @param t :文件下载的路径
*/
private void installAPK(File t) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.fromFile(t), "application/vnd.android.package-archive"); startActivity(intent);
}
});
} else {// sdcard不存在
Toast.makeText(getApplicationContext(), "没有sdcard,请安装上在试",0).show();
return;
} }
}); builder.show(); } /**
* 进入主界面
*/
protected void enterHome() {
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
// 关闭当前页面
finish();
}
}

其中使用的把流转化为一个String类型的方法可以封装为一个工具类:

StreamTools.java

package com.xuliugen.mobilesafe.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream; /**
* 把流转换为一个String类型
*
* @author xuliugen
*
*/
public class StreamTools {
/**
* @param is 输入流
* @return String 返回的字符串
* @throws IOException
*/
public static String readFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
is.close();
String result = baos.toString();
baos.close();
return result;
}
}

注意:我们的签名一定要牢记,但是如果忘记也有方法解决: 
(1)改签名:这将无法覆盖安装,这是包名不变的情况,这就要求用户先卸载以前的应用,体验不佳! 
(2)改包名:重新签名,这就相当于手机安装两个应用,但是可以用技术卸载第一个应用。

Android实现在线更新的过程案例的更多相关文章

  1. android绘制view的过程

    1 android绘制view的过程简单描述  简单描述可以解释为:计算大小(measure),布局坐标计算(layout),绘制到屏幕(draw):            下面看看每一步的动作到底是 ...

  2. 【转】Android绘制View的过程研究——计算View的大小

    Android绘制View的过程研究——计算View的大小 转自:http://liujianqiao398.blog.163.com/blog/static/18182725720121023218 ...

  3. Android Activity的启动过程

    文章编辑的太长了,请移步我的csdn博客:http://blog.csdn.net/xyh269 Android Activity的启动过程原文链接:http://blog.csdn.net/xyh2 ...

  4. 【Android】Android SDK在线更新镜像服务器

    Android SDK在线更新镜像服务器 中国科学院开源协会镜像站地址: IPV4/IPV6: http://mirrors.opencas.cn 端口:80 IPV4/IPV6: http://mi ...

  5. Android应用打包安装过程具体解释

    Android应用打包安装过程(Run as Android Application ): 1,过程:Android Project --> Compilation and Packaging ...

  6. Android系统进程Zygote启动过程的源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6768304 在Android系统中,所有的应用 ...

  7. Android 8 声音调整过程

    记录Android 8声音调整过程. frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.j ...

  8. Android 6.0启动过程具体解析

    在之前的一篇文章中.从概念上学习了Andoird系统的启动过程.Android系统启动过程学习 而在这篇文章中,我们将从代码角度细致学习Android系统的启动过程,同一时候,学习Android启动过 ...

  9. android基础-Apk打包过程(了解)

    此文来源于<Android软件安全与逆向分析> 一.打包资料文件,生成R.java文件. 二.处理aidl文件,生成相应的Java文件. 三.编译工程源代码,生成相应的class文件. 四 ...

随机推荐

  1. CSDN博客栏目设置个性化

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha ====== <a href=" http://weibo.com/23 ...

  2. luoguP3768 简单的数学题

    题目链接 luoguP3768 简单的数学题 题解 上面那个式子的最后一步,需要定理 用数学归纳法证明 \(S1=1^3=1^2\) \(S2=1^3+2^3=9=3^2=(1+2)^2\) \(S3 ...

  3. POJ.2454.Jersey Politics(随机化算法)

    题目链接 \(Description\) 将长为\(3n\)的序列划分成\(3\)个子序列,要求至少有两个子序列的和都\(\geq 500*n\),输出任一方案.保证有解. \(Solution\) ...

  4. [Java]类的生命周期(下)类的初始化[转]

    上接深入java虚拟机——深入java虚拟机(二)——类加载器详解(上),在上一篇文章中,我们讲解了类的生命周期的加载和连接,这一篇我们接着上面往下看. 类的初始化:在类的生命周期执行完加载和连接之后 ...

  5. 移动端web,tap与click事件

    一.tap与click的区别 两者都会在点击时系统自动触发,但是在手机WEB端,click会有 200~300 ms.延迟来自判断双击和长按,因为只有默认等待时间结束以确定没有后续动作发生时,才会触发 ...

  6. Java删除ArrayList中的重复元素

    Java删除ArrayList中的重复元素的2种方法 ArrayList是Java中最常用的集合类型之一.它允许灵活添加多个null元素,重复的元素,并保持元素的插入顺序.在编码时我们经常会遇到那种必 ...

  7. API测试利器postMan 使用教程

    自从开始做API开发之后,我就在寻找合适的API测试工具.一开始不是很想用Chrome扩展,用的 WizTools 的工具,后来试过一次 Postman 之后就停不下来了,还买了付费的Jetpacks ...

  8. 【stanford C++】容器III——Vector类

    主要介绍如下5个容器类——Vector, Stack,Queue,Map和Set,各个都表示一重要的抽象数据类型.另外,各个类都是一些简单类型的值的集合,所以称它们为容器类. 暂且我们先不需要知道它们 ...

  9. 在ASP.NET MVC中使用Knockout实践06,自定义验证、异步验证

    在上一篇中体验了Knockout.Validation的基本验证,本篇体验自定义验证和异步验证. 自定义验证规则 ko.validation有一个rules属性,专门用来存放验证规则,它是一个键值对集 ...

  10. Delphi 包的设计思想及它与PAS、BPL、DCU、DLL、OXC的关系

    一.Delphi中各种文件的介绍,及其关系. OXC: ActiveX控件.会被安装到某一个组件包DPK中,在Imports中会创建PAS.DCU.DCR三个文件. DLL: 动态链接库文件,它的Ex ...