Android进阶之路 - 版本检测且自动升级

写项目,版本升级这种功能用到的地方太多了~

文章结构 :

  • 俩种升级方式
  • 所遇问题解析

俩种升级方式:

Effect :
这里写图片描述

权限导入

    <uses-permission android:name="android.permission.READ_PROFILE"/>
    <uses-permission android:name="android.permission.WRITE_PROFILE"/>
    <uses-permission android:name="android.permission.INTERNET"/>

方法归纳

  • 获取版本号
   /**
     * 返回版本号
     * 对应build.gradle中的versionCode
     * @param context context
     * @return String
     */
    public static String getVersionCode(Context context) {
        String versionCode = "";
        try {
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
            versionCode = String.valueOf(packInfo.versionCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return versionCode;
    }
  • 根据当前与线上版本进行比较,然后弹出是否升级提示框(提示框可用系统提供的,也可以用自定义的,这个主要看UI,UE如何决定,下面附带我的弹框)
/**
     * 弹出提示更新的dialog
     */
    private void showUpdateDialog() {
        CustomDialog customDialog = new CustomDialog(SettingActivity.this);
        customDialog.setMsg("检查到有最新版本,是否更新?");
        customDialog.setTitle("版本更新提示");
        customDialog.setCancelText("暂不更新");
        customDialog.setConfirmText("立刻更新");
        customDialog.setOnSureClickListener(new CustomDialog.OnSureClickListener() {
            @Override
            public void sureClick() {
                //下载APK
                downloadApk();
            }
        });
        customDialog.show();
    }

CustomDialog

Effect :

这里写图片描述

Code:

package com.bakheet.garage.widget;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.TextView;

import com.bakheet.garage.R;
import com.bakheet.garage.utils.ToolUtil;

/**
 * date  2017/11/27.
 * desc:
 */

public class CustomDialog extends Dialog {

    private TextView tvTitle;
    private TextView tvMsg;
    private TextView tvCancel;
    private TextView tvConfirm;
    private String sureString, titleString, msgString, msgPeople, cancelText, confirmText;

    private OnSureClickListener onSureClickListener;
    private OnCancelClickListener onCancelClickListener;
    private int msgColor;

    public CustomDialog(@NonNull Context context) {
        super(context, R.style.CarDialog);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_custom_view);
        //setCanceledOnTouchOutside(false);
        initView();
        initData();
        initEvent();
    }

    private void initView() {
        tvTitle = (TextView) findViewById(R.id.tv_title);
        tvMsg = (TextView) findViewById(R.id.tv_msg);
        tvCancel = (TextView) findViewById(R.id.tv_cancel);
        tvConfirm = (TextView) findViewById(R.id.tv_confirm);
    }

    private void initData() {
    //这里的ToolUtil.isStringNull相当于 TextUtils.empty()的判空
        if (!ToolUtil.isStringNull(sureString)) {
            tvConfirm.setText(sureString);
        }
        if (!ToolUtil.isStringNull(titleString)) {
            tvTitle.setVisibility(View.VISIBLE);
            tvTitle.setText(titleString);
        } else {
            tvTitle.setVisibility(View.GONE);
        }
        if (!ToolUtil.isStringNull(msgString)) {
            tvMsg.setText(msgString);
        }
        if (msgColor != 0) {
            tvMsg.setTextColor(msgColor);
        }
        if (!ToolUtil.isStringNull(sureString)) {
            tvConfirm.setText(sureString);
        }
        if (!ToolUtil.isStringNull(cancelText)) {
            tvCancel.setText(cancelText);
        } else {
            tvCancel.setText("取消");
        }

        if (!ToolUtil.isStringNull(confirmText)) {
            tvConfirm.setText(confirmText);
        } else {
            tvConfirm.setText("确定");
        }
    }

    public void setCancelText(String text) {
        this.cancelText = text;
    }

    public void setConfirmText(String text) {
        this.confirmText = text;
    }

    public void setTitle(String title) {
        this.titleString = title;
    }

    public void setMsgColor(int color) {
        this.msgColor = color;
    }

    public void setMsg(String msg) {
        this.msgString = msg;
    }

    public void setMsgPeople(String msgPeople) {
        this.msgPeople = msgPeople;
    }

    public void setSureString(String sureString) {
        this.sureString = sureString;
    }

    private void initEvent() {
        tvCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onCancelClickListener != null) {
                    onCancelClickListener.cancelClick();
                }
                CustomDialog.this.dismiss();
            }
        });
        tvConfirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onSureClickListener != null) {
                    onSureClickListener.sureClick();
                    CustomDialog.this.dismiss();
                }
            }
        });
    }


    /**
     * 设置确定按钮和取消被点击的接口
     */
    public interface OnSureClickListener {
        void sureClick();
    }

    public interface OnCancelClickListener {
        void cancelClick();
    }


    /**
     * 设置取消按钮的显示内容和监听
     */
    public void setNoOnclickListener(OnCancelClickListener onCancelClickListener) {
        this.onCancelClickListener = onCancelClickListener;
    }

    /**
     * 设置确定按钮的显示内容和监听
     */
    public void setOnSureClickListener(OnSureClickListener onSureClickListener) {
        this.onSureClickListener = onSureClickListener;
    }

}

dialog_custom_view

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/bg_rec_r3_white"
        android:minWidth="270dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="@dimen/d10"
            android:text="版本升级"
            android:textColor="@color/gray2"
            android:textSize="@dimen/s18"
            android:visibility="visible"/>


        <TextView
            android:id="@+id/tv_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="@dimen/d10"
            android:textColor="@color/black3"
            tools:text="If you are happy ,you can do everything"/>

        <LinearLayout
            android:layout_marginTop="@dimen/d15"
            android:layout_width="match_parent"
            android:layout_height="@dimen/d45"
            android:background="@drawable/bg_rec_r4_down"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_cancel"
                android:layout_width="@dimen/d0"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="@string/dialog_cancel"
                android:textColor="@color/gray3"
                android:textSize="@dimen/s14"/>

            <View
                android:layout_width="@dimen/d1"
                android:layout_height="match_parent"
                android:layout_marginBottom="@dimen/d12"
                android:layout_marginTop="@dimen/d12"
                android:background="@color/gray4"/>

            <TextView
                android:id="@+id/tv_confirm"
                android:layout_width="@dimen/d0"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="@string/dialog_confirm"
                android:textColor="@color/blue1"
                android:textSize="@dimen/s14"/>
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>
  • 从服务器端下载最新apk
/**
     * 从服务器端下载最新apk
     */
    private void downloadApk() {
        //显示下载进度
        ProgressDialog dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setProgressNumberFormat("");
        dialog.setCancelable(false);
        dialog.show();

        //访问网络下载apk
        new Thread(new DownloadApk(dialog)).start();
    }
  • 访问网络下载apk
 private class DownloadApk implements Runnable {
        private ProgressDialog dialog;
        InputStream is;
        FileOutputStream fos;

        public DownloadApk(ProgressDialog dialog) {
            this.dialog = dialog;
        }

        @Override
        public void run() {
            OkHttpClient client = new OkHttpClient();
            String url = "https://raw.githubusercontent.com/WVector/AppUpdateDemo/master/apk/app-debug.apk";
            Request request = new Request.Builder().get().url(url).build();
            try {
                okhttp3.Response response = client.newCall(request).execute();
                if (response.isSuccessful()) {
                    Log.d("tag", "开始下载apk");
                    //获取内容总长度
                    long contentLength = response.body().contentLength();
                    //设置最大值
                    dialog.setMax((int) contentLength);
                    //保存到sd卡
                    File apkFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".apk");
                    fos = new FileOutputStream(apkFile);
                    //获得输入流
                    is = response.body().byteStream();
                    //定义缓冲区大小
                    byte[] bys = new byte[1024];
                    int progress = 0;
                    int len = -1;
                    while ((len = is.read(bys)) != -1) {
                        try {
                            Thread.sleep(1);
                            fos.write(bys, 0, len);
                            fos.flush();
                            progress += len;
                            //设置进度
                            dialog.setProgress(progress);
                        } catch (InterruptedException e) {
                            Message msg = Message.obtain();
                            msg.what = SHOW_ERROR;
                            msg.obj = "ERROR:10002";
                            handler.sendMessage(msg);
                            //                            load2Login();
                        }
                    }
                    //下载完成,提示用户安装
                    installApk(apkFile);
                }
            } catch (IOException e) {
                Message msg = Message.obtain();
                msg.what = SHOW_ERROR;
                msg.obj = "ERROR:10003";
                handler.sendMessage(msg);
                //                load2Login();
            } finally {
                //关闭io流
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    is = null;
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    fos = null;
                }
            }
            dialog.dismiss();
        }
    }
  • 下载完成,提示用户安装
 private void installApk(File file) {
        //调用系统安装程序
        Intent intent = new Intent();
        intent.setAction("android.intent.action.VIEW");
        intent.addCategory("android.intent.category.DEFAULT");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        startActivityForResult(intent, 119);
    }

完整的Activity代码(不含XML)

package com.bakheet.garage.mine.activity;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.bakheet.garage.R;
import com.bakheet.garage.base.ActivityCollector;
import com.bakheet.garage.base.BaseActivity;
import com.bakheet.garage.http.HttpManager;
import com.bakheet.garage.http.ObjectResult;
import com.bakheet.garage.utils.DeviceUtils;
import com.bakheet.garage.utils.SpUtil;
import com.bakheet.garage.utils.TlogUtils;
import com.bakheet.garage.utils.ToastUtils;
import com.bakheet.garage.widget.CustomDialog;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import butterknife.BindView;
import butterknife.OnClick;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.Call;
import retrofit2.Response;

/**
 * @author YongLiu
 * @date 2017/11/27
 */
public class SettingActivity extends BaseActivity {
    @BindView(R.id.rl_version)
    RelativeLayout mRlVersion;
    private String versionCode;
    private String backGroundVersion = "5";
    public static final int SHOW_UPDATE_DIALOG = 0;
    public static final int SHOW_ERROR = 1;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_setting;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        setToolBarTitle(getString(R.string.title_setting));
        //这里可以忽视,按理这里是请求服务器后进行的设置,我只是为了自测方便
        versionCode = DeviceUtils.getVersionCode(this);
        if (Integer.parseInt(versionCode) < Integer.parseInt(backGroundVersion)) {
            mVersion.setText("最新版本为 V1.2 ,请更新");
            mRlVersion.setEnabled(true);
        } else {
            mVersion.setText("您当前已是最新版本");
            mRlVersion.setEnabled(false);
        }
    }

//成功与失败状态下的回调 ,看自己的需求
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                /** 弹出提示更新的dialog   */
                case SHOW_UPDATE_DIALOG:
                   // showUpdateDialog();
                    break;

                /** 提示错误    */
                case SHOW_ERROR:
                    Toast.makeText(SettingActivity.this, msg.obj+"", Toast.LENGTH_LONG).show();
                    //load2Login();
                    break;

                default:
                    break;
            }
        }
    };

    @OnClick({R.id.rl_update_password, R.id.tv_quit_account, R.id.rl_msg_inform, R.id.rl_version})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.rl_version:
                showUpdateDialog();
                break;
            default:
                break;
        }
    }


    /**
     * 弹出提示更新的dialog
     */
    private void showUpdateDialog() {
        CustomDialog customDialog = new CustomDialog(SettingActivity.this);
        customDialog.setMsg("检查到有最新版本,是否更新?");
        customDialog.setTitle("版本更新提示");
        customDialog.setCancelText("暂不更新");
        customDialog.setConfirmText("立刻更新");
        customDialog.setOnSureClickListener(new CustomDialog.OnSureClickListener() {
            @Override
            public void sureClick() {
                //下载APK
                downloadApk();
            }
        });
        customDialog.show();
    }

    /**
     * 从服务器端下载最新apk
     */
    private void downloadApk() {
        //显示下载进度
        ProgressDialog dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setProgressNumberFormat("");
        dialog.setCancelable(false);
        dialog.show();

        //访问网络下载apk
        new Thread(new DownloadApk(dialog)).start();
    }

    /**
     * 访问网络下载apk
     */
    private class DownloadApk implements Runnable {
        private ProgressDialog dialog;
        InputStream is;
        FileOutputStream fos;

        public DownloadApk(ProgressDialog dialog) {
            this.dialog = dialog;
        }

        @Override
        public void run() {
            OkHttpClient client = new OkHttpClient();
            String url = "https://raw.githubusercontent.com/WVector/AppUpdateDemo/master/apk/app-debug.apk";
            Request request = new Request.Builder().get().url(url).build();
            try {
                okhttp3.Response response = client.newCall(request).execute();
                if (response.isSuccessful()) {
                    Log.d("tag", "开始下载apk");
                    //获取内容总长度
                    long contentLength = response.body().contentLength();
                    //设置最大值
                    dialog.setMax((int) contentLength);
                    //保存到sd卡
                    File apkFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".apk");
                    fos = new FileOutputStream(apkFile);
                    //获得输入流
                    is = response.body().byteStream();
                    //定义缓冲区大小
                    byte[] bys = new byte[1024];
                    int progress = 0;
                    int len = -1;
                    while ((len = is.read(bys)) != -1) {
                        try {
                            Thread.sleep(1);
                            fos.write(bys, 0, len);
                            fos.flush();
                            progress += len;
                            //设置进度
                            dialog.setProgress(progress);
                        } catch (InterruptedException e) {
                            Message msg = Message.obtain();
                            msg.what = SHOW_ERROR;
                            msg.obj = "ERROR:10002";
                            handler.sendMessage(msg);
                            //                            load2Login();
                        }
                    }
                    //下载完成,提示用户安装
                    installApk(apkFile);
                }
            } catch (IOException e) {
                Message msg = Message.obtain();
                msg.what = SHOW_ERROR;
                msg.obj = "ERROR:10003";
                handler.sendMessage(msg);
                //                load2Login();
            } finally {
                //关闭io流
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    is = null;
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    fos = null;
                }
            }
            dialog.dismiss();
        }
    }

    /**
     * 下载完成,提示用户安装
     */
    private void installApk(File file) {
        //调用系统安装程序
        Intent intent = new Intent();
        intent.setAction("android.intent.action.VIEW");
        intent.addCategory("android.intent.category.DEFAULT");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        startActivityForResult(intent, 119);
    }


所遇问题解析:

  • 当Android版本为7.0或7.0以上系统的时候,安装Apk时报出FileUriExposedException

解决方案:
1.清单文件加入以下代码

  <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="我们的包名.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>

2.找到Xml下的 file_path.xml 修改代码
这里写图片描述

file_path.xml :

<?xml version="1.0" encoding="utf-8"?>
<paths>

    <external-path path="Android/data/我们的包名/" name="files_root" />
    <external-path path="." name="external_storage_root" />
</paths>

3.修改 installApk 方法

 /**
     * 下载完成,提示用户安装  
     * file 为File文件 或 fileName 主要看你上一个方法有没有转文件名
     */
    private void installApk(File file) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(this, "我们的包名.fileprovider", file);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setDataAndType(Uri.parse("file://" + file),
                    "application/vnd.android.package-archive");
        }
        startActivityForResult(intent, 119);
    }

借鉴文章:
Android 7.0 安装Apk时报错FileUriExposedException 解决

  • 安装完成,重新启动app

注意 :
Android 部分手机通过下面这个方法无法实现跳转,可能是6.0,7.0这种系统原因,可能是机型的问题

步骤:
1.安装完成之后startActivityForResult,如:

  startActivityForResult(intent, 119);

2.重写onActivityReenter

  @Override
    public void onActivityReenter(int resultCode, Intent data) {
        super.onActivityReenter(resultCode, data);
        if (resultCode == 119) {
            startAPP("我们的包名");
        }
    }

3.app唤醒方法

public void startAPP(String appPackageName) {
        try {
            Intent intent = this.getPackageManager().getLaunchIntentForPackage(appPackageName);
            startActivity(intent);
        } catch (Exception e) {
            Toast.makeText(this, "没有安装", Toast.LENGTH_LONG).show();
        }
    }

借鉴文章 :
Android 通过包名打开App的代码

猜你喜欢

转载自blog.csdn.net/qq_20451879/article/details/79990543