zoukankan      html  css  js  c++  java
  • 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)改包名:重新签名,这就相当于手机安装两个应用,但是可以用技术卸载第一个应用。

  • 相关阅读:
    TcpClient连接帮助类
    winform中添加管理员权限控制
    textbox中输入email做格式控制
    绑定和非绑定方法
    类的封装、property、多态和多态性
    继承类之点点滴滴
    爬虫日记-正则表达式
    爬虫日记-单元总结
    爬虫日记-最好大学排名实例
    爬虫日记-第一单元总结
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/9102068.html
Copyright © 2011-2022 走看看