多线程下载apk并安装

移动开发 waitig 615℃ 百度已收录 0评论

1、基本思路是将文件分段切割、分段传输、分段保存。

2、分段切割用到HttpUrlConnection对象的setRequestProperty("Range", "bytes=" + start + "-" + end)方法。

3、分段传输用到HttpUrlConnection对象的getInputStream()方法。

4、分段保存用到RandomAccessFile的seek(int start)方法。

5、创建指定长度的线程池,循环创建线程,执行下载操作。

直接上代码,这个主要实现的是下载文件的并保存到本地的类:

import android.util.Log;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;

public class FileDownloadThread extends Thread {
  
    private static final String TAG = FileDownloadThread.class.getSimpleName();  
  
    /** 当前下载是否完成 */  
    private boolean isCompleted = false;  
    /** 当前下载文件长度 */  
    private int downloadLength = 0;  
    /** 文件保存路径 */  
    private File file;
    /** 文件下载路径 */  
    private URL downloadUrl;
    /** 当前下载线程ID */  
    private int threadId;  
    /** 线程下载数据长度 */  
    private int blockSize;  
  
    /** 
     *  
     * @param :文件下载地址
     * @param file:文件保存路径 
     * @param blocksize:下载数据长度 
     * @param threadId:线程ID 
     */  
    public FileDownloadThread(URL downloadUrl, File file, int blocksize,  
            int threadId) {  
        this.downloadUrl = downloadUrl;  
        this.file = file;  
        this.threadId = threadId;  
        this.blockSize = blocksize;  
    }  
  
    @Override  
    public void run() {  
  
        BufferedInputStream bis = null;
        RandomAccessFile raf = null;
  
        try {  
            URLConnection conn = downloadUrl.openConnection();
            conn.setAllowUserInteraction(true);  
  
            int startPos = blockSize * (threadId - 1);//开始位置  
            int endPos = blockSize * threadId - 1;//结束位置  
            //设置当前线程下载的起点、终点  
            conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);  
            System.out.println(Thread.currentThread().getName() + "  bytes="  
                    + startPos + "-" + endPos);  
  
            byte[] buffer = new byte[1024];  
            bis = new BufferedInputStream(conn.getInputStream());  
  
            raf = new RandomAccessFile(file, "rwd");  
            raf.seek(startPos);  
            int len;  
            while ((len = bis.read(buffer, 0, 1024)) != -1) {  
                raf.write(buffer, 0, len);  
                downloadLength += len;  
            }  
            isCompleted = true;  
            Log.d(TAG, "current thread task has finished,all size:"
                    + downloadLength);  
  
        } catch (IOException e) {
            e.printStackTrace();  
        } finally {  
            if (bis != null) {  
                try {  
                    bis.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            if (raf != null) {  
                try {  
                    raf.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
  
    /** 
     * 线程文件是否下载完毕 
     */  
    public boolean isCompleted() {  
        return isCompleted;  
    }  
  
    /** 
     * 线程下载文件长度 
     */  
    public int getDownloadLength() {  
        return downloadLength;  
    }  
  
}  

然后启动下载并启动ProgreesBar显示进度条,下载完安装apk:

import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import liuhao.baway.com.liuhao_weektwo.R;
import liuhao.baway.com.liuhao_weektwo.net.FileDownloadThread;

/**
 * Created by 15218 on 2017/11/11.
 */
public class DialogUntils {

    private static Button btnok;
    private static Button btnno;
    private static ProgressDialog pro;

    public static void showDialogs(final Context context){

         final AlertDialog builder = new AlertDialog.Builder(context).create();
        View v = View.inflate(context, R.layout.dialog,null);
        btnok = v.findViewById(R.id.ok);
        btnno = v.findViewById(R.id.no);
        btnok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 获取SD卡路径
                String dirName = Environment.getExternalStorageDirectory() + "/MyDownLoad/";
                File file1 = new File(dirName);
                // 如果SD卡目录不存在创建
                if (!file1.exists()) {
                        file1.mkdir();
                }
                int threadNum = 5;
                String fileName = dirName + "qq.apk";
                NetUntils netUntils =  new NetUntils("http://169.254.248.85:8080/work/QQ_744.apk",threadNum,fileName,context);
                netUntils.start();
                builder.dismiss();
                showProgress(context);
            }
        });
        btnno.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                builder.dismiss();
            }
        });
        builder.setView(v);
        builder.setCancelable(false);
        builder.show();
    }
    public static void showProgress(Context context) {
        pro = new ProgressDialog(context);
        pro.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        pro.setProgress(0);
        pro.setTitle("正在下载");
        pro.setMessage("请稍后...");
        pro.show();
    }
    public static class NetUntils extends Thread{
        private String downloadUrl;//下载链接
        private int threadNum;   //开启线程数
        private String filePath ;  //保存文件路径
        private int blockSize ;  //每个线程的下载数量

        private Context context;
        public NetUntils(String downloadUrl, int threadNum, String filePath,Context context) {
            this.downloadUrl = downloadUrl;
            this.threadNum = threadNum;
            this.filePath = filePath;
            this.context = context;
        }

        @Override
        public void run() {

            FileDownloadThread[] threads  = new FileDownloadThread[threadNum];
            try {
                URL url = new URL(downloadUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                int filesize = conn.getContentLength();
                if (filesize <= 0) {
                    System.out.println("读取文件失败");
                    return;
                }
                pro.setMax(filesize);
                blockSize = (filesize % threadNum) == 0 ? filesize / threadNum
                        : filesize / threadNum + 1;
                Log.d("NetUtils", "fileSize:" + filesize + "  blockSize:");
                File file = new File(filePath);
                for (int i = 0; i < threads.length; i++) {
                    // 启动线程,分别下载每个线程需要下载的部分
                    threads[i] = new FileDownloadThread(url, file, blockSize,
                            (i + 1));
                    threads[i].setName("Thread:" + i);
                    threads[i].start();
                }
                boolean isfinished = false;
                int downloadedAllSize = 0;
                while (!isfinished) {
                    isfinished = true;
                    // 当前所有线程下载总量
                    downloadedAllSize = 0;
                    for (int i = 0; i < threads.length; i++) {
                        downloadedAllSize += threads[i].getDownloadLength();
                        if (!threads[i].isCompleted()) {
                            isfinished = false;
                        }
                    }
                    pro.setProgress(downloadedAllSize);
                    Thread.sleep(1000);
                }
                if(pro.getProgress()==filesize){
                    pro.dismiss();
                    if(file.exists()){
                        installApk(file,context);
                    }else {
                         //吐司事件
                          Toast.makeText(context, "apk文件不存在",Toast.LENGTH_SHORT).show();
                    }
                }
                Log.d("NetUtils", " all of downloadSize:" + downloadedAllSize);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 安装
     * @param file 文件
     * @param context 上下文
     */
    public static void installApk(File file,Context context) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
            Uri apkUri = FileProvider.getUriForFile(context, "com.shawpoo.app.fileprovider", file);  //包名.fileprovider
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }
}

在这里我用的是自定义dialog的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发现新版本"
        android:layout_margin="10dp"
        android:gravity="center"
        android:textSize="30sp"
        android:layout_gravity="center"
        android:textColor="#249BED"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="#249BED"
        />
    <TextView
        android:text="最新版本:v.1.0.0.16070810"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_marginTop="30dp"
        android:layout_marginLeft="30dp"/>

    <TextView
        android:text="新版本大小:11.97MB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_marginLeft="30dp"/>
    <TextView
        android:text="更新内容"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_marginTop="30dp"
        android:layout_marginLeft="30dp"/>
    <TextView
        android:text="测试更新接口"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_marginLeft="30dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/ok"
            android:text="立即更新"
            android:background="#0C8BEB"
            android:padding="10dp"
            android:textColor="#fff"
            android:textSize="20sp"
            android:layout_margin="30dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/no"
            android:text="以后再说"
            android:background="#C2C2C2"
            android:padding="10dp"
            android:textColor="#000"
            android:textSize="20sp"
            android:layout_margin="30dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</LinearLayout>

效果展示

此文章不实现版本更新需求,下一篇即将更新。。。



本文由【waitig】发表在等英博客
本文固定链接:多线程下载apk并安装
欢迎关注本站官方公众号,每日都有干货分享!
等英博客官方公众号
点赞 (0)分享 (0)