单点下载
OkHttpManager okHttpManager = new OkHttpManager();
Call call = okHttpManager.asyncCall("http://acj3.pc6.com/pc6_soure/2018-3/com.ss.android.essay.joke_689.apk");
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 不断的读写文件,单线程
InputStream inputStream = response.body().byteStream();
File file = new File(getCacheDir(),"nhdz.apk");
OutputStream outputStream = new FileOutputStream(file);
int len = 0;
byte[] buffer = new byte[1024*22];
while ((len = inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
inputStream.close();
outputStream.close();
//安装已经下好的apk
installFile(file);
}
});
OkHttpManager
public Call asyncCall(String url) {
Request request = new Request.Builder().url(url).build();
return okHttpClient.newCall(request);
}
installFile代码
private void installFile(File file) {
Intent intent = new Intent("android.intent.action.INSTALL_PACKAGE");
intent.setData(Uri.parse("file:" + file));
startActivity(intent);
}
多线程下载
FileManager文件管理
public class FileManager {
public static final FileManager manager = new FileManager();
private File mRootDir;
private Context mContext;
private FileManager() {
}
public static FileManager getInstance() {
return manager;
}
public void init(Context context) {
this.mContext = context.getApplicationContext();
}
public void rootDir(File file) {
if (!file.exists()) {
file.mkdirs();
}
if (file.exists() && file.isDirectory()) {
mRootDir = file;
}
}
/**
* 根据url生成文件
*/
public File getFile(String url) {
String fileName = Utils.md5Url(url)+".apk";
if (mRootDir == null) {
mRootDir = mContext.getCacheDir();
}
File file = new File(mRootDir, fileName);
return file;
}
}
Utils:MD5的加密和关闭流
public class Utils {
/**
* md5加密
*/
public static String md5Url(String url) {
if (TextUtils.isEmpty(url)) {
return url;
}
StringBuffer sb = new StringBuffer();
try {
MessageDigest messageDigest = MessageDigest.getInstance("md5");
messageDigest.update(url.getBytes());
byte[] digest = messageDigest.digest();
for (byte b : digest) {
// 转成了 16 机制
String hexStr = Integer.toHexString(b & 0xff);
//不足补0
sb.append(hexStr.length() == 0 ? "0" + hexStr : hexStr);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 关闭流
*/
public static void close(Closeable closeable) {
if(closeable!=null){
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
DownloadCallback下载接口回调
public interface DownloadCallback {
public void onFailure(IOException e);
void onSucceed(File file);
}
DownloadDispatcher开始下载
public class DownloadDispatcher {
private static final DownloadDispatcher dispatcher=new DownloadDispatcher();
/**
* Ready async calls in the order they'll be run.
*/
private final Deque<DownloadTask> readyTask = new ArrayDeque<>();
/**
* Running asynchronous calls. Includes canceled calls that haven't finished yet.
*/
private final Deque<DownloadTask> runningTask = new ArrayDeque<>();
/**
* Running synchronous calls. Includes canceled calls that haven't finished yet.
*/
private final Deque<DownloadTask> stopTask = new ArrayDeque<>();
public static DownloadDispatcher getInstance() {
return dispatcher;
}
//1.多线程下载,使用线程池
public void startDownload(final String url, final DownloadCallback callback) {
//2.获取文件大小
Call call = OkHttpManager.getInstance().asyncCall(url);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.onFailure(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//获取文件大小
long contentLength = response.body().contentLength();
//没有直接返回
if (contentLength <= -1) {
return;
}
//每个线程负责每一块
DownloadTask downloadTask = new DownloadTask(url, contentLength, callback);
downloadTask.init();
runningTask.add(downloadTask);
}
});
}
public void recyclerTask(DownloadTask downloadTask) {
runningTask.remove(downloadTask);
//可以参考okhttp的dispatch的源码,如果还需要下载的开始下一个下载
}
}
DownloadTask一个apk的下载
public class DownloadTask {
private String mUrl;
private long mContentLength;
private List<DownloadRunnable> mRunnables;//有多个线程下载
private
@Nullable
ExecutorService executorService;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//有四个线程下载
private static final int THREAD_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private DownloadCallback mCallback;
private volatile int mSucceedNumber = 0;
public DownloadTask(String url, long contentLength, DownloadCallback callback) {
this.mContentLength = contentLength;
this.mUrl = url;
mRunnables = new ArrayList<>();
mCallback = callback;
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable runnable) {
Thread thread = new Thread(runnable, "okHttp");
thread.setDaemon(false);//无守护线程
return thread;
}
});
}
return executorService;
}
public void init() {
for (int i = 0; i < THREAD_SIZE; i++) {
//每个线程要下载的内容,假设一共4M,每个线程负责1M
long threadsize = mContentLength / THREAD_SIZE;//每个线程负责下载的大小
//这里要读取数据库,不能这么写
long start = i * threadsize;
long end = (i + threadsize) - 1;
//最后一个线程可能字节没有threadsize大小
if (i == THREAD_SIZE - 1) {
end = mContentLength - 1;
}
DownloadRunnable runnable = new DownloadRunnable(mUrl, i, start, end, new DownloadCallback() {
@Override
public void onFailure(IOException e) {
mCallback.onFailure(e);
}
@Override
public void onSucceed(File file) {
//线程同步一下
synchronized (DownloadTask.this){
mSucceedNumber += 1;
if(mSucceedNumber==THREAD_SIZE){
mCallback.onSucceed(file);
//完成之后回收
DownloadDispatcher.getInstance().recyclerTask(DownloadTask.this);
//清除这个文件中的数据库
}
}
}
});
//通过线程池去执行
executorService().execute(runnable);
}
}
public void stop(String url){
for (DownloadRunnable runnable : mRunnables) {
runnable.stop();
}
}
}
一个apk多线程下载
ublic class DownloadRunnable implements Runnable {
private static final int STATUS_DOWNLOAIND = 1;
private static final int STATUS_STOP = 2;
private String mUrl;
private long mStart;
private long mEnd;
private int mThreadId;
private DownloadCallback mCallback;
private int mStatus = STATUS_DOWNLOAIND;
private int mProgress = 0;
public DownloadRunnable(String url, int threadId, long start, long end, DownloadCallback callback) {
this.mUrl = url;
this.mStart = start;
this.mEnd = end;
this.mThreadId = threadId;
this.mCallback = callback;
}
@Override
public void run() {
//只读写我们自己的内容
RandomAccessFile accessFile = null;
InputStream inputStream = null;
try {
Response response = OkHttpManager.getInstance().syncResponse(mUrl, mStart, mEnd);
Log.e("TAG", this.toString());
inputStream = response.body().byteStream();
//读写数据
File file = FileManager.manager.getFile(mUrl);
accessFile = new RandomAccessFile(file, "rwd");
//从什么位置开始写
accessFile.seek(mStart);
int len = 0;
byte[] buffer = new byte[1024 * 22];
while ((len = inputStream.read(buffer)) != -1) {
if (mStatus == STATUS_STOP)
break;
//保存进度,做进度
mProgress += len;
accessFile.write(buffer, 0, len);
}
mCallback.onSucceed(file);
} catch (IOException e) {
e.printStackTrace();
} finally {
Utils.close(inputStream);
Utils.close(accessFile);
//存到数据库
}
}
@Override
public String toString() {
return "DownloadRunnable{" +
", mStart=" + mStart +
", mEnd=" + mEnd +
", mThreadId=" + mThreadId +
'}';
}
public void stop() {
mStatus = STATUS_STOP;
}
}
测试
FileManager.getInstance().init(this);
DownloadDispatcher.getInstance().startDownload("http://acj3.pc6.com/pc6_soure/2018-3/com.ss.android.essay.joke_689.apk"
, new DownloadCallback() {
@Override
public void onFailure(IOException e) {
e.printStackTrace();
}
@Override
public void onSucceed(File file) {
installFile(file);
}
});