Android实现在线更新的过程案例

一、更新软件的准备 
在线更新软件的话需要我们有签名的应用,我们需要把签过名之后的软件放入到服务器中,我的如下: 
这里写图片描述 
其中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)改包名:重新签名,这就相当于手机安装两个应用,但是可以用技术卸载第一个应用。

猜你喜欢

转载自www.cnblogs.com/wanghuaijun/p/9102068.html