# Conflicts: # PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.ktv4
commit
f0c63d72b3
@ -0,0 +1,33 @@ |
||||
/* |
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.arialyy.aria.http.download |
||||
|
||||
import com.arialyy.aria.core.listener.AbsEventListener |
||||
import com.arialyy.aria.core.task.DownloadTask |
||||
import com.arialyy.aria.util.FileUtils |
||||
|
||||
class HttpDEventListener(task: DownloadTask) : AbsEventListener(task) { |
||||
|
||||
override fun handleCancel() { |
||||
|
||||
} |
||||
|
||||
override fun handleComplete() { |
||||
|
||||
} |
||||
|
||||
|
||||
} |
@ -1,249 +0,0 @@ |
||||
/* |
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.arialyy.aria.http.download; |
||||
|
||||
import com.arialyy.aria.core.common.RequestEnum; |
||||
import com.arialyy.aria.core.task.AbsThreadTaskAdapter; |
||||
import com.arialyy.aria.core.task.ThreadConfig; |
||||
import com.arialyy.aria.exception.AriaHTTPException; |
||||
import com.arialyy.aria.http.ConnectionHelp; |
||||
import com.arialyy.aria.http.HttpOption; |
||||
import com.arialyy.aria.http.request.IRequest; |
||||
import com.arialyy.aria.util.ALog; |
||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||
import java.io.BufferedInputStream; |
||||
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.OutputStreamWriter; |
||||
import java.net.HttpURLConnection; |
||||
import java.net.MalformedURLException; |
||||
import java.net.URLEncoder; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.channels.Channels; |
||||
import java.nio.channels.FileChannel; |
||||
import java.nio.channels.ReadableByteChannel; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import timber.log.Timber; |
||||
|
||||
/** |
||||
* Created by lyy on 2017/1/18. 下载线程 |
||||
*/ |
||||
final class HttpDThreadTaskAdapter extends AbsThreadTaskAdapter { |
||||
|
||||
HttpDThreadTaskAdapter(ThreadConfig threadConfig) { |
||||
super(threadConfig); |
||||
} |
||||
|
||||
private HttpDTaskOption getTaskOption() { |
||||
return (HttpDTaskOption) getThreadConfig().getOption(); |
||||
} |
||||
|
||||
@Override protected void handlerThreadTask() { |
||||
HttpURLConnection conn = null; |
||||
BufferedInputStream is = null; |
||||
BufferedRandomAccessFile file = null; |
||||
try { |
||||
HttpDTaskOption taskOption = getTaskOption(); |
||||
HttpOption option = taskOption.getHttpOption(); |
||||
conn = IRequest.Companion.getRequest(option).getDConnection(taskOption.getSourUrl(), option); |
||||
if (!taskOption.isSupportResume()) { |
||||
Timber.w("this task not support resume, url: %s", taskOption.getSourUrl()); |
||||
}else { |
||||
conn.setRequestProperty("Range", |
||||
String.format("bytes=%s-%s", getThreadConfig().getBlockRecord().getStartLocation(), |
||||
(getThreadConfig().getBlockRecord().getEndLocation() - 1))); |
||||
} |
||||
|
||||
ConnectionHelp.setConnectParam(mTaskOption, conn); |
||||
conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); |
||||
conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数
|
||||
if (mTaskOption.isChunked()) { |
||||
conn.setDoInput(true); |
||||
conn.setChunkedStreamingMode(0); |
||||
} |
||||
conn.connect(); |
||||
|
||||
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); |
||||
if (mTaskOption.isChunked()) { |
||||
readChunked(is); |
||||
} else if (getThreadConfig().isBlock) { |
||||
readDynamicFile(is); |
||||
} else { |
||||
//创建可设置位置的文件
|
||||
file = |
||||
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", |
||||
getTaskConfig().getBuffSize()); |
||||
//设置每条线程写入文件的位置
|
||||
if (getThreadRecord().startLocation > 0) { |
||||
file.seek(getThreadRecord().startLocation); |
||||
} |
||||
readNormal(is, file); |
||||
handleComplete(); |
||||
} |
||||
} catch (MalformedURLException e) { |
||||
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), |
||||
getEntity().getFilePath(), getEntity().getUrl()), e), false); |
||||
} catch (IOException e) { |
||||
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), |
||||
getEntity().getFilePath(), getEntity().getUrl()), e), true); |
||||
} catch (ArrayIndexOutOfBoundsException e) { |
||||
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), |
||||
getEntity().getFilePath(), getEntity().getUrl()), e), false); |
||||
} catch (Exception e) { |
||||
fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), |
||||
getEntity().getFilePath(), getEntity().getUrl()), e), false); |
||||
} finally { |
||||
try { |
||||
if (file != null) { |
||||
file.close(); |
||||
} |
||||
if (is != null) { |
||||
is.close(); |
||||
} |
||||
if (conn != null) { |
||||
conn.getInputStream().close(); |
||||
conn.disconnect(); |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 读取chunked数据 |
||||
*/ |
||||
private void readChunked(InputStream is) { |
||||
FileOutputStream fos = null; |
||||
try { |
||||
fos = new FileOutputStream(getThreadConfig().tempFile, true); |
||||
byte[] buffer = new byte[getTaskConfig().getBuffSize()]; |
||||
int len; |
||||
while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { |
||||
if (getThreadTask().isBreak()) { |
||||
break; |
||||
} |
||||
if (mSpeedBandUtil != null) { |
||||
mSpeedBandUtil.limitNextBytes(len); |
||||
} |
||||
fos.write(buffer, 0, len); |
||||
progress(len); |
||||
} |
||||
handleComplete(); |
||||
} catch (IOException e) { |
||||
fail(new AriaHTTPException( |
||||
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), |
||||
getThreadConfig().url), e), true); |
||||
} finally { |
||||
if (fos != null) { |
||||
try { |
||||
fos.close(); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 动态长度文件读取方式 |
||||
*/ |
||||
private void readDynamicFile(InputStream is) { |
||||
FileOutputStream fos = null; |
||||
FileChannel foc = null; |
||||
ReadableByteChannel fic = null; |
||||
try { |
||||
int len; |
||||
fos = new FileOutputStream(getThreadConfig().tempFile, true); |
||||
foc = fos.getChannel(); |
||||
fic = Channels.newChannel(is); |
||||
|
||||
ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); |
||||
//如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。
|
||||
|
||||
while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) { |
||||
if (getThreadTask().isBreak()) { |
||||
break; |
||||
} |
||||
if (mSpeedBandUtil != null) { |
||||
mSpeedBandUtil.limitNextBytes(len); |
||||
} |
||||
if (getRangeProgress() + len >= getThreadRecord().endLocation) { |
||||
len = (int) (getThreadRecord().endLocation - getRangeProgress()); |
||||
bf.flip(); |
||||
fos.write(bf.array(), 0, len); |
||||
bf.compact(); |
||||
progress(len); |
||||
break; |
||||
} else { |
||||
bf.flip(); |
||||
foc.write(bf); |
||||
bf.compact(); |
||||
progress(len); |
||||
} |
||||
} |
||||
handleComplete(); |
||||
} catch (IOException e) { |
||||
fail(new AriaHTTPException( |
||||
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), |
||||
getThreadConfig().url), e), true); |
||||
} finally { |
||||
try { |
||||
if (fos != null) { |
||||
fos.flush(); |
||||
fos.close(); |
||||
} |
||||
if (foc != null) { |
||||
foc.close(); |
||||
} |
||||
if (fic != null) { |
||||
fic.close(); |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 读取普通的文件流 |
||||
*/ |
||||
private void readNormal(InputStream is, BufferedRandomAccessFile file) |
||||
throws IOException { |
||||
byte[] buffer = new byte[getTaskConfig().getBuffSize()]; |
||||
int len; |
||||
while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { |
||||
if (getThreadTask().isBreak()) { |
||||
break; |
||||
} |
||||
if (mSpeedBandUtil != null) { |
||||
mSpeedBandUtil.limitNextBytes(len); |
||||
} |
||||
file.write(buffer, 0, len); |
||||
progress(len); |
||||
} |
||||
} |
||||
|
||||
@Override public void cancel() { |
||||
|
||||
} |
||||
|
||||
@Override public void stop() { |
||||
|
||||
} |
||||
} |
@ -1,265 +0,0 @@ |
||||
/* |
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package com.arialyy.aria.core.manager; |
||||
|
||||
import android.text.TextUtils; |
||||
import com.arialyy.aria.core.task.ITask; |
||||
import com.arialyy.aria.core.task.IThreadTask; |
||||
import com.arialyy.aria.util.ALog; |
||||
import com.arialyy.aria.util.CommonUtil; |
||||
import java.util.HashSet; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.Future; |
||||
import java.util.concurrent.SynchronousQueue; |
||||
import java.util.concurrent.ThreadPoolExecutor; |
||||
import java.util.concurrent.TimeUnit; |
||||
import java.util.concurrent.locks.ReentrantLock; |
||||
|
||||
/** |
||||
* 线程任务管理器 |
||||
*/ |
||||
@Deprecated |
||||
public class ThreadTaskManager1 { |
||||
private final String TAG = CommonUtil.getClassName(this); |
||||
private static volatile ThreadTaskManager INSTANCE = null; |
||||
private static final int CORE_POOL_NUM = 20; |
||||
private static final ReentrantLock LOCK = new ReentrantLock(); |
||||
private final ThreadPoolExecutor mExePool; |
||||
private final Map<Integer, Set<FutureContainer>> mThreadTasks = new ConcurrentHashMap<>(); |
||||
|
||||
public static synchronized ThreadTaskManager getInstance() { |
||||
if (INSTANCE == null) { |
||||
INSTANCE = new ThreadTaskManager(); |
||||
} |
||||
return INSTANCE; |
||||
} |
||||
|
||||
private ThreadTaskManager() { |
||||
mExePool = new ThreadPoolExecutor(CORE_POOL_NUM, Integer.MAX_VALUE, |
||||
60L, TimeUnit.SECONDS, |
||||
new SynchronousQueue<Runnable>()); |
||||
mExePool.allowsCoreThreadTimeOut(); |
||||
} |
||||
|
||||
/** |
||||
* 删除所有线程任务 |
||||
*/ |
||||
public void removeAllThreadTask() { |
||||
if (mThreadTasks.isEmpty()) { |
||||
return; |
||||
} |
||||
try { |
||||
LOCK.tryLock(2, TimeUnit.SECONDS); |
||||
for (Set<FutureContainer> threads : mThreadTasks.values()) { |
||||
for (FutureContainer container : threads) { |
||||
if (container.future.isDone() || container.future.isCancelled()) { |
||||
continue; |
||||
} |
||||
container.threadTask.destroy(); |
||||
} |
||||
threads.clear(); |
||||
} |
||||
mThreadTasks.clear(); |
||||
} catch (InterruptedException e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
LOCK.unlock(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 启动线程任务 |
||||
* |
||||
* @param taskId {@link ITask#getTaskId()} |
||||
* @param threadTask 线程任务{@link IThreadTask} |
||||
*/ |
||||
public void startThread(Integer taskId, IThreadTask threadTask) { |
||||
try { |
||||
LOCK.tryLock(2, TimeUnit.SECONDS); |
||||
if (mExePool.isShutdown()) { |
||||
ALog.e(TAG, "线程池已经关闭"); |
||||
return; |
||||
} |
||||
Set<FutureContainer> temp = mThreadTasks.get(taskId); |
||||
if (temp == null) { |
||||
temp = new HashSet<>(); |
||||
mThreadTasks.put(taskId, temp); |
||||
} |
||||
FutureContainer container = new FutureContainer(); |
||||
container.threadTask = threadTask; |
||||
container.future = mExePool.submit(threadTask); |
||||
temp.add(container); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
LOCK.unlock(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 任务是否在执行 |
||||
* |
||||
* @return {@code true} 任务正在运行 |
||||
*/ |
||||
public boolean taskIsRunning(Integer taskId) { |
||||
return mThreadTasks.get(taskId) != null; |
||||
} |
||||
|
||||
/** |
||||
* 停止任务的所有线程 |
||||
*/ |
||||
public void removeTaskThread(Integer taskId) { |
||||
try { |
||||
LOCK.tryLock(2, TimeUnit.SECONDS); |
||||
if (mExePool.isShutdown()) { |
||||
ALog.e(TAG, "线程池已经关闭"); |
||||
return; |
||||
} |
||||
Set<FutureContainer> temp = mThreadTasks.get(taskId); |
||||
if (temp != null && temp.size() > 0) { |
||||
for (FutureContainer container : temp) { |
||||
if (container.future.isDone() || container.future.isCancelled()) { |
||||
continue; |
||||
} |
||||
container.threadTask.destroy(); |
||||
} |
||||
temp.clear(); |
||||
mThreadTasks.remove(taskId); |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
LOCK.unlock(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据线程名删除任务的中的线程 |
||||
* |
||||
* @param threadName 线程名 |
||||
* @return true 删除线程成功;false 删除线程失败 |
||||
*/ |
||||
public boolean removeSingleTaskThread(Integer taskId, String threadName) { |
||||
try { |
||||
LOCK.tryLock(2, TimeUnit.SECONDS); |
||||
if (mExePool.isShutdown()) { |
||||
ALog.e(TAG, "线程池已经关闭"); |
||||
return false; |
||||
} |
||||
if (TextUtils.isEmpty(threadName)) { |
||||
ALog.e(TAG, "线程名为空"); |
||||
return false; |
||||
} |
||||
|
||||
Set<FutureContainer> temp = mThreadTasks.get(taskId); |
||||
if (temp != null && temp.size() > 0) { |
||||
FutureContainer tempC = null; |
||||
for (FutureContainer container : temp) { |
||||
if (container.threadTask.getThreadName().equals(threadName)) { |
||||
tempC = container; |
||||
break; |
||||
} |
||||
} |
||||
if (tempC != null) { |
||||
tempC.threadTask.destroy(); |
||||
temp.remove(tempC); |
||||
return true; |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
LOCK.unlock(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* 删除单个线程任务 |
||||
* |
||||
* @param task 线程任务 |
||||
*/ |
||||
public boolean removeSingleTaskThread(Integer taskId, IThreadTask task) { |
||||
try { |
||||
LOCK.tryLock(2, TimeUnit.SECONDS); |
||||
if (mExePool.isShutdown()) { |
||||
ALog.e(TAG, "线程池已经关闭"); |
||||
return false; |
||||
} |
||||
if (task == null) { |
||||
ALog.e(TAG, "线程任务为空"); |
||||
return false; |
||||
} |
||||
Set<FutureContainer> temp = mThreadTasks.get(taskId); |
||||
if (temp != null && temp.size() > 0) { |
||||
FutureContainer tempC = null; |
||||
for (FutureContainer container : temp) { |
||||
if (container.threadTask == task) { |
||||
tempC = container; |
||||
break; |
||||
} |
||||
} |
||||
if (tempC != null) { |
||||
task.destroy(); |
||||
temp.remove(tempC); |
||||
return true; |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
LOCK.unlock(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* 重试线程任务 |
||||
* |
||||
* @param task 线程任务 |
||||
*/ |
||||
public void retryThread(IThreadTask task) { |
||||
try { |
||||
LOCK.tryLock(2, TimeUnit.SECONDS); |
||||
if (mExePool.isShutdown()) { |
||||
ALog.e(TAG, "线程池已经关闭"); |
||||
return; |
||||
} |
||||
try { |
||||
if (task == null || task.isDestroy()) { |
||||
ALog.e(TAG, "线程为空或线程已经中断"); |
||||
return; |
||||
} |
||||
} catch (Exception e) { |
||||
ALog.e(TAG, "", e); |
||||
return; |
||||
} |
||||
mExePool.submit(task); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
LOCK.unlock(); |
||||
} |
||||
} |
||||
|
||||
private static class FutureContainer { |
||||
Future future; |
||||
IThreadTask threadTask; |
||||
} |
||||
} |
@ -0,0 +1,82 @@ |
||||
/* |
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.arialyy.aria.core.task |
||||
|
||||
import java.util.concurrent.ConcurrentHashMap |
||||
import java.util.concurrent.TimeUnit.SECONDS |
||||
import java.util.concurrent.locks.ReentrantLock |
||||
|
||||
object ThreadTaskManager2 { |
||||
private val mThreadTasks: ConcurrentHashMap<Int, MutableSet<IThreadTask>> = ConcurrentHashMap() |
||||
private val LOCK = ReentrantLock() |
||||
|
||||
/** |
||||
* 任务是否在执行 |
||||
* |
||||
* @return `true` 任务正在运行 |
||||
*/ |
||||
fun taskIsRunning(taskId: Int): Boolean { |
||||
return mThreadTasks[taskId] != null |
||||
} |
||||
|
||||
/** |
||||
* stop thread task |
||||
* @param isRemoveTask if true, remove task and block |
||||
*/ |
||||
fun stopThreadTask(taskId: Int, isRemoveTask: Boolean = false) { |
||||
try { |
||||
LOCK.tryLock(2, SECONDS) |
||||
val threadTaskList: MutableSet<IThreadTask>? = mThreadTasks[taskId] |
||||
threadTaskList?.forEach { |
||||
if (isRemoveTask) { |
||||
it.cancel() |
||||
return@forEach |
||||
} |
||||
it.stop() |
||||
} |
||||
mThreadTasks.remove(taskId) |
||||
|
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} finally { |
||||
LOCK.unlock() |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 删除所有线程任务 |
||||
*/ |
||||
fun removeAllThreadTask() { |
||||
if (mThreadTasks.isEmpty()) { |
||||
return |
||||
} |
||||
try { |
||||
LOCK.tryLock(2, SECONDS) |
||||
for (threads in mThreadTasks.values) { |
||||
for (tt in threads) { |
||||
tt.stop() |
||||
} |
||||
threads.clear() |
||||
} |
||||
mThreadTasks.clear() |
||||
} catch (e: InterruptedException) { |
||||
e.printStackTrace() |
||||
} finally { |
||||
LOCK.unlock() |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue