Android はバージョン アップデート (必須アップデートを含む) を自動的に検出し、インストールします。

録画用のデモテストなのでバックグラウンドでは spring+springmvc+dbutils を使用しています。

データベースセクション:

非常に単純なtb_versionテーブル 

v_id: 主キー

v_code: バージョン番号 (通常は整数値)

v_name: バージョン名 (例: 1.0.1)

v_desc: バージョンの説明の紹​​介

v_down_url: ダウンロード URL

v_type: 選択的更新の場合は 0、強制更新の場合は 1 (キャンセル ボタンなし)

v_date: バージョンのリリース日

次はバックグラウンドの Java 部分です。

@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;
	}

}

最新のものをクエリしてクライアントに返すのは非常に簡単です

Androidコード

レイアウト ファイル XML: activity_main.xml

これは、現在のバージョン番号とバージョン名を表示するために使用されるテキスト コントロールです。

<?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>

ここでは okgo リクエスト フレームワークが使用されています。

    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'

したがって、MyApplication をカスタマイズして初期化します

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);
    }
}

マニフェスト ファイルと必要な権限を忘れずに変更してください

<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"
        ...
        />

対応するエンティティクラス 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 コアコード:

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;
    }
}

Android 7.0ではSDKプライベートファイルへのアクセスにFileProviderを使用するため、FileProviderを作成する必要があります。

1.マニフェストファイルに次のように記述します

<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. res の下に XML フォルダーとファイルを作成します

ファイルの内容は次のとおりです。

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

これまでのところ、すべて完了しました

レンダリング:

この時のコード:

アップデートが検出された場合:

「更新」をクリックした後、必須バージョンのレンダリングは表示されません。

ダウンロードが開始され、その後インストールが開始されます

私の個人的な創作物で、類似点がある場合、それは全くの偶然であるか、変更を加えるために私に連絡してください。出典を明記して転載またはCVの組み合わせをお願いします。ありがとうございます! (ご質問や間違いがございましたら、ご指摘ください、私の QQ:752231513)

おすすめ

転載: blog.csdn.net/qq_30548105/article/details/118335923