Android下载apk并安装apk(用于软件版本升级用途)

软件版本更新是每个应用必不可少的功能,基本实现方案是请求服务器最新的版本号与本地的版本号对比,有新版本则下载apk并执行安装。请求服务器版本号与本地对比很容易,本文就不过多讲解,主要讲解下载apk到安装apk的内容。

一、所需权限

<!--请求安装APK的权限-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!--写如外部存储的权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--读取外部存储的权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>
(1)读写外部存储的权限需要动态申请,详见:Android动态获取权限
(2)安装apk的权限从Android8.0开始需要每个应用独立开启
//跳转到开启apk安装权限开启的界面,让用户手动打开
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,Uri.parse("package:" +getPackageName()));
intentActivityResultLauncher.launch(intent);

二、代码实现

(1)注册provider

在AndroidManifest.xml中声明provider

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="你的包名">

    <!--省略属性。。。-->
    
    <application
        省略属性。。。>

        <activity
        省略属性。。。>

        <!--声明provider-->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="你的包名.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>

    </application>

</manifest>

在res的xml目录增加filepaths.xml
在这里插入图片描述
filepaths.xml中配置path路径

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <path>
        <root-path name="files_apk"
            path="/"/>
    </path>
</paths>
(2)动态申请权限基础BaseActivity

这个类在另外一篇文章中讲解,主要为了方便动态获取权限。

package com.soface.versioncontroll;

import android.content.pm.PackageManager;
import android.os.Build;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;

public class BaseActivity extends AppCompatActivity {

    public static final int REQUEST_CONDE =0xFFFF;

    /**
     * 动态请求权限(入口)
     * @param permissionNameList 需要授权的权限名称
     */
    public void requestPermission(List<String> permissionNameList){

        // TODO: 2023/2/22 第一步:排除(已经获得过授权的权限)=============================================================
        List<String> UnauthorizedPermissionNameList = new ArrayList<>();//用于存放未获得授权的权限
        for (String permission : permissionNameList){
            //检查每个权限是否已经获得授权
            int checkResult=ContextCompat.checkSelfPermission(this,permission);
            if (checkResult==PackageManager.PERMISSION_GRANTED){
                //已获得过授权,直接抛出结果true
                throwPermissionResults(permission,true);
            }else if (checkResult==PackageManager.PERMISSION_DENIED) {
                //未获得授权,把未获得授权的权限加入到thisPermissionNames中,待下一步请求
                UnauthorizedPermissionNameList.add(permission);
            }else {
                //按道理,这里永远不会发生,
                //因为checkSelfPermission方法说得很清楚,只会返回PERMISSION_GRANTED或者PERMISSION_DENIED
                //但是为了严谨,以防万一,还是给它抛出结果false
                throwPermissionResults("Unknown_result",false);
            }
        }
        if (UnauthorizedPermissionNameList.size()==0)return;//表示:全部已经拥有全选,不用往下执行

        // TODO: 2023/2/22 第二步:开始申请权限==========================================================================
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //由于请求权限的参数必须是String[],所以List转到String[]中
            String[] UnauthorizedPermissionNames=new String[UnauthorizedPermissionNameList.size()];
            for (int k=0;k<UnauthorizedPermissionNameList.size();k++){
                UnauthorizedPermissionNames[k]=UnauthorizedPermissionNameList.get(k);
            }
            //请求权限
            ActivityCompat.requestPermissions(this, UnauthorizedPermissionNames, REQUEST_CONDE);
        }else {
            //低版本的Android不需要动态获取权限,这里直接抛出结果true
            throwPermissionResults("Below_VERSION_M",true);
        }
    }

    /**
     * 抛出授权结果(出口)
     * @param permissionName 权限名称
     * @param isSuccess 该权限是否获得授权(true:获得授权  false:未获得授权)
     */
    public void throwPermissionResults(String permissionName, boolean isSuccess){
        // TODO: 2023/2/22 这里如果isSuccess=false,可以自定义一个弹窗,让用户选择
        //  到底是要直接退出应用,还是去设置中开启权限,本文主要是总结动态获取权限,所以弹窗笔者就不写了
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        //判断我们的请求码,避免别的事件调用onRequestPermissionsResult,导致我们拿到本不该属于我们的数据
        if (requestCode==REQUEST_CONDE){
            // 如果请求被取消,则结果数组为空。
            if (grantResults.length > 0) {
                //循环一个一个地去判断结果
                for (int k=0;k<permissions.length;k++){

                    if (grantResults[k] == PackageManager.PERMISSION_GRANTED){
                        // 权限请求成功,抛出结果true
                        throwPermissionResults(permissions[k],true);
                    }

                    if (grantResults[k] == PackageManager.PERMISSION_DENIED){
                        // 权限请求失败,抛出结果false
                        throwPermissionResults(permissions[k],false);
                    }
                }
            } else {
                //没有任何授权结果,直接抛出结果false
                throwPermissionResults("Unknown_result",false);
            }
        }
    }

}
(3)判断需不需要升级最新软件的MainActivity
package com.soface.versioncontroll;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends BaseActivity{


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //动态请求权限
        List<String> perList=new ArrayList<>();
        perList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        perList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        perList.add(Manifest.permission.INTERNET);
        requestPermission(perList);

        //初始化结果返回接听
        initActivityResult();

        Button permission=(Button) findViewById(R.id.permission);
        permission.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            	//当判断需要升级最新软件,则调用这个方法,这里为了方便测试,放在点击事件中
                openSetting();
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stop();
    }

    @Override
    public void throwPermissionResults(String permissionName, boolean isSuccess) {
        super.throwPermissionResults(permissionName, isSuccess);
        //拿到相应的权限,以及授权结果
        switch (permissionName){
            case Manifest.permission.WRITE_EXTERNAL_STORAGE:
                Log.d("fxHou","WRITE_EXTERNAL_STORAGE授权结果:"+isSuccess);
                break;
            case Manifest.permission.READ_EXTERNAL_STORAGE:
                Log.d("fxHou","READ_EXTERNAL_STORAGE授权结果:"+isSuccess);
                break;
            default:
                break;
        }
    }

    public void openSetting() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //Android 8.0以上
            if(!getPackageManager().canRequestPackageInstalls()){
                //权限没有打开,跳转界面,提示用户去手动打开
                Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,Uri.parse("package:" +getPackageName()));
                intentActivityResultLauncher.launch(intent);
            }else {
                //已经拥有权限,直接执行下载apk操作
                start();
            }
        }else {
            //开始下载安装
            start();
        }
    }
    private ActivityResultLauncher<Intent> intentActivityResultLauncher;
    private void initActivityResult() {
        intentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == AppCompatActivity.RESULT_OK) {
                //开始下载安装
                start();
            }
        });
    }


    VersionControl versionControl;
    String downloadUrl="http://www.soface.top:8080/source/Public/ApkVersionControl/chart.apk";
    String titleStr="麦麦商家版V1.1.2";
    String contentStr="正在下载中,请耐心等待";
    //开始执行版本更新操作
    public void start(){
        //初始化版本控制
        versionControl=new VersionControl();
        versionControl.download(this,downloadUrl,titleStr,contentStr);
        versionControl.registerReceiver(this);
    }
    //停止执行版本更新操作
    public void stop(){
        //初始化版本控制
        versionControl.unRegisterReceiver(MainActivity.this);
        versionControl=null;
    }
}
(4)下载apk和安装apk的实现类
package com.soface.versioncontroll;

import static android.content.Context.DOWNLOAD_SERVICE;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;

import java.io.File;

public class VersionControl {

    //第一步: 下载APK
    private long downloadId=-1;
    private DownloadManager downloadManager;
    public void download(Context context,String url,String titleStr,String contentStr) {
        //创建下载任务
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        //在通知栏中显示,默认就是显示的
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
        request.setTitle(titleStr);
        request.setDescription(contentStr);
        //设置下载的路径
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "chart.apk");
        request.setDestinationUri(Uri.fromFile(file));
        file.getAbsolutePath();
        //获取DownloadManager
        downloadManager = (DownloadManager)context.getSystemService(DOWNLOAD_SERVICE);
        //将下载请求放入队列
        downloadId = downloadManager.enqueue(request);
    }

    //第二步: 监听下载结果
    private BroadcastReceiver broadcastReceiver;
    public void registerReceiver(Context context) {
        // 注册广播监听系统的下载完成事件。
        IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                long thisDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                if (thisDownloadId!=-1 && downloadId!=-1){
                    if (thisDownloadId == downloadId) {
                        //下载完成,检查下载状态
                        checkStatus(context);
                    }
                }
            }
        };
        context.registerReceiver(broadcastReceiver, intentFilter);
    }
    public void unRegisterReceiver(Context context){
        if (broadcastReceiver!=null) {
            context.unregisterReceiver(broadcastReceiver);
        }
    }

    //第三部: 检查下载状态,是否下载成功
    @SuppressLint("Range")
    private void checkStatus(Context context) {

        DownloadManager.Query query = new DownloadManager.Query();
        // 执行查询, 返回一个 Cursor (相当于查询数据库)
        Cursor cursor = downloadManager.query(query);
        if (!cursor.moveToFirst()) {
            cursor.close();
        }
        int id = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
        //通过下载的id查找
        query.setFilterById(id);

        // 获取下载好的 apk 路径
        String localFilename = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
        } else {
            localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
        }

        if (cursor.moveToFirst()) {
            int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
            switch (status) {
                case DownloadManager.STATUS_PAUSED:
                    //下载暂停
                    Log.d("fxHou","下载暂停");
                    break;
                case DownloadManager.STATUS_PENDING:
                    //下载延迟
                    Log.d("fxHou","下载延迟");
                    break;
                case DownloadManager.STATUS_RUNNING:
                    //正在下载
                    Log.d("fxHou","正在下载");
                    break;
                case DownloadManager.STATUS_SUCCESSFUL:
                    //下载完成安装APK
                    installApk(context,localFilename);
                    cursor.close();
                    break;
                case DownloadManager.STATUS_FAILED:
                    //下载失败
                    Log.d("fxHou","下载失败");
                    cursor.close();
                    break;
                default:
                    break;
            }
        }
    }

    //第四部: 安装apk
    private void installApk(Context context,String path) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        File file = new File(Uri.parse(path).getPath());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri uri = FileProvider.getUriForFile(context, "你的包名.fileprovider", file);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }

}
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Android 版本升级的代码主要涉及以下几个方面: 1. 检查当前版本号和最新版本号是否一致; 2. 判断是否需要进行版本升级; 3. 弹出对话框提示用户是否进行版本升级; 4. 开始下载最新版本的 APK 文件; 5. 安装最新版本的 APK 文件。 下面是一个基本的 Android 版本升级的代码示例: ```java public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int REQUEST_WRITE_EXTERNAL_STORAGE = 1; private static final String DOWNLOAD_URL = "http://example.com/app.apk"; private int currentVersionCode; private int latestVersionCode; private String latestVersionName; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); currentVersionCode = BuildConfig.VERSION_CODE; // 获取最新版本号和版本名,可以通过网络接口或者本地文件获取 latestVersionCode = 2; latestVersionName = "1.1.0"; // 判断是否需要进行版本升级 if (latestVersionCode > currentVersionCode) { showUpgradeDialog(); } } private void showUpgradeDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("版本升级"); builder.setMessage("发现新版本 " + latestVersionName + ",是否立即升级?"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestWriteExternalStoragePermission(); } }); builder.setNegativeButton("取消", null); builder.show(); } private void requestWriteExternalStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { downloadApk(); } else { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_EXTERNAL_STORAGE); } } else { downloadApk(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { downloadApk(); } } } private void downloadApk() { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(DOWNLOAD_URL)); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "app.apk"); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); downloadManager.enqueue(request); } private void installApk() { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/Download/app.apk")), "application/vnd.android.package-archive"); startActivity(intent); } // 在下载完成后调用 private void onDownloadComplete(long downloadId) { DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); Cursor cursor = downloadManager.query(query); if (cursor.moveToFirst()) { int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); if (status == DownloadManager.STATUS_SUCCESSFUL) { installApk(); } } cursor.close(); } // 在 Activity 的 onResume 方法中调用 private void checkDownloadStatus() { DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query(); query.setFilterByStatus(DownloadManager.STATUS_RUNNING | DownloadManager.STATUS_PENDING); Cursor cursor = downloadManager.query(query); if (cursor.moveToFirst()) { long downloadId = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)); onDownloadComplete(downloadId); } cursor.close(); } } ``` 在实际开发中,需要根据具体的需求进行相应的修改和优化。需要注意的是,Android 6.0 及以上版本需要动态申请存储权限。另外,需要根据实际情况修改下载文件的 URL 和文件名。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝命三郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值