Android automatically detects version updates (including mandatory updates) and installs them

Because it is just a demo test for recording, so the background uses spring+springmvc+dbutils

Database section:

Just such a very simple tb_version table 

v_id: primary key

v_code: version number (usually an integer value)

v_name: version name (eg: 1.0.1)

v_desc: version description introduction

v_down_url: download URL

v_type: 0 for selective update 1 for forced update (no cancel button)

v_date: version release date

Next is the background java part:

@RestController
@RequestMapping("/app")
public class AppVersionController {
	
	@Autowired
	QueryRunner qr;
	
	@RequestMapping("/version")
	public ResultBean checkVersion() throws Exception{
		ResultBean rb=new ResultBean();
		rb.setCode(1);
		String sql="select *from tb_version order by v_date desc limit 1";
		VersionBean vBean=qr.query(sql, new BeanHandler<VersionBean>(VersionBean.class));
		rb.setCode(0);
		rb.setData(vBean);
		return rb;
	}

}

It is very simple to query the latest one and return it to the client

Android code

Layout file xml: activity_main.xml

Here is a text control, which will be used to display the current version number and version name

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

The okgo request framework is used here,

    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    implementation 'com.lzy.net:okgo:3.0.4'
    implementation 'com.google.code.gson:gson:2.8.5'

So customize a MyApplication to initialize

package com.ls.myversion;

import android.app.Application;

import com.lzy.okgo.OkGo;

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
         //初始化okgo
        OkGo.getInstance().init(this);
    }
}

Remember to change the manifest file and the necessary permissions

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 可以跳转并安装apk所需权限,不然不能兼容8.0 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

    <application
        android:name=".MyApplication"
        ...
        />

Write the corresponding entity class VersionBean.class

public class VersionBean {

    private int v_code;
    private String v_name;
    private String v_desc;
    private String v_down_url;
    private int v_type;
    private String v_date;

    //省略get,set,toString()
}

Android core code:

package com.ls.myversion;

import android.Manifest;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.gson.Gson;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.FileCallback;
import com.lzy.okgo.callback.StringCallback;
import com.lzy.okgo.model.Response;

import org.json.JSONObject;

import java.io.File;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv_version)
    TextView tvVersion;

    private Integer v_code;
    private String v_name;
    AlertDialog dialog=null;
    private VersionBean vb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        //读取当前版本的versionCode和versionName
        PackageInfo pf=getMyVersion();
        v_code=pf.versionCode;
        v_name=pf.versionName;
        //显示在文本空间上
        tvVersion.setText(v_code+"=="+v_name);
        //检测是否需要更新apk
        checkVersion();
    }

    private void checkVersion() {
        //向后台服务器发请求,获取最新的apk信息
        OkGo.<String>get("http://192.168.0.107:8080/xxxxx/app/version").execute(new StringCallback() {
            @Override
            public void onSuccess(Response<String> response) {
                //请求成功时
                String result=response.body();
                try {
                    JSONObject jsonObject=new JSONObject(result);
                    int code=jsonObject.getInt("code");
                    if (code==0){
                        Gson gson=new Gson();
                        //把服务器返回的结果转成实体类
                        vb=gson.fromJson(jsonObject.getString("data"),VersionBean.class);
                        //判断服务器返回的本版号是否大于当前的版本号
                        if (vb.getV_code()>v_code){
                            //大于则更新,然后判断是可以取消的选择性更新还是没有取消按钮的强制更新
                            if (vb.getV_type()==0){
                                showSelectDialog(vb);
                            }else {
                                //强制更新
                                showMustDialog(vb);
                            }
                        }
                        //不大于什么都不做...
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
    }
    
    //强制更新时的操作(弹出diglog)
    private void showMustDialog(VersionBean vb) {
        AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
        builder.setCancelable(false);
        String apkUrl=vb.getV_down_url();
        builder.setTitle("版本更新")
                .setMessage(vb.getV_desc())
                .setPositiveButton("更新", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialog.dismiss();
                        //动态权限申请
                        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                        } else {
                            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                            } else {
                                    showDownProgressDialog(vb);
                            }
                        }
                    }
                });
        dialog=builder.create();
        dialog.show();
    }
    
    //下载弹窗
    private void showDownProgressDialog(VersionBean vb) {
        ProgressDialog pd=new ProgressDialog(MainActivity.this);
        pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        pd.setMessage("正在下载安装包,请稍等...");
        pd.setCancelable(false);
        pd.show();
        String path= getSdPath()+"/download";
        OkGo.<File>get(vb.getV_down_url()).execute(new FileCallback(path,"自己命名的新名字.apk") {
            @Override
            public void onSuccess(Response<File> response) {
                Toast.makeText(MainActivity.this,"下载成功!",Toast.LENGTH_SHORT).show();
                pd.dismiss();
                installApk(new File(path+"/自己命名的新名字.apk"));
            }
        });
    }

    private void installApk(File apk) {
        Intent intent=new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(Intent.ACTION_VIEW);
        Uri apkFileUri;
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
            apkFileUri= FileProvider.getUriForFile(MainActivity.this,BuildConfig.APPLICATION_ID+".provider",apk);
        }else{
            apkFileUri=Uri.fromFile(apk);
        }
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(apkFileUri,"application/vnd.android.package-archive");
        Log.i("tag","开始安装");
        try {
            MainActivity.this.startActivity(intent);
//            finish();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private String getSdPath(){
        File sdDir = null;
        boolean sdCardExist=Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        if (sdCardExist){
            sdDir=Environment.getExternalStorageDirectory();
        }
        return sdDir.toString();
    }

    private void showSelectDialog(VersionBean vb) {
        AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
        builder.setCancelable(false);
        String apkUrl=vb.getV_down_url();
        builder.setTitle("版本更新")
                .setMessage(vb.getV_desc())
                .setPositiveButton("更新", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialog.dismiss();
                        //动态权限申请
                        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                        } else {
                            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                            } else {
                                showDownProgressDialog(vb);
                            }
                        }
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialog.dismiss();
                    }
                });
        dialog=builder.create();
        dialog.show();
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                    } else {
                        showDownProgressDialog(vb);
                    }
                } else {
                    Toast.makeText(this, "你拒绝了无法下载哟!", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }

    private PackageInfo getMyVersion() {
        PackageInfo pf=null;
        try {
            PackageManager pm=getPackageManager();
            pf=pm.getPackageInfo(getPackageName(),0);
        }catch (Exception e){
            e.printStackTrace();
        }
        return pf;
    }
}

Since Android 7.0 uses FileProvider to access sdk private files, it is necessary to create a FileProvider

1. Write like this in the manifest file

<application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyVersion">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="自己的包名(com.ls.myversion).provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
    </application>

2. Create xml folders and files under res

The file content is:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <!--path:需要临时授权访问的路径(.代表所有路径) name:就是你给这个访问路径起个名字-->
    <external-path
        name="external_files"
        path="." />
</paths>

So far it's all done

Renderings:

The code at this time:

When an update is detected:

The renderings of the mandatory version will not be displayed, after clicking Update

will start the download and then start the installation

My personal creation, if there is any similarity, it is purely coincidental, or contact me to make changes. Please reprint or CV combination to indicate the source, thank you! (If you have any questions or mistakes, please point them out, my QQ:752231513)

Guess you like

Origin blog.csdn.net/qq_30548105/article/details/118335923