当前位置: 代码迷 >> Android >> Android实现在线更新的进程案例
  详细解决方案

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

热度:86   发布时间:2016-04-28 02:01:29.0
Android实现在线更新的过程案例

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

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

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

项目结构如下:

这里写图片描述

主要的业务逻辑在这里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;    }}

其他界面请参考源代码:http://yunpan.cn/cZd37WYYcp6mI (提取码:2a38)

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

  相关解决方案