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

这篇具有很好参考价值的文章主要介绍了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
Android下载apk并安装apk(用于软件版本升级用途)
filepaths.xml中配置path路径

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

这个类在另外一篇文章中讲解,主要为了方便动态获取权限。文章来源地址https://www.toymoban.com/news/detail-427791.html

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

}

到了这里,关于Android下载apk并安装apk(用于软件版本升级用途)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在Android Studio通过adb命令强制安装debug版本apk到手机,且允许version code降级

    在Android Studio通过adb命令强制安装debug版本apk到手机,且允许version code降级 切换到Terminal: .appbuildintermediatesapkdebug是android studio的apk编译生成路径。 参数: -t   允许安装测试包 -d   运行version code 降级 -r   取代现有application -g  授予所有权限       adb命令导出手机已安

    2024年02月14日
    浏览(52)
  • 【代码阅读软件】VSCode最新版本 下载、安装、配置

    VSCode 全称是 Visual Studio Code,是一款免费且开源的现代化代码编辑器,几乎支持所有主流开发语言的语法高亮、智能代码补全、代码片段提示、自定义快捷键等。 VSCode的特点包括: 轻量级:VSCode的安装包非常小,启动速度也很快,占用内存较少,因此非常适合在低配置电脑上

    2024年02月11日
    浏览(58)
  • 如何下载安装最新版本的 Android Studio

    最新版本的 Android Studio 软件打开的时候,如下图: 下载安装android studio的步骤: 第一步骤:下载最新版本的 Android Studio 地址:https://developer.android.google.cn/studio 第二步骤:点击 Download Android Studio Electric Eel (默认64位) 注意:上面中文必须要改英语 ,如下图: 按下图操作: 第

    2024年02月03日
    浏览(72)
  • 如何快速下载Google play里软件APK

      可能有些小伙伴为了某个APP而去安装Google应用商店下载,我之前也折腾过,但实在是太麻烦了。如果只是为了下载某个APP,不建议这样折腾。 下面分享一个快速方法 先找到你要下载的APP 然后复制你Google Play的链接  接着打开这个下载APK的网站 https://apps.evozi.com/apk-downloade

    2024年02月05日
    浏览(30)
  • Linux下载高版本的gcc与g++并编译,升级g++,演示安装g++11.2以及gdb11.1

    点我进入清华源-GCC链接: https://mirror.tuna.tsinghua.edu.cn/gnu/gcc/ 为了保证后续安装gcc能编译通过, 且安装g++同时也会安装gcc!   下载从清华源所需要的gcc版本,作者这边选择的是 11.2.0 , 此处版本不同,后续的指令文件名需要选择自己的版本文件名。 点我进入清华源-GCC链接,

    2024年02月11日
    浏览(33)
  • LLMs之LLaMA-2:LLaMA-2的简介(技术细节)、安装、使用方法(开源-免费用于研究和商业用途)之详细攻略

    LLMs之LLaMA-2:LLaMA-2的简介(技术细节)、安装、使用方法(开源-免费用于研究和商业用途)之详细攻略 导读 :2023年7月18日,Meta重磅发布Llama 2!这是一组预训练和微调的大型语言模型(LLM),规模从70亿到700亿个参数不等。Meta微调的LLM称为Llama 2-Chat,专为对话使用场景进行了优化

    2024年02月08日
    浏览(36)
  • Android studio 打包release版本 apk 换方式了?

     有一段时间没有搞Android,在基本操作打release包的时候竟然一直打包不成功,一直报 Generate Signed APK: APK(s) generated successfully for module \\\'CorrectionPlatform.app\\\' with 0 build variants: 但主要还不提示打包失败; 在经历一段煎熬各种搜索各种找,终于找到了另一种可以生成release包的方法。

    2023年04月09日
    浏览(30)
  • android studio 打包签名apk时报kotlin版本错误

    报错信息如下: /Users/abbb/Library/Android/sdk/caches/transforms-3/572ca993caa0789f4046529ddf3eacd2/transformed/jetified-BaseRecyclerViewAdapterHelper-4.0.1/jars/classes.jar!/META-INF/com.github.CymChad.brvah.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.

    2024年01月25日
    浏览(36)
  • LLMs之LLaMA2:LLaMA2的简介(技术细节)、安装、使用方法(开源-免费用于研究和商业用途)之详细攻略

    LLMs之LLaMA-2:LLaMA-2的简介(技术细节)、安装、使用方法(开源-免费用于研究和商业用途)之详细攻略 导读 :2023年7月18日,Meta重磅发布Llama 2!这是一组预训练和微调的大型语言模型(LLM),规模从70亿到700亿个参数不等。Meta微调的LLM称为Llama 2-Chat,专为对话使用场景进行了优化

    2024年02月16日
    浏览(41)
  • 【一站解决您的问题】mac 利用命令升级nodejs、npm、安装Nodejs的多版本管理器n、nodejs下载地址

    https://nodejs.org/en 如果官网下载特别慢,可以点击这个地址下载 点击这里 https://nodejs.cn/download/current/ 安装完成后,就包含了nodejs 和 npm。此时您的版本就是下载安装的版本号。 查看各种版本号的命令,往下看。 sudo npm install npm@latest -g //升级到最新版 sudo npm install npm@xx -g //升级

    2024年01月23日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包