先看效果:

覆盖安装 android进程 下载覆盖安装_安卓

实现步骤:

1、 传入apk链接,下载apk文件
2、 画个View展示交互效果
3、 主动提示安装


1:根据链接,去下载apk文件:

首先引库PRDownloader

implementation 'com.mindorks.android:prdownloader:0.5.0'

下载部分代码

String url="http://artist2020.oss-cn-beijing.aliyuncs.com/........../ygApp.apk";//网络地址
String name= VolleyUtils.getRandomString(5)+ "ygApp.apk";//本地存此APK要定义一个名字

final String dirPath = getFilesDir().getPath() + "/" + "volley";
File file = new File(dirPath);
if (!file.exists()) {
   file.mkdirs();
}

PRDownloader.download(url, dirPath, name)
	 .build()
	 .setOnProgressListener(new OnProgressListener() {
	     @Override
	     public void onProgress(Progress progress) {
	       float a=(float) progress.currentBytes/(float)  progress.totalBytes;
	       //正在下载中,显示进度 a%
	    }
	 })
	 .start(new OnDownloadListener() {
	    @Override
	    public void onDownloadComplete() {
	        //下载成功,开始安装APK文件
	        //最下面有installApp方法
	        ProUtils.installApp(Welcome.this,dirPath+"/"+name);
	      }
	    @Override
	    public void onError(Error error) {
	         //下载失败后的操作
	    }
});

2:展示进度的View:

可以把这个view放在dialog里,暴露出方法
这样直接就可以通过dialog去控制进度显示

进度View代码
最下面3个方法,去控制view的UI显示

public class CircleProgressView extends View {

    private boolean isAysning=false;
    private float progress=0f;
    private Paint paintCircle=null;
    private Paint paintBack=null;
    private Paint paintText=null;
    private int width=0;
    private int textSize=0;
    private RectF rectF=null;
    private LinearGradient linearGradient;

    public CircleProgressView(Context context) {
        this(context, null);
    }
    public CircleProgressView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        width=BaseObserver.getInstance().getPXformDP(7);
        paintCircle=new Paint();
        paintCircle.setStrokeWidth(width);
        paintCircle.setColor(0xff313131);
        paintCircle.setAntiAlias(true);
        paintCircle.setStrokeCap(Paint.Cap.ROUND);
        paintCircle.setStyle(Paint.Style.STROKE);

        paintBack=new Paint();
        paintBack.setStrokeWidth(width);
        paintBack.setColor(0xfff2cd30);
        paintBack.setAntiAlias(true);
        paintBack.setStrokeCap(Paint.Cap.ROUND);
        paintBack.setStyle(Paint.Style.FILL);
        paintBack.setShadowLayer(10,0, 0, Color.BLACK);

        paintText=new Paint();
        paintText.setColor(0xff313131);
        paintCircle.setAntiAlias(true);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(rectF==null){
            rectF=new RectF(0+width,0+width,getWidth()-width,getHeight()-width);
        }
        canvas.drawArc(rectF,-90,360,false,paintBack);
        float f=progress*360f;
        canvas.drawArc(rectF,-90,(int)f,false,paintCircle);
        String pro;
        if(isAysning){
             pro=(int)(100f*(progress))+"%";
        }else {
             pro="更新";
        }
        if(textSize==0){
            textSize=getHeight()/4;
            paintText.setTextSize(textSize);
        }
        float textWidth = paintText.measureText(pro);
        Paint.FontMetrics fontMetrics = paintText.getFontMetrics();
        float y = getHeight()/2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent)/2;
        canvas.drawText(pro, (getWidth()-textWidth)/2,y,paintText);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMySize(500, widthMeasureSpec);
        int height = getMySize(500, heightMeasureSpec);
        //设置成正方形
        if (width < height) {
            height = width;
        } else {
            width = height;
        }
        setMeasuredDimension(width, height);
    }

    private int getMySize(int defaultSize, int measureSpec) {
        int mySize = defaultSize;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        switch (mode) {
            case MeasureSpec.EXACTLY: {
                mySize = size;
                break;
            }
            case MeasureSpec.UNSPECIFIED: {
                mySize = defaultSize;
                break;
            }
            case MeasureSpec.AT_MOST: {
                mySize = size;
                break;
            }
        }
        return mySize;
    }

    public void begin(){
        if(!isAysning){
            isAysning=true;
        }
        invalidate();
    }
    public void progress(float f){
        isAysning=true;
        this.progress=f;
        invalidate();
    }
    public void clear(){
        isAysning=false;
        progress=0f;
        invalidate();
    }
}

3:主动去安装APK文件:

这个就不说了,网上有很多类似的代码,我整理了下
目前没有遇见安卓版本的bug
可大胆的用

String appFilePro="com.androeda.xx.xxx";//你的包名


public static void installApp(Context context,String path){
        if(context==null){
            return;
        }
        File file = new File(path);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if(Build.VERSION.SDK_INT>=24) { //Android 7.0及以上
            // 参数2 清单文件中provider节点里面的authorities ; 参数3  共享的文件,即apk包的file类
            Uri apkUri = FileProvider.getUriForFile(context, appFilePro, file);
            //对目标应用临时授权该Uri所代表的文件
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        }else{
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }





在manifest的application节点中添加
<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="......上面的appFilePro 要一致"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path" />
        </provider>




在res中新建file_path文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path name="root" path="" />
    <files-path name="files" path="." />
    <cache-path name="cache" path="." />
    <external-path name="external" path="." />
    <external-files-path name="name" path="." />
    <external-cache-path name="name" path="." />
</paths>

写在最后:

同是天涯打工人,相逢何必论高下。
红颜何处真知己,人生无聊才编程。