114 Commits

Author SHA1 Message Date
51eb7e9cb7 build(deps): 升级项目依赖版本 2026-07-27 02:10:41 +08:00
cef92d6a2f feat(download): 支持分区存储Uri下载模式 2026-07-27 01:38:57 +08:00
cb50a2dcd2 build(deps): 更新项目依赖版本 2026-07-26 05:37:30 +08:00
3dfeabdf3b feat(deps): 升级 RxJava 至 v3 并更新相关依赖 2026-07-26 05:16:47 +08:00
c494c06265 build(deps): 更新发布配置以兼容新版本Gradle 2026-07-26 04:53:39 +08:00
9eca197acb feat(notification): 添加 Android 13 通知权限和 Android 11 存储权限适配
- 添加了 Android O+ 通知渠道创建支持
- 实现了 Android 13 POST_NOTIFICATIONS 权限检查
- 添加了 Android 11+ MANAGE_EXTERNAL_STORAGE 权限适配
- 更新了 AndroidManifest 中的权限配置
- 添加了存储权限请求回调处理逻辑
2026-07-26 04:36:51 +08:00
31cee5b0f5 refactor(download): 重构下载模块代码格式和逻辑实现
- 格式化 ChildHandleDialog 类代码缩进和空行
- 优化 ChildHandleDialog 中的 onClick 方法使用 if-else 替代 switch
- 格式化 M3U8LiveDLoadActivity 类代码缩进和空行
- 优化 M3U8LiveDLoadActivity 中的菜单点击事件处理逻辑
- 格式化 M3U8VodDLoadActivity 类代码缩进和空行
- 添加必要的 import 语句和空行分隔
- 统一代码风格和提高可读性
2026-07-25 19:02:09 +08:00
8b74586403 build(deps): 升级 Gradle 与 Kotlin 及依赖配置 2026-07-25 15:56:23 +08:00
lyy
aa292e4389 Merge pull request #914 from wangkai9799/master
网络切换导致下载失败
2021-09-05 12:30:26 +08:00
lyy
cba9483f3a Merge pull request #918 from bihailantian/patch-1
Update SFtpURecordHandler.java(fix SFTP断点续传上传文件错误)
2021-09-05 12:29:41 +08:00
bihailantian
eeb0bea97a Update SFtpURecordHandler.java
fix SFTP断点上传文件错误
2021-09-02 16:32:27 +08:00
wangkai
3fc99cd01e 网络切换导致下载失败 2021-08-31 13:18:20 +08:00
lyy
48af5509f5 Merge pull request #910 from ls1110924/master
fix: 修复Entity使用Parcel序列化时丢失rowId的问题
2021-08-25 20:28:08 +08:00
A Shuai
56893b0d88 fix: 修复Entity使用Parcel序列化时丢失rowId的问题 2021-08-17 23:54:02 +08:00
lyy
ada1f9f993 Merge pull request #907 from Equalzys/3.8.16.1
修改3个bug #886、#897、#899
2021-08-16 15:29:00 +08:00
Equalzys
7392eba667 1、修改上传文件时去合并文件的错误
2、捕获sql查询失败异常
3、修复下载文件获取不到文件长度而导致合并失败的问题
2021-08-12 13:36:13 +08:00
lyy
3e2c3bc055 Update stale.yml 2021-07-06 10:40:37 +08:00
lyy
6e9f674fab Update README.md 2021-04-20 09:42:30 +08:00
lyy
afa6434664 Update README.md 2021-04-18 21:31:57 +08:00
laoyuyu
00a81220cb 修改上传脚本,将从库从jcenter迁移到mavencentral 2021-04-18 21:30:03 +08:00
laoyuyu
ebbbbee1f8 修改上传脚本,将从库从jcenter迁移到mavencentral 2021-04-18 19:28:29 +08:00
laoyuyu
6eab496b6a Merge branch 'master' of github.com:AriaLyy/Aria 2021-04-14 21:19:30 +08:00
laoyuyu
3679e0cb23 fix bug https://github.com/AriaLyy/Aria/issues/851 2021-04-14 21:19:04 +08:00
lyy
66f9aa9cd5 Update stale.yml 2021-01-11 14:20:15 +08:00
lyy
edb3e66a96 Update stale.yml 2020-12-18 17:34:41 +08:00
laoyuyu
1c0765be02 修复bug 2020-12-09 15:29:40 +08:00
laoyuyu
5853fe9e3e 修复组合任务获取文件信息时,其中子任务失败,状态回调错误的问题 2020-12-09 11:32:51 +08:00
laoyuyu
9db8f1e9c0 修复组合任务在获取子任务信息的过程中,手动停止或删除,没有回调的问题 2020-12-09 10:24:14 +08:00
laoyuyu
35e66caab5 fix bug https://github.com/AriaLyy/Aria/issues/811 2020-12-09 09:09:59 +08:00
laoyuyu
bcfb78109b fixbug https://github.com/AriaLyy/Aria/issues/807 2020-12-09 08:53:53 +08:00
laoyuyu
d7d081ffb1 修复单线程下载时,文件已经完成,重复下载回调下载失败的问题
修复一个重新下载文件时,同名路径文件没有被被删除的问题
2020-11-12 22:35:18 +08:00
laoyuyu
840facd1cc 3.8.15 2020-11-09 20:34:27 +08:00
laoyuyu
f67f829dfc fix bug https://github.com/AriaLyy/Aria/issues/799 https://github.com/AriaLyy/Aria/issues/798 2020-11-09 20:34:05 +08:00
laoyuyu
0152187aa8 修复iv不存在时,索引文件异常的问题, https://github.com/AriaLyy/Aria/issues/780 2020-10-16 09:24:16 +08:00
laoyuyu
68c992962b 修复不支持断点的连接下载失败问题,https://github.com/AriaLyy/Aria/issues/771 2020-09-23 13:46:12 +08:00
laoyuyu
514fa4321d Merge branch 'master' of github.com:AriaLyy/Aria 2020-09-23 10:09:05 +08:00
laoyuyu
313e047d1f v3.1.4 2020-09-23 10:08:38 +08:00
lyy
62ad99e252 Update stale.yml 2020-09-21 08:35:57 +08:00
laoyuyu
8785bb2875 增加路径不可以写的判断,下载时,如果路径不可写,将执行失败回调 https://github.com/AriaLyy/Aria/issues/750 2020-08-28 09:16:31 +08:00
laoyuyu
7be93b38c2 fix bug https://github.com/AriaLyy/Aria/issues/743 2020-08-27 08:45:45 +08:00
laoyuyu
3024b85091 3.8.12 2020-08-15 13:38:41 +08:00
laoyuyu
c78fa1b66a 修复删除加密的m3u8文件时,key没有被删除的问题 https://github.com/AriaLyy/Aria/issues/735#issuecomment-673958845 2020-08-14 17:39:52 +08:00
laoyuyu
428fbab9d2 修复加密的m3u8下载,并且使用索引模式时,key的uri没有使用双引号的问题 https://github.com/AriaLyy/Aria/issues/731
修复删除m3u8索引文件后,下载完成回调无法触发的问题
2020-08-14 11:11:55 +08:00
laoyuyu
07a1f45f1e 修复m3u8任务地址错误时,无法删除实体的问题 https://github.com/AriaLyy/Aria/issues/712
修复0kb的文件不可下载的问题 https://github.com/AriaLyy/Aria/issues/711
2020-08-14 11:11:14 +08:00
laoyuyu
22a3b61dbf Merge branch 'master' of github.com:AriaLyy/Aria 2020-08-13 16:42:31 +08:00
lyy
eccbb37647 Update FUNDING.yml 2020-08-13 16:29:25 +08:00
laoyuyu
ab358e7dbf 修复http表单上传,本地md5和上传的服务的的文件md5不一致的问题 https://github.com/AriaLyy/Aria/issues/730 2020-08-13 13:58:19 +08:00
laoyuyu
86aeac0ad8 修复m3u8gzip的问题,https://github.com/AriaLyy/Aria/issues/639
m3u8使用`ignoreFailureTs`后将不会自动重试失败的切片 https://github.com/AriaLyy/Aria/issues/662
2020-08-13 10:06:28 +08:00
laoyuyu
ff0796c580 Merge branch 'master' of github.com:AriaLyy/Aria 2020-08-12 18:32:12 +08:00
laoyuyu
fed2b4111f 增加允许不使用apt直接通过实现监听器来回调下载进度更新,该功能由[chenfei0928](https://github.com/chenfei0928)提交,感谢他的pr。如果注解不生效,只需要实现接口便可
修复m3u8任务地址错误时,无法删除实体的问题 https://github.com/AriaLyy/Aria/issues/71://github.com/AriaLyy/Aria/issues/712
2020-08-12 18:31:26 +08:00
lyy
1a04a6d221 Update FUNDING.yml 2020-08-12 08:52:00 +08:00
laoyuyu
5f60b9ed57 现在http文件下载将使用HEAD请求获取文件大小,配置文件增加 <useHeadRequest value=false/> 2020-07-27 19:16:09 +08:00
laoyuyu
9d8bf579b6 修复一个匿名内部类中的内存溢出的问题 https://github.com/AriaLyy/Aria/issues/705
m3u8密钥下载地址转换器增加ts列表的url地址https://github.com/AriaLyy/Aria/issues/718
2020-07-27 13:21:45 +08:00
lyy
bebc9b926d Merge pull request #716 from chmod4755/fix-encoding-plus-character
fix regex of character +
2020-07-22 15:14:39 +08:00
wangshang
ae1a9e3870 fix regex of character + 2020-07-22 14:24:10 +08:00
laoyuyu
288dcf633e 使用spi机制优化组件的加载 2020-07-12 11:29:32 +08:00
lyy
fed6ae55e1 Update README.md 2020-07-09 14:14:41 +08:00
lyy
41bae31d6a Merge pull request #709 from chenfei0928/master
允许不使用apt直接通过实现监听器来回调下载进度更新
2020-07-08 09:30:17 +08:00
陈斐
8787030fa6 调整下载任务监听器父类接口名 2020-07-07 17:13:50 +08:00
陈斐
b5cbbd9d3d 修复类型转换错误 2020-07-07 17:10:58 +08:00
陈斐
1241bc5991 通过广播发送下载进度事件时,限制在当前包发送广播 2020-07-07 16:08:56 +08:00
陈斐
a3df4016ff 添加通过父类接口判断来检查是某种下载任务类型 2020-07-07 15:29:44 +08:00
laoyuyu
981917fa52 修改dialogfragment实现 2020-07-05 12:27:02 +08:00
laoyuyu
023f648d07 fix bug https://github.com/AriaLyy/Aria/issues/695 2020-06-26 15:53:28 +08:00
laoyuyu
9a174311d9 fix bug https://github.com/AriaLyy/Aria/issues/703
fix bug https://github.com/AriaLyy/Aria/issues/702
优化异常提示
2020-06-26 13:32:54 +08:00
lyy
3edf40f533 Update FUNDING.yml 2020-06-26 11:11:08 +08:00
lyy
46c10f6ffd Update FUNDING.yml 2020-06-26 11:09:03 +08:00
lyy
32cb6152dd Update FUNDING.yml 2020-06-26 11:06:13 +08:00
lyy
f657bbcc53 Update FUNDING.yml 2020-06-26 11:05:37 +08:00
lyy
e6f5c4f472 Update FUNDING.yml 2020-06-26 11:04:03 +08:00
lyy
b7e0f6f0e8 Update FUNDING.yml 2020-06-26 11:02:33 +08:00
lyy
0bc083fa98 Update FUNDING.yml 2020-06-26 10:58:38 +08:00
lyy
d931b75800 Create FUNDING.yml 2020-06-26 10:57:36 +08:00
laoyuyu
418ec74b7b fix bug https://github.com/AriaLyy/Aria/issues/688
fix bug https://github.com/AriaLyy/Aria/issues/690
m3u8任务增加`setUseDefConvert()`方法,用于处理默认的m3u8任务
2020-06-14 15:03:15 +08:00
laoyuyu
29f9bdcdbd - 修复设置了cancel(false),文件还是被删除的问题 https://github.com/AriaLyy/Aria/issues/686
- 修复错误url的下载任务,无法删除的问题 https://github.com/AriaLyy/Aria/issues/684
2020-06-07 10:35:23 +08:00
laoyuyu
9a5e491e37 3.8.7 2020-05-25 08:49:44 +08:00
laoyuyu
e64f3b1240 Merge branch 'master' of github.com:AriaLyy/Aria 2020-05-16 16:19:18 +08:00
laoyuyu
dcde3f07b8 fix bug https://github.com/AriaLyy/Aria/issues/664 2020-05-16 10:07:52 +08:00
laoyuyu
290e58c58b fix bug https://github.com/AriaLyy/Aria/issues/670 2020-05-14 19:51:03 +08:00
lyy
b109726c7c Merge pull request #661 from DaveBoy/aria
子任务失败的问题处理
2020-04-26 09:11:35 +08:00
mxm
4d9a887076 子任务失败的问题处理 2020-04-24 16:54:46 +08:00
laoyuyu
a1c294bcbd Merge branch 'master' of github.com:AriaLyy/Aria 2020-04-18 11:31:04 +08:00
laoyuyu
1f2ac36580 fix bug https://github.com/AriaLyy/Aria/issues/646 2020-04-18 11:30:55 +08:00
lyy
203eeb10ad Merge pull request #657 from DaveBoy/aria
组合子任务多线程下载
2020-04-18 08:57:47 +08:00
mxm
06a0d764ed 分组子任务多线程下载 2020-04-16 09:56:06 +08:00
mxm
baf4bac091 子任务多线程 2020-04-15 18:05:45 +08:00
DaveBoy
4951dda5b0 Merge pull request #1 from AriaLyy/master
从主项目合并
2020-04-15 17:13:37 +08:00
lyy
ba0162e413 Update README.md 2020-04-14 13:25:22 +08:00
laoyuyu
0476c3a941 Merge branch 'master' of github.com:AriaLyy/Aria 2020-03-24 21:05:43 +08:00
laoyuyu
73292b6405 修复组合任务不执行 onPre的问题 2020-03-24 21:05:24 +08:00
lyy
e99d4a3d7e Merge pull request #640 from DaveBoy/aria
失败回调stop/fail设置完善
2020-03-19 10:42:42 +08:00
DaveBoy1994
e0dc7f3644 失败回调stop/fail设置完善 2020-03-19 09:26:48 +08:00
lyy
cd31276591 Merge pull request #633 from DaveBoy/aria
组合下载子任务失败Exception为空,单个任务下载失败后最终组任务回调stop/fail开关设置
2020-03-13 16:13:51 +08:00
DaveBoy1994
a65b2e31f9 修复下载路径中?后面存在/导致的崩溃问题 2020-03-13 10:40:33 +08:00
laoyuyu
de09add3a3 修复组任务中,其中一个子任务是30x地址,导致调度器无法出现该子任务状态的问题
增加组任务groupHash冲突检查 https://github.com/AriaLyy/Aria/issues/635
2020-03-11 21:46:26 +08:00
DaveBoy1994
41088dfa23 增加组任务中单个任务下载失败后,最终组任务回调stop/fail设置 2020-03-05 14:00:28 +08:00
DaveBoy1994
14901d4c0c 组合下载子任务失败Exception为空 2020-03-05 13:38:14 +08:00
laoyuyu
9cb6490da9 修复组任务进度为0的问题 2020-03-04 20:48:32 +08:00
laoyuyu
a1fd63b5c6 Merge branch 'master' of github.com:AriaLyy/Aria 2020-03-04 13:45:37 +08:00
lyy
b90450f1cd Merge pull request #632 from DaveBoy/aria
修复关联分页逻辑空指针异常
2020-03-04 13:45:17 +08:00
DaveBoy1994
370dca27ff 修复关联分页逻辑空指针异常 2020-03-04 13:23:00 +08:00
laoyuyu
669ac6b09c - 修复组任务,其中一个子任务在获取文件长度失败后,重新恢复组合任务,组合任务状态变为完成的问题 https://github.com/AriaLyy/Aria/issues/628 2020-03-03 21:03:50 +08:00
lyy
54cbdb7ee0 Merge pull request #631 from DaveBoy/aria
修改任务组分页逻辑
2020-03-02 20:56:28 +08:00
DaveBoy1994
e770818cee 修改任务组分页逻辑 2020-03-02 19:47:16 +08:00
laoyuyu
d92f1bba94 修复now模式下的一些问题 https://github.com/AriaLyy/Aria/issues/620
修复wait模式下,resume(true)无效问题
2020-02-22 13:08:47 +08:00
laoyuyu
ea2e27f3e9 修复40x错误,会继续重试并且无法重试成功的问题 https://github.com/AriaLyy/Aria/issues/619 2020-02-20 22:01:12 +08:00
laoyuyu
ad58dce395 修复组合任务单个子任务失败后,重新恢复组合任务,组合任务状态变为完成的问题 2020-02-20 19:58:48 +08:00
laoyuyu
472006ef61 3.8.6 2020-02-18 23:16:47 +08:00
lyy
604fb91800 Merge pull request #617 from DaveBoy/master
添加文件名适配器接口,修复group中useServerFileName未同步到子task的问题
2020-02-18 10:19:47 +08:00
DaveBoy1994
63ec23f241 添加文件名适配器接口,修复group中useServerFileName未同步到子task的问题 2020-02-18 10:01:10 +08:00
laoyuyu
2f2d783655 fix bug https://github.com/AriaLyy/Aria/issues/579#issuecomment-586665035
https://github.com/AriaLyy/Aria/issues/610
https://github.com/AriaLyy/Aria/issues/614
2020-02-17 23:49:20 +08:00
laoyuyu
e015c2a2f7 fix bug https://github.com/AriaLyy/Aria/issues/608
优化异常提示
2020-02-06 22:22:20 +08:00
laoyuyu
96efa35c4e demo 例子优化 2020-01-19 21:53:55 +08:00
laoyuyu
7f81995bd8 修改readme 2020-01-18 10:18:26 +08:00
285 changed files with 9916 additions and 6152 deletions

8
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
# These are supported funding model platforms
# https://help.github.com/cn/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository
# github: ["AriaLyy"]
# patreon: AriaLyy
# liberapay: Aria
open_collective: Aria
custom: ["https://paypal.me/arialyy"]

View File

@@ -11,6 +11,8 @@ about: 提交问题前请先阅读文档和搜索issue
* 系统版本
## 错误的url
## 错误日志
<!-- 请提供详细的错误日志 -->

20
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 7
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 1000
# Issues with these labels will never be considered stale
exemptLabels:
- bug
- adaptation
- new features
- suggest
- pending
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -1,50 +1,78 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
namespace "com.lyy.frame"
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
dataBinding {
enabled = true
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding true
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testImplementation 'junit:junit:4.12'
api "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}"
api 'com.google.code.gson:gson:2.8.2'
api 'io.reactivex:rxandroid:1.2.0'
api 'io.reactivex:rxjava:1.1.5'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.2.0'
implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
implementation 'androidx.multidex:multidex:2.0.1'
api fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
api 'androidx.lifecycle:lifecycle-runtime:2.0.0'
api 'androidx.lifecycle:lifecycle-extensions:2.0.0'
kapt 'androidx.lifecycle:lifecycle-compiler:2.0.0'
testImplementation 'junit:junit:4.13.2'
api 'androidx.appcompat:appcompat:1.3.1'
api 'com.google.code.gson:gson:2.14.0'
api 'io.reactivex.rxjava3:rxjava:3.1.12'
api 'io.reactivex.rxjava3:rxandroid:3.0.2'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
implementation 'com.squareup.retrofit2:retrofit:3.0.0'
// implementation 'com.squareup.retrofit2:adapter-rxjava:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:3.0.0'
implementation 'com.squareup.retrofit2:converter-gson:3.0.0'
implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
api 'androidx.lifecycle:lifecycle-common:2.6.2'
api 'androidx.lifecycle:lifecycle-runtime:2.6.2'
kapt 'androidx.lifecycle:lifecycle-compiler:2.6.2'
}

View File

@@ -2,33 +2,35 @@ package com.arialyy.frame.base.net;
import com.arialyy.frame.util.show.FL;
import com.arialyy.frame.util.show.L;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableSource;
import io.reactivex.rxjava3.core.ObservableTransformer;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
/**
* Created by “Aria.Lao” on 2016/10/26.
* HTTP数据回调
*/
public abstract class HttpCallback<T> implements INetResponse<T>, Observable.Transformer<T, T> {
public abstract class HttpCallback<T> implements INetResponse<T>, ObservableTransformer<T, T> {
@Override public void onFailure(Throwable e) {
L.e("HttpCallback", FL.getExceptionString(e));
}
@Override public Observable<T> call(Observable<T> observable) {
@Override public ObservableSource<T> apply(Observable<T> observable) {
Observable<T> tObservable = observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<T, T>() {
@Override public T call(T t) {
.map(new Function<T, T>() {
@Override public T apply(T t) {
onResponse(t);
return t;
}
})
.onErrorReturn(new Func1<Throwable, T>() {
@Override public T call(Throwable throwable) {
.onErrorReturn(new Function<Throwable, T>() {
@Override public T apply(Throwable throwable) {
onFailure(throwable);
return null;
}

View File

@@ -12,7 +12,7 @@ import com.google.gson.Gson;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
@@ -79,7 +79,7 @@ public class NetManager {
;
final Retrofit.Builder builder = new Retrofit.Builder().client(okHttpClient)
.baseUrl(NetConstant.BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create());
.addCallAdapterFactory(RxJava3CallAdapterFactory.create());
builder.addConverterFactory(f);
return builder.build().create(service);
}

View File

@@ -12,7 +12,7 @@ import javax.crypto.spec.SecretKeySpec;
/**
* AES加密
*
* @author lyy
* author lyy
*/
public class AESEncryption {
/**

View File

@@ -1,32 +1,51 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
namespace "com.arialyy.aria"
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'junit:junit:4.12'
// implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}"
api project(':AriaAnnotations')
api project(path: ':PublicComponent')
api project(path: ':HttpComponent')
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
apply from: 'bintray-release.gradle'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'junit:junit:4.13.2'
// implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}"
api project(path: ':AriaAnnotations')
api project(path: ':PublicComponent')
api project(path: ':HttpComponent')
}
//apply from: 'bintray-release.gradle'
ext {
PUBLISH_ARTIFACT_ID = 'core'
}
apply from: '../gradle/mavenCentral-release.gradle'

View File

@@ -27,6 +27,7 @@ import android.widget.PopupWindow;
import com.arialyy.aria.core.download.DownloadReceiver;
import com.arialyy.aria.core.upload.UploadReceiver;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
/**
* Created by lyy on 2016/12/1.
@@ -130,15 +131,20 @@ import com.arialyy.aria.util.ALog;
private static Context convertContext(Object obj) {
if (obj instanceof Application) {
return (Application) obj;
} else if (obj instanceof Service) {
}
if (obj instanceof Service) {
return (Service) obj;
} else if (obj instanceof Activity) {
}
if (obj instanceof Activity) {
return (Activity) obj;
} else if (AriaManager.isFragment(obj.getClass())) {
return AriaManager.getFragmentActivity(obj);
} else if (obj instanceof Dialog) {
}
if (CommonUtil.isFragment(obj.getClass())) {
return CommonUtil.getFragmentActivity(obj);
}
if (obj instanceof Dialog) {
return ((Dialog) obj).getContext();
} else if (obj instanceof PopupWindow) {
}
if (obj instanceof PopupWindow) {
return ((PopupWindow) obj).getContentView().getContext();
}
ALog.e("Aria", "请使用download(this)或upload(this)");

View File

@@ -19,11 +19,10 @@ import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.app.Dialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.widget.PopupWindow;
import android.util.Log;
import com.arialyy.aria.core.command.CommandManager;
import com.arialyy.aria.core.common.QueueMod;
import com.arialyy.aria.core.config.AppConfig;
@@ -35,21 +34,21 @@ import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.download.DownloadReceiver;
import com.arialyy.aria.core.inf.AbsReceiver;
import com.arialyy.aria.core.inf.IReceiver;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.inf.ReceiverType;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.upload.UploadReceiver;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.orm.DelegateWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.AriaCrashHandler;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.DeleteURecord;
import com.arialyy.aria.util.RecordUtil;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -61,35 +60,12 @@ import java.util.concurrent.ConcurrentHashMap;
private static final String TAG = "AriaManager";
private static final Object LOCK = new Object();
/**
* android、androidx、support的fragment、dialogFragment类名
*/
private static List<String> mFragmentClassName = new ArrayList<>();
private static List<String> mDialogFragmentClassName = new ArrayList<>();
@SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null;
private Map<String, AbsReceiver> mReceivers = new ConcurrentHashMap<>();
/**
* activity 和其Dialog、Fragment的映射表
*/
private Map<String, List<String>> mSubClass = new ConcurrentHashMap<>();
private static Context APP;
private DelegateWrapper mDbWrapper;
private AriaConfig mConfig;
static {
mFragmentClassName.add("androidx.fragment.app.Fragment");
mFragmentClassName.add("androidx.fragment.app.DialogFragment");
mFragmentClassName.add("android.app.Fragment");
mFragmentClassName.add("android.app.DialogFragment");
mFragmentClassName.add("android.support.v4.app.Fragment");
mFragmentClassName.add("android.support.v4.app.DialogFragment");
mDialogFragmentClassName.add("androidx.fragment.app.DialogFragment");
mDialogFragmentClassName.add("android.app.DialogFragment");
mDialogFragmentClassName.add("android.support.v4.app.DialogFragment");
}
private AriaManager(Context context) {
APP = context.getApplicationContext();
}
@@ -251,6 +227,7 @@ import java.util.concurrent.ConcurrentHashMap;
* 处理下载操作
*/
DownloadReceiver download(Object obj) {
IReceiver receiver = mReceivers.get(getKey(ReceiverType.DOWNLOAD, obj));
if (receiver == null) {
receiver = putReceiver(ReceiverType.DOWNLOAD, obj);
@@ -285,143 +262,24 @@ import java.util.concurrent.ConcurrentHashMap;
RecordUtil.delGroupTaskRecordByHash(key, removeFile);
break;
case 3:
RecordUtil.delTaskRecord(key, IRecordHandler.TYPE_UPLOAD);
DeleteURecord.getInstance().deleteRecord(key, removeFile, true);
break;
}
}
private IReceiver putReceiver(String type, Object obj) {
private IReceiver putReceiver(ReceiverType type, Object obj) {
final String key = getKey(type, obj);
IReceiver receiver = mReceivers.get(key);
boolean needRmReceiver = false;
// 监控Dialog、fragment、popupWindow的生命周期
final WidgetLiftManager widgetLiftManager = new WidgetLiftManager();
Context subParenActivity = null;
if (obj instanceof Dialog) {
needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj);
subParenActivity = ((Dialog) obj).getOwnerActivity();
} else if (obj instanceof PopupWindow) {
needRmReceiver = widgetLiftManager.handlePopupWindowLift((PopupWindow) obj);
subParenActivity = ((PopupWindow) obj).getContentView().getContext();
} else if (isDialogFragment(obj.getClass())) {
needRmReceiver = widgetLiftManager.handleDialogFragmentLift(getDialog(obj));
subParenActivity = getFragmentActivity(obj);
} else if (isFragment(obj.getClass())) {
subParenActivity = getFragmentActivity(obj);
}
if (subParenActivity instanceof Activity) {
relateSubClass(type, obj, (Activity) subParenActivity);
}
if (receiver == null) {
AbsReceiver absReceiver =
type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver() : new UploadReceiver();
absReceiver.targetName = obj.getClass().getName();
AbsReceiver.OBJ_MAP.put(absReceiver.getKey(), obj);
absReceiver.needRmListener = needRmReceiver;
type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver(obj) : new UploadReceiver(obj);
mReceivers.put(key, absReceiver);
receiver = absReceiver;
}
return receiver;
}
/**
* 获取fragment的activity
*
* @return 获取失败返回null
*/
static Activity getFragmentActivity(Object obj) {
try {
Method method = obj.getClass().getMethod("getActivity");
return (Activity) method.invoke(obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* 判断注解对象是否是fragment
*
* @return true 对象是fragment
*/
static boolean isFragment(Class subClazz) {
Class parentClass = subClazz.getSuperclass();
if (parentClass == null) {
return false;
} else {
String parentName = parentClass.getName();
if (mFragmentClassName.contains(parentName)) {
return true;
} else {
return isFragment(parentClass);
}
}
}
/**
* 判断对象是否是DialogFragment
*
* @return true 对象是DialogFragment
*/
private boolean isDialogFragment(Class subClazz) {
Class parentClass = subClazz.getSuperclass();
if (parentClass == null) {
return false;
} else {
String parentName = parentClass.getName();
if (mDialogFragmentClassName.contains(parentName)) {
return true;
} else {
return isDialogFragment(parentClass);
}
}
}
/**
* 获取DialogFragment的dialog
*
* @return 获取失败返回null
*/
private Dialog getDialog(Object obj) {
try {
Method method = obj.getClass().getMethod("getDialog");
return (Dialog) method.invoke(obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* 关联Activity类和Fragment间的关系
*
* @param sub Fragment或dialog类
* @param activity activity寄主类
*/
private void relateSubClass(String type, Object sub, Activity activity) {
String key = getKey(type, activity);
List<String> subClass = mSubClass.get(key);
if (subClass == null) {
subClass = new ArrayList<>();
mSubClass.put(key, subClass);
}
subClass.add(getKey(type, sub));
if (mReceivers.get(key) == null) { // 将activity填充进去
mReceivers.put(key, new DownloadReceiver());
}
}
/**
* 根据功能类型和控件类型获取对应的key
*
@@ -430,8 +288,8 @@ import java.util.concurrent.ConcurrentHashMap;
* @return key的格式为{@code String.format("%s_%s_%s", obj.getClass().getName(), type,
* obj.hashCode());}
*/
private String getKey(String type, Object obj) {
return String.format("%s_%s_%s", obj.getClass().getName(), type, obj.hashCode());
private String getKey(ReceiverType type, Object obj) {
return String.format("%s_%s_%s", CommonUtil.getTargetName(obj), type.name(), obj.hashCode());
}
/**
@@ -453,40 +311,47 @@ import java.util.concurrent.ConcurrentHashMap;
ALog.e(TAG, "target obj is null");
return;
}
List<String> temp = new ArrayList<>();
// 移除寄主的receiver
for (Iterator<Map.Entry<String, AbsReceiver>> iter = mReceivers.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry<String, AbsReceiver> entry = iter.next();
String key = entry.getKey();
if (key.equals(getKey(ReceiverType.DOWNLOAD, obj)) || key.equals(
getKey(ReceiverType.UPLOAD, obj))) {
AbsReceiver receiver = mReceivers.get(key);
List<String> subNames = mSubClass.get(key);
if (subNames != null && !subNames.isEmpty()) {
temp.addAll(subNames);
}
if (receiver != null) {
receiver.destroy();
AbsReceiver receiver = entry.getValue();
if (receiver.isFragment()){
Method method = CommonUtil.getMethod(receiver.obj.getClass(), "getActivity");
if (method != null){
try {
Activity ac = (Activity) method.invoke(receiver.obj);
if (ac == obj){
receiver.destroy();
iter.remove();
continue;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
// 处理内部类的
String objClsName = obj.getClass().getName();
if (receiver.isLocalOrAnonymousClass && key.startsWith(objClsName)) {
receiver.destroy();
iter.remove();
continue;
}
if (key.equals(getKey(ReceiverType.DOWNLOAD, obj))
|| key.equals(getKey(ReceiverType.UPLOAD, obj))
) {
receiver.destroy();
iter.remove();
}
}
// 移除寄生的receiver
if (!temp.isEmpty()) {
for (Iterator<Map.Entry<String, AbsReceiver>> iter = mReceivers.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry<String, AbsReceiver> entry = iter.next();
if (temp.contains(entry.getKey())) {
AbsReceiver receiver = mReceivers.get(entry.getKey());
if (receiver != null) {
receiver.destroy();
}
iter.remove();
}
}
}
Log.d(TAG, "debug");
}
/**

View File

@@ -29,20 +29,20 @@ import java.lang.reflect.Field;
* Created by lyy on 2017/2/7.
* 为组件添加生命周期
*/
final class WidgetLiftManager {
public final class WidgetLiftManager {
private final String TAG = "WidgetLiftManager";
/**
* 处理DialogFragment事件
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB) boolean handleDialogFragmentLift(Dialog dialog) {
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public boolean handleDialogFragmentLift(Dialog dialog) {
return handleDialogLift(dialog);
}
/**
* 处理悬浮框取消或dismiss事件
*/
boolean handlePopupWindowLift(PopupWindow popupWindow) {
public boolean handlePopupWindowLift(PopupWindow popupWindow) {
try {
Field dismissField = CommonUtil.getField(popupWindow.getClass(), "mOnDismissListener");
PopupWindow.OnDismissListener listener =
@@ -76,7 +76,7 @@ final class WidgetLiftManager {
*
* @return true 设置了dialog的销毁事件。false 没有设置dialog的销毁事件
*/
boolean handleDialogLift(Dialog dialog) {
public boolean handleDialogLift(Dialog dialog) {
if (dialog == null) {
ALog.w(TAG,
"dialog 为空没有设置自动销毁事件为了防止内存泄露请在dismiss方法中调用Aria.download(this).unRegister();来注销事件\n"

View File

@@ -29,7 +29,7 @@ import com.arialyy.aria.util.NetUtils;
*/
final public class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
private boolean newStart = false;
private boolean nowStart = false;
StartCmd(T entity, int taskType) {
super(entity, taskType);
@@ -38,10 +38,10 @@ final public class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
/**
* 立即执行任务
*
* @param newStart true 立即执行任务,无论执行队列是否满了
* @param nowStart true 立即执行任务,无论执行队列是否满了
*/
public void setNewStart(boolean newStart) {
this.newStart = newStart;
public void setNowStart(boolean nowStart) {
this.nowStart = nowStart;
}
@Override public void executeCmd() {
@@ -82,7 +82,7 @@ final public class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
startTask();
}
} else {
if (newStart) {
if (nowStart) {
startTask();
} else {
sendWaitState(task);
@@ -92,7 +92,19 @@ final public class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
} else {
//任务没执行并且执行队列中没有该任务,才认为任务没有运行中
if (!mQueue.taskIsRunning(task.getKey())) {
resumeTask();
if (mod.equals(QueueMod.NOW.getTag())) {
resumeTask();
} else {
if (mQueue.getCurrentExePoolNum() < maxTaskNum) {
resumeTask();
} else {
if (nowStart) {
resumeTask();
} else {
sendWaitState(task);
}
}
}
} else {
ALog.w(TAG, String.format("任务【%s】已经在运行", task.getTaskName()));
}

View File

@@ -23,6 +23,7 @@ import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.DeleteDGRecord;
import com.arialyy.aria.util.RecordUtil;
/**
@@ -82,11 +83,10 @@ public abstract class AbsNormalTarget<TARGET extends AbsNormalTarget> extends Ab
cancel();
} else {
if (getEntity() instanceof AbsNormalEntity) {
RecordUtil.delTaskRecord((AbsNormalEntity) getEntity(), getTaskWrapper().isRemoveFile());
RecordUtil.delNormalTaskRecord((AbsNormalEntity) getEntity(), getTaskWrapper().isRemoveFile());
} else if (getEntity() instanceof DownloadGroupEntity) {
RecordUtil.delGroupTaskRecord(((DownloadGroupEntity) getEntity()),
getTaskWrapper().isRemoveFile(),
true);
DeleteDGRecord.getInstance()
.deleteRecord(getEntity(), getTaskWrapper().isRemoveFile(), true);
}
TaskWrapperManager.getInstance().removeTaskWrapper(getTaskWrapper());
}

View File

@@ -17,6 +17,7 @@ package com.arialyy.aria.core.common;
import android.text.TextUtils;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
import com.arialyy.aria.core.processor.IHttpFileNameAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
import java.net.Proxy;
@@ -35,6 +36,7 @@ public class HttpOption extends BaseOption {
private Proxy proxy;
private boolean useServerFileName = false;
private IHttpFileLenAdapter fileLenAdapter;
private IHttpFileNameAdapter fileNameAdapter;
private String attachment;
public HttpOption() {
@@ -168,4 +170,12 @@ public class HttpOption extends BaseOption {
this.fileLenAdapter = fileLenAdapter;
return this;
}
public HttpOption setFilNameAdapter(IHttpFileNameAdapter fileNameAdapter) {
if (fileNameAdapter == null) {
throw new IllegalArgumentException("adapter为空");
}
CheckUtil.checkMemberClass(fileNameAdapter.getClass());
this.fileNameAdapter = fileNameAdapter;
return this;
}
}

View File

@@ -16,6 +16,12 @@
package com.arialyy.aria.core.common;
import com.arialyy.annotations.TaskEnum;
import com.arialyy.aria.core.download.DownloadGroupTaskListener;
import com.arialyy.aria.core.download.DownloadTaskListener;
import com.arialyy.aria.core.scheduler.M3U8PeerTaskListener;
import com.arialyy.aria.core.scheduler.SubTaskListener;
import com.arialyy.aria.core.scheduler.TaskInternalListenerInterface;
import com.arialyy.aria.core.upload.UploadTaskListener;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -69,12 +75,26 @@ public class ProxyHelper {
* @return {@link #PROXY_TYPE_DOWNLOAD}如果没有实体对象则返回空的list
*/
public Set<Integer> checkProxyType(Class clazz) {
final String className = clazz.getName();
Set<Integer> result = mProxyCache.get(clazz.getName());
if (result != null) {
return result;
}
result = new HashSet<>();
result = checkProxyTypeByInterface(clazz);
if (result != null && !result.isEmpty()) {
mProxyCache.put(clazz.getName(), result);
return result;
}
result = checkProxyTypeByProxyClass(clazz);
if (!result.isEmpty()) {
mProxyCache.put(clazz.getName(), result);
}
return result;
}
private Set<Integer> checkProxyTypeByProxyClass(Class clazz) {
final String className = clazz.getName();
Set<Integer> result = new HashSet<>();
if (checkProxyExist(className, TaskEnum.DOWNLOAD_GROUP.proxySuffix)) {
result.add(PROXY_TYPE_DOWNLOAD_GROUP);
}
@@ -93,9 +113,31 @@ public class ProxyHelper {
if (checkProxyExist(className, TaskEnum.DOWNLOAD_GROUP_SUB.proxySuffix)) {
result.add(PROXY_TYPE_DOWNLOAD_GROUP_SUB);
}
return result;
}
if (!result.isEmpty()) {
mProxyCache.put(clazz.getName(), result);
private Set<Integer> checkProxyTypeByInterface(Class clazz) {
if (!TaskInternalListenerInterface.class.isAssignableFrom(clazz)) {
return null;
}
Set<Integer> result = new HashSet<>();
if (DownloadGroupTaskListener.class.isAssignableFrom(clazz)) {
result.add(PROXY_TYPE_DOWNLOAD_GROUP);
}
if (DownloadTaskListener.class.isAssignableFrom(clazz)) {
result.add(PROXY_TYPE_DOWNLOAD);
}
if (UploadTaskListener.class.isAssignableFrom(clazz)) {
result.add(PROXY_TYPE_UPLOAD);
}
if (M3U8PeerTaskListener.class.isAssignableFrom(clazz)) {
result.add(PROXY_TYPE_M3U8_PEER);
}
if (SubTaskListener.class.isAssignableFrom(clazz)) {
result.add(PROXY_TYPE_DOWNLOAD_GROUP_SUB);
}
return result;
}

View File

@@ -35,7 +35,7 @@ public class SFtpOption extends BaseOption {
public SFtpOption() {
super();
ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8);
ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_SFTP);
}
public SFtpOption charSet(String charSet) {

View File

@@ -17,8 +17,10 @@ package com.arialyy.aria.core.common.controller;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.download.CheckDEntityUtil;
@@ -36,6 +38,7 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -160,37 +163,79 @@ public abstract class FeatureController {
/**
* 检查权限,需要的权限有
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE}
* {@link Manifest.permission#READ_EXTERNAL_STORAGE}
* {@link Manifest.permission#INTERNET}
* 存储权限会根据 Android 版本与下载模式(分区存储 Uri / 普通文件路径)进行适配:
* <ul>
* <li>分区存储 Uri 模式MediaStore / SAF跨 Android 版本均无需申请存储权限</li>
* <li>Android 9 及以下:{@link Manifest.permission#WRITE_EXTERNAL_STORAGE} +
* {@link Manifest.permission#READ_EXTERNAL_STORAGE}</li>
* <li>Android 10Q同上仍可请求 legacy 存储</li>
* <li>Android 11/12R/S{@link Manifest.permission#WRITE_EXTERNAL_STORAGE} 已废弃,
* 仅需 {@link Manifest.permission#READ_EXTERNAL_STORAGE}</li>
* <li>Android 13+TIRAMISUREAD/WRITE_EXTERNAL_STORAGE 不再强制,使用细分媒体权限
* {@link Manifest.permission#READ_MEDIA_IMAGES} / {@link Manifest.permission#READ_MEDIA_VIDEO} /
* {@link Manifest.permission#READ_MEDIA_AUDIO},或兼容旧 target 的 READ_EXTERNAL_STORAGE</li>
* </ul>
*
* @return {@code false} 缺少权限
*/
private boolean checkPermission() {
if (AriaConfig.getInstance()
.getAPP()
.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ALog.e(TAG, "启动失败缺少权限Manifest.permission.WRITE_EXTERNAL_STORAGE");
return false;
}
if (AriaConfig.getInstance()
.getAPP()
.checkCallingOrSelfPermission(Manifest.permission.INTERNET)
!= PackageManager.PERMISSION_GRANTED) {
if (!hasPermission(Manifest.permission.INTERNET)) {
ALog.e(TAG, "启动失败缺少权限Manifest.permission.INTERNET");
return false;
}
if (AriaConfig.getInstance()
.getAPP()
.checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ALog.e(TAG, "启动失败缺少权限Manifest.permission.READ_EXTERNAL_STORAGE");
return false;
// 分区存储 Uri 模式MediaStore / SAF写入 Uri跨 Android 版本均无需存储权限
if (isUriTask()) {
return true;
}
return true;
int sdk = Build.VERSION.SDK_INT;
if (sdk >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+READ/WRITE_EXTERNAL_STORAGE 已不再强制,使用细分媒体权限;
// 同时声明 READ_EXTERNAL_STORAGE 在 13+ 仍会被授予(兼容旧 target二者满足其一即可
if (!hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
&& !hasPermission(Manifest.permission.READ_MEDIA_IMAGES)
&& !hasPermission(Manifest.permission.READ_MEDIA_VIDEO)
&& !hasPermission(Manifest.permission.READ_MEDIA_AUDIO)) {
ALog.e(TAG, "启动失败缺少权限Android 13+ 需要 Manifest.permission.READ_MEDIA_* "
+ "或 Manifest.permission.READ_EXTERNAL_STORAGE");
return false;
}
return true;
} else if (sdk >= Build.VERSION_CODES.R) {
// Android 11/12WRITE_EXTERNAL_STORAGE 已废弃且不再授予共享存储写权限,读仍可通过
// READ_EXTERNAL_STORAGE 兼容获取
if (!hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) {
ALog.e(TAG, "启动失败缺少权限Manifest.permission.READ_EXTERNAL_STORAGE");
return false;
}
return true;
} else {
// Android 10 及以下:需要读写外部存储权限
if (!hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
ALog.e(TAG, "启动失败缺少权限Manifest.permission.WRITE_EXTERNAL_STORAGE");
return false;
}
if (!hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) {
ALog.e(TAG, "启动失败缺少权限Manifest.permission.READ_EXTERNAL_STORAGE");
return false;
}
return true;
}
}
/**
* 是否为分区存储 Uri 任务Uri 模式下通过 MediaStore / SAF 写入,无需存储权限
*/
private boolean isUriTask() {
return mTaskWrapper instanceof DTaskWrapper && ((DTaskWrapper) mTaskWrapper).isUriMode();
}
private boolean hasPermission(String permission) {
return AriaConfig.getInstance()
.getAPP()
.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
private boolean checkEntity() {

View File

@@ -66,7 +66,7 @@ public final class NormalController extends FeatureController implements INormal
StartCmd cmd =
(StartCmd) CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START,
checkTaskType());
cmd.setNewStart(newStart);
cmd.setNowStart(newStart);
EventMsgUtil.getDefault()
.post(cmd);
}

View File

@@ -15,17 +15,24 @@
*/
package com.arialyy.aria.core.download;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.common.controller.FeatureController;
import com.arialyy.aria.core.inf.ICheckEntityUtil;
import com.arialyy.aria.core.inf.IOptionConstant;
import com.arialyy.aria.core.inf.ITargetHandler;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.RecordUtil;
import com.arialyy.aria.util.UriFileUtil;
import java.io.File;
/**
@@ -50,7 +57,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil {
@Override
public boolean checkEntity() {
if (mWrapper.getErrorEvent() != null) {
ALog.e(TAG, String.format("下载失败,%s", mWrapper.getErrorEvent().errorMsg));
ALog.e(TAG, String.format("任务操作失败,%s", mWrapper.getErrorEvent().errorMsg));
return false;
}
@@ -105,11 +112,20 @@ public class CheckDEntityUtil implements ICheckEntityUtil {
}
private boolean checkFilePath() {
if (mWrapper instanceof DTaskWrapper
&& !TextUtils.isEmpty(((DTaskWrapper) mWrapper).getFileUri())) {
return checkUriPath();
}
String filePath = mWrapper.getTempFilePath();
if (TextUtils.isEmpty(filePath)) {
ALog.e(TAG, "下载失败文件保存路径为null");
return false;
} else if (!filePath.startsWith("/")) {
}
if (!FileUtil.canWrite(new File(filePath).getParent())){
ALog.e(TAG, String.format("路径【%s】不可写", filePath));
return false;
}
if (!filePath.startsWith("/")) {
ALog.e(TAG, String.format("下载失败,文件保存路径【%s】错误", filePath));
return false;
}
@@ -133,14 +149,68 @@ public class CheckDEntityUtil implements ICheckEntityUtil {
return checkPathConflicts(filePath);
}
/**
* 校验通过 Uri 下载的路径合法性
*/
private boolean checkUriPath() {
String uriStr = ((DTaskWrapper) mWrapper).getFileUri();
Uri uri = Uri.parse(uriStr);
Context context = AriaConfig.getInstance().getAPP();
int requestType = mWrapper.getRequestType();
if (requestType != ITaskWrapper.D_HTTP && requestType != ITaskWrapper.D_FTP) {
ALog.e(TAG, "当前任务类型不支持传入 Uri 下载,请使用 setFilePath 指定文件路径");
return false;
}
if (!UriFileUtil.canWrite(context, uri)) {
ALog.e(TAG, String.format("Uri【%s】不可写请确认已通过 SAF 授予写入权限", uriStr));
return false;
}
UriFileUtil.tryTakePersistablePermission(context, uri);
if (TextUtils.isEmpty(mEntity.getFileName())) {
mEntity.setFileName(UriFileUtil.getFileName(context, uri));
}
mEntity.setFileUri(uriStr);
return checkUriConflicts(uriStr);
}
/**
* 检查 Uri 冲突
*/
private boolean checkUriConflicts(String uriStr) {
DownloadEntity de = DbEntity.findFirst(DownloadEntity.class, "fileUri=?", uriStr);
if (de != null) {
if (de.getUrl().equals(mEntity.getUrl())) {
mEntity.rowID = de.rowID;
mEntity.setFileUri(uriStr);
if (!TextUtils.isEmpty(de.getFileName())) {
mEntity.setFileName(de.getFileName());
}
return true;
} else if (!mWrapper.isIgnoreFilePathOccupy()) {
ALog.e(TAG, String.format("Uri【%s】已被其他任务占用", uriStr));
return false;
}
}
return true;
}
/**
* 检查路径冲突
* @return true 路径没有冲突
*/
private boolean checkPathConflicts(String filePath) {
DownloadEntity de = DbEntity.findFirst(DownloadEntity.class, "downloadPath=?", filePath);
if (de != null && de.getUrl().equals(mEntity.getUrl())){
mEntity.rowID = de.rowID;
mEntity.setFilePath(filePath);
mEntity.setFileName(new File(filePath).getName());
return true;
}
//设置文件保存路径,如果新文件路径和旧文件路径不同,则修改路径
if (!filePath.equals(mEntity.getFilePath())) {
// 检查路径冲突
if (!CheckUtil.checkDPathConflicts(mWrapper.isIgnoreFilePathOccupy(), filePath)) {
if (!CheckUtil.checkDPathConflicts(mWrapper.isIgnoreFilePathOccupy(), filePath,
mWrapper.getRequestType())) {
return false;
}

View File

@@ -23,7 +23,7 @@ import com.arialyy.aria.core.inf.IOptionConstant;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.RecordUtil;
import java.io.File;
import java.util.ArrayList;
@@ -44,6 +44,9 @@ public class CheckDGEntityUtil implements ICheckEntityUtil {
private boolean needModifyPath = false;
private int action;
/**
* @param action {@link FeatureController#ACTION_CREATE}
*/
public static CheckDGEntityUtil newInstance(DGTaskWrapper wrapper, int action) {
return new CheckDGEntityUtil(wrapper, action);
}
@@ -60,33 +63,41 @@ public class CheckDGEntityUtil implements ICheckEntityUtil {
* @return {@code true} 合法
*/
private boolean checkDirPath() {
if (TextUtils.isEmpty(mWrapper.getDirPathTemp())) {
String dirPath = mWrapper.getDirPathTemp();
if (TextUtils.isEmpty(dirPath)) {
ALog.e(TAG, "文件夹路径不能为null");
return false;
} else if (!mWrapper.getDirPathTemp().startsWith("/")) {
ALog.e(TAG, String.format("文件夹路径【%s】错误", mWrapper.getDirPathTemp()));
}
File file = new File(dirPath);
if (!FileUtil.canWrite(file.getParent()) && !FileUtil.canWrite(dirPath)) {
ALog.e(TAG, String.format("路径【%s】不可写", dirPath));
return false;
}
File file = new File(mWrapper.getDirPathTemp());
if (!dirPath.startsWith("/")) {
ALog.e(TAG, String.format("文件夹路径【%s】错误", dirPath));
return false;
}
if (file.isFile()) {
ALog.e(TAG, String.format("路径【%s】是文件请设置文件夹路径", mWrapper.getDirPathTemp()));
ALog.e(TAG, String.format("路径【%s】是文件请设置文件夹路径", dirPath));
return false;
}
// 检查路径冲突
if (mWrapper.isNewTask() && !CheckUtil.checkDGPathConflicts(mWrapper.isIgnoreFilePathOccupy(),
mWrapper.getDirPathTemp())) {
dirPath)) {
return false;
}
if (TextUtils.isEmpty(mEntity.getDirPath()) || !mEntity.getDirPath()
.equals(mWrapper.getDirPathTemp())) {
.equals(dirPath)) {
if (!file.exists()) {
file.mkdirs();
}
needModifyPath = true;
mEntity.setDirPath(mWrapper.getDirPathTemp());
ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", mWrapper.getDirPathTemp()));
mEntity.setDirPath(dirPath);
ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", dirPath));
}
return true;
}
@@ -113,10 +124,43 @@ public class CheckDGEntityUtil implements ICheckEntityUtil {
}
}
/**
* 检查和处理组合任务的路径冲突
*
* @param isIgnoreTaskOccupy true如果hash冲突将删除其它任务的记录的
* @param groupHash 组任务hash
* @return false 任务不再执行true 任务继续执行
*/
private boolean checkGroupHash(boolean isIgnoreTaskOccupy, String groupHash) {
DownloadGroupEntity dge =
DbEntity.findFirst(DownloadGroupEntity.class, "groupHash=?", groupHash);
if (dge != null && dge.getGroupHash().equals(mEntity.getGroupHash())) {
mEntity.rowID = dge.rowID;
return true;
}
if (DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", groupHash)) {
if (!isIgnoreTaskOccupy) {
ALog.e(TAG, String.format("下载失败数据库中已存在相同的url的组任务groupHash = %s", groupHash));
return false;
} else {
ALog.w(TAG, String.format("数据库中已存在相同的url的组任务将删除groupHash = %s 的旧任务", groupHash));
RecordUtil.delGroupTaskRecordByHash(groupHash, true);
return true;
}
}
return true;
}
@Override
public boolean checkEntity() {
if (mWrapper.getErrorEvent() != null) {
ALog.e(TAG, String.format("操作失败,%s", mWrapper.getErrorEvent().errorMsg));
ALog.e(TAG, String.format("任务操作失败,%s", mWrapper.getErrorEvent().errorMsg));
return false;
}
if ((action == FeatureController.ACTION_CREATE || action == FeatureController.ACTION_ADD)
&& !checkGroupHash(mWrapper.isIgnoreTaskOccupy(), mEntity.getGroupHash())) {
return false;
}
@@ -240,9 +284,6 @@ public class CheckDGEntityUtil implements ICheckEntityUtil {
mWrapper.getSubNameTemp().remove(index);
}
}
mEntity.setGroupHash(CommonUtil.getMd5Code(mEntity.getUrls()));
return true;
}

View File

@@ -45,32 +45,34 @@ public class CheckFtpDirEntityUtil implements ICheckEntityUtil {
* @return {@code true} 合法
*/
private boolean checkDirPath() {
if (TextUtils.isEmpty(mWrapper.getDirPathTemp())) {
String dirPath = mWrapper.getDirPathTemp();
if (TextUtils.isEmpty(dirPath)) {
ALog.e(TAG, "文件夹路径不能为null");
return false;
} else if (!mWrapper.getDirPathTemp().startsWith("/")) {
ALog.e(TAG, String.format("文件夹路径【%s】错误", mWrapper.getDirPathTemp()));
}
if (!dirPath.startsWith("/")) {
ALog.e(TAG, String.format("文件夹路径【%s】错误", dirPath));
return false;
}
File file = new File(mWrapper.getDirPathTemp());
File file = new File(dirPath);
if (file.isFile()) {
ALog.e(TAG, String.format("路径【%s】是文件请设置文件夹路径", mWrapper.getDirPathTemp()));
ALog.e(TAG, String.format("路径【%s】是文件请设置文件夹路径", dirPath));
return false;
}
// 检查路径冲突
if (mWrapper.isNewTask() && !CheckUtil.checkDGPathConflicts(mWrapper.isIgnoreFilePathOccupy(),
mWrapper.getDirPathTemp())) {
dirPath)) {
return false;
}
if (TextUtils.isEmpty(mEntity.getDirPath()) || !mEntity.getDirPath()
.equals(mWrapper.getDirPathTemp())) {
.equals(dirPath)) {
if (!file.exists()) {
file.mkdirs();
}
mEntity.setDirPath(mWrapper.getDirPathTemp());
ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", mWrapper.getDirPathTemp()));
mEntity.setDirPath(dirPath);
ALog.i(TAG, String.format("文件夹路径改变,将更新文件夹路径为:%s", dirPath));
}
return true;
}
@@ -78,7 +80,7 @@ public class CheckFtpDirEntityUtil implements ICheckEntityUtil {
@Override
public boolean checkEntity() {
if (mWrapper.getErrorEvent() != null) {
ALog.e(TAG, String.format("下载失败,%s", mWrapper.getErrorEvent().errorMsg));
ALog.e(TAG, String.format("任务操作失败,%s", mWrapper.getErrorEvent().errorMsg));
return false;
}

View File

@@ -0,0 +1,26 @@
/*
* 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.download;
import com.arialyy.aria.core.scheduler.NormalTaskListenerInterface;
import com.arialyy.aria.core.task.DownloadGroupTask;
/**
* @author ChenFei(chenfei0928 @ gmail.com)
* Date: 2020-07-07 14:12
*/
public interface DownloadGroupTaskListener extends NormalTaskListenerInterface<DownloadGroupTask> {
}

View File

@@ -38,6 +38,9 @@ import com.arialyy.aria.core.inf.AbsReceiver;
import com.arialyy.aria.core.inf.ReceiverType;
import com.arialyy.aria.core.queue.DGroupTaskQueue;
import com.arialyy.aria.core.queue.DTaskQueue;
import com.arialyy.aria.core.scheduler.M3U8PeerTaskListener;
import com.arialyy.aria.core.scheduler.SubTaskListener;
import com.arialyy.aria.core.scheduler.TaskInternalListenerInterface;
import com.arialyy.aria.core.scheduler.TaskSchedulers;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.orm.DbEntity;
@@ -55,7 +58,10 @@ import java.util.Set;
* 下载功能接收器
*/
public class DownloadReceiver extends AbsReceiver {
private final String TAG = "DownloadReceiver";
public DownloadReceiver(Object obj) {
super(obj);
}
/**
* 设置最大下载速度单位kb
@@ -105,8 +111,8 @@ public class DownloadReceiver extends AbsReceiver {
/**
* 加载组合任务,用于任务停止、删除等操作
*
* @param taskId 任务id可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link
* * AbsEntity#getId()}读取任务id
* @param taskId 任务id可从{@link AbsBuilderTarget#create}、{@link AbsBuilderTarget#add}、{@link
* AbsEntity#getId}读取任务id
*/
public GroupNormalTarget loadGroup(long taskId) {
@@ -164,11 +170,27 @@ public class DownloadReceiver extends AbsReceiver {
* 将当前类注册到Aria
*/
public void register() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) {
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
ALog.e(TAG, String.format("register【%s】观察者为空", getTargetName()));
return;
}
if (obj instanceof TaskInternalListenerInterface){
ProxyHelper.getInstance().checkProxyType(obj.getClass());
if (obj instanceof DownloadTaskListener){
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD);
}
if (obj instanceof DownloadGroupTaskListener){
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD_GROUP);
}
if (obj instanceof M3U8PeerTaskListener){
TaskSchedulers.getInstance().register(obj, TaskEnum.M3U8_PEER);
}
if (obj instanceof SubTaskListener){
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD_GROUP_SUB);
}
return;
}
Set<Integer> set = ProxyHelper.getInstance().checkProxyType(obj.getClass());
if (set != null && !set.isEmpty()) {
for (Integer type : set) {
@@ -199,20 +221,19 @@ public class DownloadReceiver extends AbsReceiver {
* @see <a href="https://aria.laoyuyu.me/aria_doc/start/any_java.html#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9">module类中销毁</a>
*/
@Override public void unRegister() {
if (needRmListener) {
if (isNeedRmListener()) {
unRegisterListener();
}
AriaManager.getInstance().removeReceiver(OBJ_MAP.get(getKey()));
AriaManager.getInstance().removeReceiver(obj);
}
@Override public String getType() {
@Override public ReceiverType getType() {
return ReceiverType.DOWNLOAD;
}
@Override protected void unRegisterListener() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) {
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
ALog.e(TAG, String.format("unRegister【%s】观察者为空", getTargetName()));
return;
}
Set<Integer> set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName());
@@ -220,7 +241,9 @@ public class DownloadReceiver extends AbsReceiver {
for (Integer integer : set) {
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
TaskSchedulers.getInstance().unRegister(obj);
} else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
continue;
}
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
TaskSchedulers.getInstance().unRegister(obj);
}
}
@@ -417,15 +440,7 @@ public class DownloadReceiver extends AbsReceiver {
* @return 如果没有任务组列表则返回null
*/
public List<DownloadGroupEntity> getGroupTaskList() {
List<DGEntityWrapper> wrappers = DbEntity.findRelationData(DGEntityWrapper.class);
if (wrappers == null || wrappers.isEmpty()) {
return null;
}
List<DownloadGroupEntity> entities = new ArrayList<>();
for (DGEntityWrapper wrapper : wrappers) {
entities.add(wrapper.groupEntity);
}
return entities;
return getGroupTaskList(1, 10);
}
/**

View File

@@ -0,0 +1,26 @@
/*
* 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.download;
import com.arialyy.aria.core.scheduler.NormalTaskListenerInterface;
import com.arialyy.aria.core.task.DownloadTask;
/**
* @author ChenFei(chenfei0928@gmail.com)
* Date: 2020-07-07 14:12
*/
public interface DownloadTaskListener extends NormalTaskListenerInterface<DownloadTask> {
}

View File

@@ -37,12 +37,23 @@ public class M3U8Option<OP extends M3U8Option> extends BaseOption {
private IKeyUrlConverter keyUrlConverter;
private boolean ignoreFailureTs = false;
private String keyPath;
private boolean useDefConvert = true;
M3U8Option() {
super();
ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8);
}
/**
* 设置使用默认的码率转换器和TS转换器默认打开
* @param useDefConvert true 使用默认的转换器false 关闭默认的转换器
*/
public OP setUseDefConvert(boolean useDefConvert) {
this.useDefConvert = useDefConvert;
ALog.d(TAG, "使用默认的码率转换器和TS转换器如果无法下载请参考https://github.com/AriaLyy/Aria/issues/597 定制转换器");
return (OP) this;
}
/**
* 设置密钥文件的保存路径
*
@@ -76,7 +87,7 @@ public class M3U8Option<OP extends M3U8Option> extends BaseOption {
}
/**
* 是否合并ts文件默认合并ts
* 下载完成后,将所有的切片合并为一个文件
*
* @param mergeFile {@code true}合并所有ts文件为一个
*/

View File

@@ -15,7 +15,9 @@
*/
package com.arialyy.aria.core.download.target;
import android.net.Uri;
import android.text.TextUtils;
import com.arialyy.aria.core.common.AbsNormalTarget;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
@@ -60,7 +62,11 @@ class DNormalConfigHandler<TARGET extends AbsTarget> implements IConfigHandler {
mEntity = mWrapper.getEntity();
mTarget.setTaskWrapper(mWrapper);
if (mEntity != null) {
getWrapper().setTempFilePath(mEntity.getFilePath());
if (!TextUtils.isEmpty(mEntity.getFileUri())) {
getWrapper().setFileUri(Uri.parse(mEntity.getFileUri()));
} else if (!TextUtils.isEmpty(mEntity.getFilePath())) {
getWrapper().setTempFilePath(mEntity.getFilePath());
}
}
}
@@ -98,6 +104,7 @@ class DNormalConfigHandler<TARGET extends AbsTarget> implements IConfigHandler {
void setUrl(String url) {
mEntity.setUrl(url);
mWrapper.setTempUrl(url);
}
String getUrl() {
@@ -108,6 +115,14 @@ class DNormalConfigHandler<TARGET extends AbsTarget> implements IConfigHandler {
getWrapper().setTempFilePath(tempFilePath);
}
/**
* 设置分区存储场景下的下载目标 Uri如 SAF 文档、MediaStore 条目)。
* 与 {@link #setTempFilePath(String)} 互斥,设置 Uri 后文件路径将被清空。
*/
void setFileUri(Uri uri) {
getWrapper().setFileUri(uri);
}
private DTaskWrapper getWrapper() {
return mWrapper;
}

View File

@@ -20,8 +20,8 @@ import com.arialyy.aria.core.common.AbsNormalTarget;
import java.util.List;
/**
* @Author aria
* @Date 2019-09-05
* @author aria
* Date: 2019-09-05
*/
public class DTargetFactory {

View File

@@ -15,6 +15,8 @@
*/
package com.arialyy.aria.core.download.target;
import android.net.Uri;
import com.arialyy.aria.core.common.AbsBuilderTarget;
import com.arialyy.aria.core.common.FtpOption;
import com.arialyy.aria.core.common.SFtpOption;
@@ -75,6 +77,18 @@ public class FtpBuilderTarget extends AbsBuilderTarget<FtpBuilderTarget> {
return this;
}
/**
* 设置分区存储Scoped Storage场景下的下载目标 Uri。
* 与 {@link #setFilePath(String)} 互斥,设置 Uri 后文件路径将被清空。
* 注意:当前仅支持 FTP 普通下载任务。
*
* @param uri 可写的文档 Uri
*/
public FtpBuilderTarget setFileUri(Uri uri) {
mConfigHandler.setFileUri(uri);
return this;
}
/**
* 设置文件存储路径,如果需要修改新的文件名,修改路径便可。
* 如:原文件路径 /mnt/sdcard/test.zip

View File

@@ -17,6 +17,7 @@ package com.arialyy.aria.core.download.target;
import com.arialyy.aria.core.common.AbsBuilderTarget;
import com.arialyy.aria.core.common.HttpOption;
import com.arialyy.aria.core.download.AbsGroupTaskWrapper;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.manager.SubTaskManager;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
@@ -105,6 +106,14 @@ public class GroupBuilderTarget extends AbsBuilderTarget<GroupBuilderTarget> {
return this;
}
/**
* {@code true} 忽略任务冲突不考虑组任务hash冲突的情况
*/
public GroupBuilderTarget ignoreTaskOccupy() {
((AbsGroupTaskWrapper) getTaskWrapper()).setIgnoreTaskOccupy(true);
return this;
}
/**
* 设置任务组的文件夹路径在Aria中任务组的所有子任务都会下载到以任务组组名的文件夹中。
* 如groupDirPath = "/mnt/sdcard/download/group_test"

View File

@@ -17,6 +17,7 @@ package com.arialyy.aria.core.download.target;
import com.arialyy.aria.core.common.AbsNormalTarget;
import com.arialyy.aria.core.common.HttpOption;
import com.arialyy.aria.core.download.AbsGroupTaskWrapper;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.manager.SubTaskManager;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
@@ -73,6 +74,14 @@ public class GroupNormalTarget extends AbsNormalTarget<GroupNormalTarget> {
return mConfigHandler.updateUrls(urls);
}
/**
* {@code true} 忽略任务冲突不考虑组任务hash冲突的情况
*/
public GroupNormalTarget ignoreTaskOccupy() {
((AbsGroupTaskWrapper) getTaskWrapper()).setIgnoreTaskOccupy(true);
return this;
}
/**
* 更新任务组的文件夹路径在Aria中任务组的所有子任务都会下载到以任务组组名的文件夹中。
* 如groupDirPath = "/mnt/sdcard/download/group_test"

View File

@@ -15,6 +15,8 @@
*/
package com.arialyy.aria.core.download.target;
import android.net.Uri;
import com.arialyy.aria.core.common.AbsBuilderTarget;
import com.arialyy.aria.core.common.HttpOption;
import com.arialyy.aria.core.download.DTaskWrapper;
@@ -78,6 +80,24 @@ public class HttpBuilderTarget extends AbsBuilderTarget<HttpBuilderTarget> {
return this;
}
/**
* 设置分区存储Scoped Storage场景下的下载目标 Uri。
* 适用于 Android 10+ 无法直接访问外部存储路径的场景,例如:
* <ul>
* <li>通过 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} 或
* {@link android.content.Intent#ACTION_OPEN_DOCUMENT_TREE} 获取的用户授权 Uri</li>
* <li>通过 {@link android.provider.MediaStore} 插入后得到的 Uri</li>
* </ul>
* 与 {@link #setFilePath(String)} 互斥,设置 Uri 后文件路径将被清空。
* 注意:当前仅支持 HTTP 普通下载任务,不支持 M3U8 任务。
*
* @param uri 可写的文档 Uri
*/
public HttpBuilderTarget setFileUri(Uri uri) {
mConfigHandler.setFileUri(uri);
return this;
}
/**
* 设置文件存储路径,如果需要修改新的文件名,修改路径便可。
* 如:原文件路径 /mnt/sdcard/test.zip

View File

@@ -20,8 +20,8 @@ import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
/**
* @Author aria
* @Date 2019-09-06
* @author aria
* Date: 2019-09-06
*/
public class TcpBuilderTarget extends AbsBuilderTarget<TcpBuilderTarget> {

View File

@@ -21,8 +21,8 @@ import com.arialyy.aria.util.ALog;
import java.nio.charset.Charset;
/**
* @Author aria
* @Date 2019-09-06
* @author aria
* Date: 2019-09-06
*/
public class TcpDelegate extends BaseOption {

View File

@@ -18,8 +18,8 @@ package com.arialyy.aria.core.download.tcp;
/**
* tcp任务配置
*
* @Author aria
* @Date 2019-09-06
* @author aria
* Date: 2019-09-06
*/
public class TcpTaskConfig {

View File

@@ -16,31 +16,119 @@
package com.arialyy.aria.core.inf;
import android.app.Dialog;
import android.util.Log;
import android.widget.PopupWindow;
import com.arialyy.aria.core.WidgetLiftManager;
import com.arialyy.aria.core.queue.DGroupTaskQueue;
import com.arialyy.aria.core.queue.DTaskQueue;
import com.arialyy.aria.core.queue.UTaskQueue;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.arialyy.aria.util.CommonUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by AriaL on 2017/6/27.
* 接收器
*/
public abstract class AbsReceiver implements IReceiver {
protected String TAG = getClass().getSimpleName();
/**
* 观察者对象map
* key 由 {@link #getKey(IReceiver)}指定
* 观察者对象
*/
public static final Map<String, Object> OBJ_MAP = new ConcurrentHashMap<>();
public Object obj;
/**
* 观察者对象类的完整名称
*/
public String targetName;
private String targetName;
/**
* 当dialog、dialogFragment、popupwindow已经被用户使用了Dismiss事件或Cancel事件需要手动移除receiver
*/
public boolean needRmListener = false;
private boolean needRmReceiver = false;
private boolean isFragment = false;
public boolean isLocalOrAnonymousClass = false;
public AbsReceiver(Object obj) {
this.obj = obj;
initParams();
}
private void initParams() {
try {
targetName = CommonUtil.getTargetName(obj);
Class clazz = obj.getClass();
if (CommonUtil.isLocalOrAnonymousClass(clazz)) {
isLocalOrAnonymousClass = true;
String parentName = CommonUtil.getTargetName(obj);
Class parentClazz = Class.forName(parentName);
handleFragmentOrDialogParam(parentClazz, true);
return;
}
handleFragmentOrDialogParam(clazz, false);
} catch (Exception e) {
e.printStackTrace();
}
}
private void handleFragmentOrDialogParam(Class clazz, boolean isLocalOrAnonymousClass) {
final WidgetLiftManager widgetLiftManager = new WidgetLiftManager();
if (obj instanceof Dialog) {
needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj);
return;
}
if (obj instanceof PopupWindow) {
needRmReceiver = widgetLiftManager.handlePopupWindowLift((PopupWindow) obj);
return;
}
if (CommonUtil.isFragment(clazz)){
isFragment = true;
}
if (CommonUtil.isDialogFragment(clazz)) {
isFragment = true;
if (isLocalOrAnonymousClass) {
Log.e(TAG, String.format(
"%s 是匿名内部类无法获取到dialog对象为了防止内存泄漏请在dismiss方法中调用Aria.download(this).unRegister();来注销事件",
obj.getClass().getName()
));
return;
}
needRmReceiver = widgetLiftManager.handleDialogFragmentLift(getDialog(obj));
}
}
/**
* 获取DialogFragment的dialog
*
* @return 获取失败返回null
*/
private Dialog getDialog(Object obj) {
try {
Method method = obj.getClass().getMethod("getDialog");
return (Dialog) method.invoke(obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
protected boolean isNeedRmListener() {
return needRmReceiver;
}
@Override public boolean isFragment() {
return isFragment;
}
/**
* 创建观察者对象map的key生成规则
@@ -69,14 +157,7 @@ public abstract class AbsReceiver implements IReceiver {
* 移除观察者对象
*/
private void removeObj() {
for (Iterator<Map.Entry<String, Object>> iter = OBJ_MAP.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry<String, Object> entry = iter.next();
String key = entry.getKey();
if (key.equals(getKey())) {
iter.remove();
}
}
obj = null;
}
@Override public void destroy() {

View File

@@ -49,5 +49,11 @@ public interface IReceiver {
*
* @return {@link ReceiverType}
*/
String getType();
ReceiverType getType();
/**
* 判断是否是fragment如果是fragment在activity销毁时需要将其从receiver中移除
*/
boolean isFragment();
}

View File

@@ -18,7 +18,15 @@ package com.arialyy.aria.core.inf;
/**
* {@link AbsReceiver}类型
*/
public interface ReceiverType {
String DOWNLOAD = "download";
String UPLOAD = "upload";
public enum ReceiverType {
DOWNLOAD(1, "download"),
UPLOAD(2, "upload");
String name;
int type;
ReceiverType(int type, String name) {
this.type = type;
this.name = name;
}
}

View File

@@ -15,12 +15,15 @@
*/
package com.arialyy.aria.core.manager;
import android.text.TextUtils;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import java.io.File;
/**
@@ -68,11 +71,17 @@ class DTaskWrapperFactory implements INormalTEFactory<DownloadEntity, DTaskWrapp
entity = new DownloadEntity();
return entity;
}
File file = new File(entity.getFilePath());
boolean isUri = !TextUtils.isEmpty(entity.getFileUri());
File file = null;
if (!isUri) {
file = new File(entity.getFilePath());
}
// 分区存储 Uri 任务以 Uri 字符串作为记录查询键
String recordKey = isUri ? entity.getFileUri() : entity.getFilePath();
if (!entity.isComplete()) {
TaskRecord record =
TaskRecord.findFirst(TaskRecord.class, "filePath=?", entity.getFilePath());
TaskRecord.findFirst(TaskRecord.class, "filePath=?", recordKey);
if (record == null) {
resetEntity(entity);
} else {
@@ -87,7 +96,7 @@ class DTaskWrapperFactory implements INormalTEFactory<DownloadEntity, DTaskWrapp
if (count == record.threadNum) {
resetEntity(entity);
}
} else if (!file.exists()
} else if (file != null && !file.exists()
&& record.taskType != ITaskWrapper.M3U8_VOD) { // 非分块文件需要判断文件是否存在
resetEntity(entity);
}

View File

@@ -28,7 +28,7 @@ import java.util.List;
* 子任务管理器
*/
public class SubTaskManager {
private String TAG = "SubTaskManager";
private final String TAG = getClass().getSimpleName();
private DGTaskWrapper mEntity;
public SubTaskManager(DGTaskWrapper entity) {

View File

@@ -17,6 +17,7 @@
package com.arialyy.aria.core.queue;
import android.text.TextUtils;
import android.util.Log;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.TaskSchedulerType;
@@ -113,6 +114,10 @@ public abstract class AbsTaskQueue<TASK extends AbsTask, TASK_WRAPPER extends Ab
ALog.w(TAG, "resume task fail, task is null");
return;
}
if (mExecutePool.taskExits(task.getKey())) {
ALog.w(TAG, String.format("task【%s】running", task.getKey()));
return;
}
if (mExecutePool.size() >= getMaxTaskNum()) {
task.getTaskWrapper().getEntity().setState(IEntity.STATE_WAIT);
mCachePool.putTaskToFirst(task);
@@ -229,6 +234,7 @@ public abstract class AbsTaskQueue<TASK extends AbsTask, TASK_WRAPPER extends Ab
if (task == null) {
task = mCachePool.getTask(key);
}
ALog.i(TAG, "获取任务key" + key);
return task;
}
@@ -259,6 +265,7 @@ public abstract class AbsTaskQueue<TASK extends AbsTask, TASK_WRAPPER extends Ab
ALog.w(TAG, String.format("任务【%s】执行中", task.getKey()));
return;
}
ALog.i(TAG, "添加任务key" + task.getKey());
mCachePool.removeTask(task);
mExecutePool.putTask(task);
task.getTaskWrapper().getEntity().setFailNum(0);

View File

@@ -13,20 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.exception;
package com.arialyy.aria.core.scheduler;
/**
* Created by lyy on 2017/1/18.
* Aria 文件异常
* Created by Aria.Lao on 2019/6/26.
* m3u8切片事件回调类
*/
public class FileException extends BaseException {
private static final String ARIA_FILE_EXCEPTION = "Aria File Exception:";
public class AptM3U8PeerTaskListener implements M3U8PeerTaskListener, ISchedulerListener{
public FileException(String tag, String detailMessage) {
super(tag, String.format("%s%s", ARIA_FILE_EXCEPTION, detailMessage));
@Override public void onPeerStart(final String m3u8Url, final String peerPath, final int peerIndex) {
}
public FileException(String tag, String message, Exception e){
super(tag, message, e);
@Override public void onPeerComplete(final String m3u8Url, final String peerPath, final int peerIndex) {
}
}
@Override public void onPeerFail(final String m3u8Url, final String peerPath, final int peerIndex) {
}
@Override public void setListener(Object obj) {
}
}

View File

@@ -24,12 +24,12 @@ import com.arialyy.aria.core.task.UploadTask;
* Created by Aria.Lao on 2017/6/7.
* 普通任务事件{@link DownloadTask}{@link UploadTask}{@link DownloadGroupTask}回调类
*/
public class NormalTaskListener<TASK extends ITask> implements ISchedulerListener {
public class AptNormalTaskListener<TASK extends ITask> implements NormalTaskListenerInterface<TASK>, ISchedulerListener {
/**
* 队列已经满了继续创建任务将会回调该方法
*/
public void onWait(TASK task) {
@Override public void onWait(TASK task) {
}
@@ -37,42 +37,42 @@ public class NormalTaskListener<TASK extends ITask> implements ISchedulerListene
* 预处理有时有些地址链接比较慢这时可以先在这个地方出来一些界面上的UI如按钮的状态
* 在这个回调中任务是获取不到文件大小下载速度等参数
*/
public void onPre(TASK task) {
@Override public void onPre(TASK task) {
}
/**
* 任务预加载完成
*/
public void onTaskPre(TASK task) {
@Override public void onTaskPre(TASK task) {
}
/**
* 任务恢复下载
*/
public void onTaskResume(TASK task) {
@Override public void onTaskResume(TASK task) {
}
/**
* 任务开始
*/
public void onTaskStart(TASK task) {
@Override public void onTaskStart(TASK task) {
}
/**
* 任务停止
*/
public void onTaskStop(TASK task) {
@Override public void onTaskStop(TASK task) {
}
/**
* 任务取消
*/
public void onTaskCancel(TASK task) {
@Override public void onTaskCancel(TASK task) {
}
@@ -88,25 +88,25 @@ public class NormalTaskListener<TASK extends ITask> implements ISchedulerListene
/**
* 任务失败
*/
public void onTaskFail(TASK task, Exception e) {
@Override public void onTaskFail(TASK task, Exception e) {
}
/**
* 任务完成
*/
public void onTaskComplete(TASK task) {
@Override public void onTaskComplete(TASK task) {
}
/**
* 任务执行中
*/
public void onTaskRunning(TASK task) {
@Override public void onTaskRunning(TASK task) {
}
public void onNoSupportBreakPoint(TASK task) {
@Override public void onNoSupportBreakPoint(TASK task) {
}

View File

@@ -0,0 +1,68 @@
/*
* 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.scheduler;
import com.arialyy.aria.core.common.AbsNormalEntity;
import com.arialyy.aria.core.task.ITask;
/**
* Created by Aria.Lao on 2019/6/26.
* 子任务事件回调类
*/
public class AptSubTaskListener<TASK extends ITask, SUB_ENTITY extends AbsNormalEntity>
implements SubTaskListener<TASK, SUB_ENTITY>, ISchedulerListener {
@Override public void onNoSupportBreakPoint(TASK task) {
}
@Override public void onSubTaskPre(TASK task, SUB_ENTITY subTask) {
}
@Override public void onSubTaskStart(TASK task, SUB_ENTITY subTask) {
}
@Override public void onSubTaskStop(TASK task, SUB_ENTITY subTask) {
}
@Override public void onSubTaskCancel(TASK task, SUB_ENTITY subTask) {
}
@Override public void onSubTaskComplete(TASK task, SUB_ENTITY subTask) {
}
@Deprecated
public void onSubTaskFail(TASK task, SUB_ENTITY subTask) {
}
@Override public void onSubTaskFail(TASK task, SUB_ENTITY subTask, Exception e) {
}
@Override public void onSubTaskRunning(TASK task, SUB_ENTITY subTask) {
}
@Override public void setListener(Object obj) {
}
}

View File

@@ -19,18 +19,11 @@ package com.arialyy.aria.core.scheduler;
* Created by Aria.Lao on 2019/6/26.
* m3u8切片事件回调类
*/
public class M3U8PeerTaskListener implements ISchedulerListener {
public interface M3U8PeerTaskListener extends TaskInternalListenerInterface {
public void onPeerStart(final String m3u8Url, final String peerPath, final int peerIndex) {
}
public void onPeerStart(final String m3u8Url, final String peerPath, final int peerIndex);
public void onPeerComplete(final String m3u8Url, final String peerPath, final int peerIndex) {
}
public void onPeerComplete(final String m3u8Url, final String peerPath, final int peerIndex);
public void onPeerFail(final String m3u8Url, final String peerPath, final int peerIndex) {
}
@Override public void setListener(Object obj) {
}
public void onPeerFail(final String m3u8Url, final String peerPath, final int peerIndex);
}

View File

@@ -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.scheduler;
import com.arialyy.aria.core.task.DownloadGroupTask;
import com.arialyy.aria.core.task.DownloadTask;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.task.UploadTask;
/**
* Created by Aria.Lao on 2017/6/7.
* 普通任务事件{@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask}回调类
*/
public interface NormalTaskListenerInterface<TASK extends ITask> extends
TaskInternalListenerInterface {
/**
* 队列已经满了,继续创建任务,将会回调该方法
*/
public void onWait(TASK task);
/**
* 预处理有时有些地址链接比较慢这时可以先在这个地方出来一些界面上的UI如按钮的状态。
* 在这个回调中,任务是获取不到文件大小,下载速度等参数
*/
public void onPre(TASK task);
/**
* 任务预加载完成
*/
public void onTaskPre(TASK task);
/**
* 任务恢复下载
*/
public void onTaskResume(TASK task);
/**
* 任务开始
*/
public void onTaskStart(TASK task);
/**
* 任务停止
*/
public void onTaskStop(TASK task);
/**
* 任务取消
*/
public void onTaskCancel(TASK task);
/**
* 任务失败
*/
public void onTaskFail(TASK task, Exception e);
/**
* 任务完成
*/
public void onTaskComplete(TASK task);
/**
* 任务执行中
*/
public void onTaskRunning(TASK task);
public void onNoSupportBreakPoint(TASK task);
}

View File

@@ -22,47 +22,22 @@ import com.arialyy.aria.core.task.ITask;
* Created by Aria.Lao on 2019/6/26.
* 子任务事件回调类
*/
public class SubTaskListener<TASK extends ITask, SUB_ENTITY extends AbsNormalEntity>
implements ISchedulerListener {
public interface SubTaskListener<TASK extends ITask, SUB_ENTITY extends AbsNormalEntity> extends
TaskInternalListenerInterface {
public void onNoSupportBreakPoint(TASK task) {
public void onNoSupportBreakPoint(TASK task);
}
public void onSubTaskPre(TASK task, SUB_ENTITY subTask);
public void onSubTaskPre(TASK task, SUB_ENTITY subTask) {
public void onSubTaskStart(TASK task, SUB_ENTITY subTask);
}
public void onSubTaskStop(TASK task, SUB_ENTITY subTask);
public void onSubTaskStart(TASK task, SUB_ENTITY subTask) {
public void onSubTaskCancel(TASK task, SUB_ENTITY subTask);
}
public void onSubTaskComplete(TASK task, SUB_ENTITY subTask);
public void onSubTaskStop(TASK task, SUB_ENTITY subTask) {
public void onSubTaskFail(TASK task, SUB_ENTITY subTask, Exception e);
}
public void onSubTaskCancel(TASK task, SUB_ENTITY subTask) {
}
public void onSubTaskComplete(TASK task, SUB_ENTITY subTask) {
}
@Deprecated
public void onSubTaskFail(TASK task, SUB_ENTITY subTask) {
}
public void onSubTaskFail(TASK task, SUB_ENTITY subTask, Exception e) {
}
public void onSubTaskRunning(TASK task, SUB_ENTITY subTask) {
}
@Override public void setListener(Object obj) {
}
public void onSubTaskRunning(TASK task, SUB_ENTITY subTask);
}

View File

@@ -0,0 +1,25 @@
/*
* 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.scheduler;
/**
* 直接实现监听器回调接口的基类,不对外部直接开放,仅作为内部监听器的父接口使用
*
* @author ChenFei(chenfei0928 @ gmail.com)
* Date: 2020-07-07 15:18
*/
public interface TaskInternalListenerInterface {
}

View File

@@ -53,7 +53,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
private static volatile TaskSchedulers INSTANCE;
private static FailureTaskHandler mFailureTaskHandler;
private Map<String, Map<TaskEnum, ISchedulerListener>> mObservers = new ConcurrentHashMap<>();
private Map<String, Map<TaskEnum, Object>> mObservers = new ConcurrentHashMap<>();
private AriaConfig mAriaConfig;
private TaskSchedulers() {
@@ -98,15 +98,19 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
*/
public void register(Object obj, TaskEnum taskEnum) {
String targetName = obj.getClass().getName();
Map<TaskEnum, ISchedulerListener> listeners = mObservers.get(getKey(obj));
Map<TaskEnum, Object> listeners = mObservers.get(getKey(obj));
if (listeners == null) {
listeners = new ConcurrentHashMap<>();
mObservers.put(getKey(obj), listeners);
}
String proxyClassName = targetName + taskEnum.proxySuffix;
if (!hasProxyListener(listeners, taskEnum)) {
if (obj instanceof TaskInternalListenerInterface) {
listeners.put(taskEnum, obj);
return;
}
String proxyClassName = targetName + taskEnum.proxySuffix;
ISchedulerListener listener = createListener(proxyClassName);
if (listener != null) {
listener.setListener(obj);
@@ -123,7 +127,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
* @param taskEnum 代理类类型
* @return true已注册代理类false没有注册代理类
*/
private boolean hasProxyListener(Map<TaskEnum, ISchedulerListener> listeners, TaskEnum taskEnum) {
private boolean hasProxyListener(Map<TaskEnum, Object> listeners, TaskEnum taskEnum) {
return !listeners.isEmpty() && listeners.get(taskEnum) != null;
}
@@ -136,9 +140,9 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
if (!mObservers.containsKey(getKey(obj))) {
return;
}
for (Iterator<Map.Entry<String, Map<TaskEnum, ISchedulerListener>>> iter =
for (Iterator<Map.Entry<String, Map<TaskEnum, Object>>> iter =
mObservers.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<String, Map<TaskEnum, ISchedulerListener>> entry = iter.next();
Map.Entry<String, Map<TaskEnum, Object>> entry = iter.next();
if (entry.getKey().equals(getKey(obj))) {
iter.remove();
@@ -201,7 +205,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
if (mObservers.size() > 0) {
Set<String> keys = mObservers.keySet();
for (String key : keys) {
Map<TaskEnum, ISchedulerListener> listeners = mObservers.get(key);
Map<TaskEnum, Object> listeners = mObservers.get(key);
if (listeners == null || listeners.isEmpty()) {
continue;
}
@@ -234,6 +238,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
boolean canSend = mAriaConfig.getAConfig().isUseBroadcast();
if (canSend) {
Intent intent = new Intent(ISchedulers.ARIA_TASK_INFO_ACTION);
intent.setPackage(mAriaConfig.getAPP().getPackageName());
intent.putExtras(data);
mAriaConfig.getAPP().sendBroadcast(intent);
}
@@ -249,7 +254,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
if (mObservers.size() > 0) {
Set<String> keys = mObservers.keySet();
for (String key : keys) {
Map<TaskEnum, ISchedulerListener> listeners = mObservers.get(key);
Map<TaskEnum, Object> listeners = mObservers.get(key);
if (listeners == null || listeners.isEmpty()) {
continue;
}
@@ -357,6 +362,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
boolean canSend = mAriaConfig.getAConfig().isUseBroadcast();
if (canSend) {
Intent intent = new Intent(ISchedulers.ARIA_TASK_INFO_ACTION);
intent.setPackage(mAriaConfig.getAPP().getPackageName());
Bundle b = new Bundle();
b.putInt(ISchedulers.TASK_TYPE, taskType);
b.putInt(ISchedulers.TASK_STATE, ISchedulers.FAIL);
@@ -367,18 +373,18 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
if (mObservers.size() > 0) {
Set<String> keys = mObservers.keySet();
for (String key : keys) {
Map<TaskEnum, ISchedulerListener> listeners = mObservers.get(key);
Map<TaskEnum, Object> listeners = mObservers.get(key);
if (listeners == null || listeners.isEmpty()) {
continue;
}
NormalTaskListener<TASK> listener = null;
NormalTaskListenerInterface<TASK> listener = null;
if (mObservers.get(key) != null) {
if (taskType == ITask.DOWNLOAD) {
listener = (NormalTaskListener<TASK>) listeners.get(TaskEnum.DOWNLOAD);
listener = (NormalTaskListenerInterface<TASK>) listeners.get(TaskEnum.DOWNLOAD);
} else if (taskType == ITask.DOWNLOAD_GROUP) {
listener = (NormalTaskListener<TASK>) listeners.get(TaskEnum.DOWNLOAD_GROUP);
listener = (NormalTaskListenerInterface<TASK>) listeners.get(TaskEnum.DOWNLOAD_GROUP);
} else if (taskType == ITask.DOWNLOAD_GROUP) {
listener = (NormalTaskListener<TASK>) listeners.get(TaskEnum.UPLOAD);
listener = (NormalTaskListenerInterface<TASK>) listeners.get(TaskEnum.UPLOAD);
}
}
if (listener != null) {
@@ -398,18 +404,18 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
if (mObservers.size() > 0) {
Set<String> keys = mObservers.keySet();
for (String key : keys) {
Map<TaskEnum, ISchedulerListener> listeners = mObservers.get(key);
Map<TaskEnum, Object> listeners = mObservers.get(key);
if (listeners == null || listeners.isEmpty()) {
continue;
}
NormalTaskListener<TASK> listener = null;
NormalTaskListenerInterface<TASK> listener = null;
if (mObservers.get(key) != null) {
if (task instanceof DownloadTask) {
listener = (NormalTaskListener<TASK>) listeners.get(TaskEnum.DOWNLOAD);
listener = (NormalTaskListenerInterface<TASK>) listeners.get(TaskEnum.DOWNLOAD);
} else if (task instanceof DownloadGroupTask) {
listener = (NormalTaskListener<TASK>) listeners.get(TaskEnum.DOWNLOAD_GROUP);
listener = (NormalTaskListenerInterface<TASK>) listeners.get(TaskEnum.DOWNLOAD_GROUP);
} else if (task instanceof UploadTask) {
listener = (NormalTaskListener<TASK>) listeners.get(TaskEnum.UPLOAD);
listener = (NormalTaskListenerInterface<TASK>) listeners.get(TaskEnum.UPLOAD);
}
}
if (listener != null) {
@@ -419,7 +425,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
}
}
private void normalTaskCallback(int state, TASK task, NormalTaskListener<TASK> listener) {
private void normalTaskCallback(int state, TASK task, NormalTaskListenerInterface<TASK> listener) {
if (listener != null) {
if (task == null && state != ISchedulers.CHECK_FAIL) {
ALog.e(TAG, "TASK 为null回调失败");
@@ -496,6 +502,7 @@ public class TaskSchedulers<TASK extends ITask> implements ISchedulers {
*/
private Intent createData(int taskState, int taskType, AbsEntity entity) {
Intent intent = new Intent(ISchedulers.ARIA_TASK_INFO_ACTION);
intent.setPackage(mAriaConfig.getAPP().getPackageName());
Bundle b = new Bundle();
b.putInt(ISchedulers.TASK_TYPE, taskType);
b.putInt(ISchedulers.TASK_STATE, taskState);

View File

@@ -40,7 +40,7 @@ public class CheckUEntityUtil implements ICheckEntityUtil {
@Override
public boolean checkEntity() {
if (mWrapper.getErrorEvent() != null) {
ALog.e(TAG, String.format("上传失败,%s", mWrapper.getErrorEvent().errorMsg));
ALog.e(TAG, String.format("任务操作失败,%s", mWrapper.getErrorEvent().errorMsg));
return false;
}
@@ -61,8 +61,9 @@ public class CheckUEntityUtil implements ICheckEntityUtil {
return false;
}
// 任务是新任务,并且路径冲突就不会继续执行
if (mWrapper.isNewTask() && !CheckUtil.checkUPathConflicts(
mWrapper.isIgnoreFilePathOccupy(), filePath)) {
if (mWrapper.isNewTask()
&& !CheckUtil.checkUPathConflicts(mWrapper.isIgnoreFilePathOccupy(), filePath,
mWrapper.getRequestType())) {
return false;
}

View File

@@ -29,6 +29,7 @@ import com.arialyy.aria.core.event.EventMsgUtil;
import com.arialyy.aria.core.inf.AbsReceiver;
import com.arialyy.aria.core.inf.ReceiverType;
import com.arialyy.aria.core.queue.UTaskQueue;
import com.arialyy.aria.core.scheduler.TaskInternalListenerInterface;
import com.arialyy.aria.core.scheduler.TaskSchedulers;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.upload.target.FtpBuilderTarget;
@@ -48,7 +49,9 @@ import java.util.Set;
* 上传功能接收器
*/
public class UploadReceiver extends AbsReceiver {
private static final String TAG = "UploadReceiver";
public UploadReceiver(Object obj) {
super(obj);
}
/**
* 设置最大上传速度单位kb
@@ -264,11 +267,18 @@ public class UploadReceiver extends AbsReceiver {
* 将当前类注册到Aria
*/
public void register() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) {
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
return;
}
if (obj instanceof TaskInternalListenerInterface){
ProxyHelper.getInstance().checkProxyType(obj.getClass());
if (obj instanceof UploadTaskListener){
TaskSchedulers.getInstance().register(obj, TaskEnum.UPLOAD);
}
return;
}
Set<Integer> set = ProxyHelper.getInstance().checkProxyType(obj.getClass());
if (set != null && !set.isEmpty()) {
for (Integer type : set) {
@@ -286,18 +296,17 @@ public class UploadReceiver extends AbsReceiver {
* 如果是Dialog或popupwindow需要你在撤销界面时调用该方法
*/
@Override public void unRegister() {
if (needRmListener) {
if (isNeedRmListener()) {
unRegisterListener();
}
AriaManager.getInstance().removeReceiver(OBJ_MAP.get(getKey()));
AriaManager.getInstance().removeReceiver(obj);
}
@Override public String getType() {
@Override public ReceiverType getType() {
return ReceiverType.UPLOAD;
}
@Override protected void unRegisterListener() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) {
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
return;

View File

@@ -0,0 +1,28 @@
/*
* 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.upload;
import com.arialyy.aria.core.scheduler.NormalTaskListenerInterface;
import com.arialyy.aria.core.task.UploadTask;
/**
* 上传任务接口
*
* @author ChenFei(chenfei0928 @ gmail.com)
* Date: 2020-07-07 13:23
*/
public interface UploadTaskListener extends NormalTaskListenerInterface<UploadTask> {
}

View File

@@ -19,8 +19,8 @@ import com.arialyy.aria.core.common.AbsBuilderTarget;
import com.arialyy.aria.core.common.AbsNormalTarget;
/**
* @Author aria
* @Date 2019-09-05
* @author aria
* Date: 2019-09-05
*/
public class UTargetFactory {

View File

@@ -1,14 +1,19 @@
apply plugin: 'java'
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.encoding = "UTF-8"
}
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
apply from: 'bintray-release.gradle'
//apply from: 'bintray-release.gradle'
ext {
PUBLISH_ARTIFACT_ID = 'annotations'
}
apply from: '../gradle/mavenCentral-release.gradle'

View File

@@ -21,15 +21,15 @@ package com.arialyy.annotations;
*/
public enum TaskEnum {
DOWNLOAD("com.arialyy.aria.core.task", "DownloadTask", "$$DownloadListenerProxy",
"NormalTaskListener"),
"AptNormalTaskListener"),
DOWNLOAD_GROUP("com.arialyy.aria.core.task", "DownloadGroupTask",
"$$DownloadGroupListenerProxy", "NormalTaskListener"),
"$$DownloadGroupListenerProxy", "AptNormalTaskListener"),
DOWNLOAD_GROUP_SUB("com.arialyy.aria.core.task", "DownloadGroupTask",
"$$DGSubListenerProxy", "SubTaskListener"),
"$$DGSubListenerProxy", "AptSubTaskListener"),
UPLOAD("com.arialyy.aria.core.task", "UploadTask", "$$UploadListenerProxy",
"NormalTaskListener"),
"AptNormalTaskListener"),
M3U8_PEER("com.arialyy.aria.core.task", "DownloadTask", "$$M3U8PeerListenerProxy",
"M3U8PeerTaskListener");
"AptM3U8PeerTaskListener");
public String pkg, className, proxySuffix, proxySuperClass;

View File

@@ -1,18 +1,22 @@
apply plugin: 'java'
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.encoding = "UTF-8"
}
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.auto:auto-common:0.10'
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation 'com.squareup:javapoet:1.9.0'
implementation project(':AriaAnnotations')
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.auto:auto-common:1.2.2'
implementation 'com.google.auto.service:auto-service:1.1.1'
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
implementation 'com.squareup:javapoet:1.13.0'
implementation project(':AriaAnnotations')
}
apply from: 'bintray-release.gradle'
//apply from: 'bintray-release.gradle'
ext {
PUBLISH_ARTIFACT_ID = 'compiler'
}
apply from: '../gradle/mavenCentral-release.gradle'

View File

@@ -1,4 +1,63 @@
## 开发日志
+ v_3.8.16 (2021/4/18)
- 修复单线程下载时,文件已经完成,重复下载回调下载失败的问题
- 修复一个重新下载文件时,同名路径文件没有被被删除的问题
- fix bug https://github.com/AriaLyy/Aria/issues/807
- fix bug https://github.com/AriaLyy/Aria/issues/811
- fix bug https://github.com/AriaLyy/Aria/issues/851
- 修复组合任务在获取子任务信息的过程中,手动停止或删除,没有回调的问题
+ v_3.8.15 (2020/11/9)
- 修复不支持断点的连接下载失败问题https://github.com/AriaLyy/Aria/issues/771
- 修复iv不存在时索引文件异常的问题https://github.com/AriaLyy/Aria/issues/780
- fix bug https://github.com/AriaLyy/Aria/issues/799
- fix bug https://github.com/AriaLyy/Aria/issues/798
+ v_3.8.14 (2020/9/23)
- 修复spi机制的兼容问题https://github.com/AriaLyy/Aria/issues/743
- 增加路径不可以写的判断,下载时,如果路径不可写,将执行失败回调 https://github.com/AriaLyy/Aria/issues/750
+ v_3.8.12 (2020/8/15)
- 修复一个正则表达式导致的文件名保存号异常问题 https://github.com/AriaLyy/Aria/issues/715
- 修复一个匿名内部类中的内存溢出的问题 https://github.com/AriaLyy/Aria/issues/705
- 修复同一个url地址的任务提示路径冲突的问题
- 修复m3u8任务地址错误时无法删除实体的问题 https://github.com/AriaLyy/Aria/issues/712
- 修复m3u8gzip的问题https://github.com/AriaLyy/Aria/issues/639
- 修复http表单上传本地md5和上传的服务的的文件md5不一致的问题 https://github.com/AriaLyy/Aria/issues/730
- 修复0kb的文件不可下载的问题 https://github.com/AriaLyy/Aria/issues/711
- 修复加密的m3u8下载并且使用索引模式时key的uri没有使用双引号的问题 https://github.com/AriaLyy/Aria/issues/731
- 修复删除m3u8索引文件后下载完成回调无法触发的问题
- 修复删除加密的m3u8文件时key没有被删除的问题 https://github.com/AriaLyy/Aria/issues/735#issuecomment-673958845
- 增加m3u8密钥下载地址转换器增加ts列表的url地址 https://github.com/AriaLyy/Aria/issues/718
- 增加现在http文件下载将使用HEAD请求获取文件大小配置文件增加 <useHeadRequest value="true"/>。慎用并不是所有服务器都支持head请求
- 增加允许不使用apt直接通过实现监听器来回调下载进度更新该功能由[chenfei0928](https://github.com/chenfei0928)提交感谢他的pr。如果注解不生效只需要实现`DownloadListener`接口便可
- 增加m3u8使用`ignoreFailureTs`后将不会自动重试失败的切片 https://github.com/AriaLyy/Aria/issues/662
+ v_3.8.10 (2020/6/26)
- fix bug https://github.com/AriaLyy/Aria/issues/703
- fix bug https://github.com/AriaLyy/Aria/issues/702
- fix bug https://github.com/AriaLyy/Aria/issues/695
+ v_3.8.9 (2020/6/14)
- fix bug https://github.com/AriaLyy/Aria/issues/688
- fix bug https://github.com/AriaLyy/Aria/issues/690
- m3u8任务增加`setUseDefConvert()`方法用于处理默认的m3u8任务
+ v_3.8.8 (2020/6/7)
- 修复设置了cancel(false),文件还是被删除的问题 https://github.com/AriaLyy/Aria/issues/686
- 修复错误url的下载任务无法删除的问题 https://github.com/AriaLyy/Aria/issues/684
+ v_3.8.7 (2020/5/25)
- 修复组合任务单个子任务失败后,重新恢复组合任务,组合任务状态变为完成的问题
- 修复40x错误会继续重试并且无法重试成功的问题 https://github.com/AriaLyy/Aria/issues/619
- 修复wait模式下resume(true)无效问题
- 修复now模式下的一些问题 https://github.com/AriaLyy/Aria/issues/620
- 修复组任务,其中一个子任务在获取文件长度失败后,重新恢复组合任务,组合任务状态变为完成的问题 https://github.com/AriaLyy/Aria/issues/628
- 修复组任务中其中一个子任务是30x地址导致调度器无法出现该子任务状态的问题
- 增加组任务groupHash冲突检查 https://github.com/AriaLyy/Aria/issues/635
- 修复task.cancel(false)还是把本地文件删除的问题 https://github.com/AriaLyy/Aria/issues/646
- fix bug https://github.com/AriaLyy/Aria/issues/670
- fix bug https://github.com/AriaLyy/Aria/issues/664
+ v_3.8.6 (2020/2/17)
- fix bug https://github.com/AriaLyy/Aria/issues/608
- fix bug https://github.com/AriaLyy/Aria/issues/579#issuecomment-586665035
- fix bug https://github.com/AriaLyy/Aria/issues/610
- fix bug https://github.com/AriaLyy/Aria/issues/614
- 增加文件名适配器(感谢小伙伴[DaveBoy](https://github.com/DaveBoy)的PR
- 优化异常提示
+ v_3.8.5 (2020/1/18)
- fix bug https://github.com/AriaLyy/Aria/issues/599
- 增加密钥url转换器的参数 https://github.com/AriaLyy/Aria/issues/603

View File

@@ -1,292 +1,173 @@
# Aria
![图标](https://github.com/AriaLyy/DownloadUtil/blob/master/app/src/main/res/mipmap-hdpi/ic_launcher.png)</br>
![图标](https://github.com/AriaLyy/DownloadUtil/blob/master/img/ic_launcher.png)</br>
## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)</br>
## [中文文档](https://aria.laoyuyu.me/aria_doc)
The Aria project originated from the need for file download management encountered in work. At that time, the pain of being downloaded was unhappy. Since then, a simple and easy-to-use, stable and efficient download framework has emerged. Aria has experienced 1.0 to 3.0 development. , It is getting closer and closer to the goal set at the beginning.
## [中文文档](https://github.com/AriaLyy/Aria/blob/master/CHINESE_README.md)
Aria project is from the moment taht the work encountered in a file download management needs adn i was tortured at the time of the pain.</br>
Since then i have a idea which is to program a simple and easy to use,stable and efficient download framework.
Aria experienced 1.0 to 3.0 development, be more and more close to the original set by the target.
Aria has the following characteristics
+ simple and convenient
- can be used in Activity, Service, Fragment, Dialog, popupWindow, Notification and other components
- support the task of automatic scheduling, the user does not need to care about the state of the task switch logic
- [Through the Aria event, it is easy to get the download status of the current download task](#status)
- [a code plus can get the current download speed](#interface)
- [a code can be dynamically set the maximum number of downloads](#parameters)
- [code to achieve speed limit](#interface)
- [It is easy to modify the number of download threads by modifying the configuration file](https://github.com/AriaLyy/Aria/blob/master/app/src/main/assets/aria_config.xml)
- [priority to download a task](#interface)
Aria has the following characteristics:
+ Simple and convenient
- Can be used in Activity, Service, Fragment, Dialog, popupWindow, Notification and other components
- Support HTTP\FTP resumable download, multi-task automatic scheduling
- Support multi-file package download, multi-file sharing the same progress (eg: video + cover + subtitles)
- Support downloading FTP folder
- Support HTTP form upload
- Support file FTP resumable upload
- Support FTPS resumable upload, [see](https://aria.laoyuyu.me/aria_doc/api/ftp_params.html#%E4%BA%8C%E3%80%81ftps)
- Support SFTP resumable upload, [sftp download](https://aria.laoyuyu.me/aria_doc/download/sftp_normal.html), [sftp upload](https://aria.laoyuyu.me/aria_doc/upload /sftp_normal.html)
+ Support https address download
- It is easy to set the CA certificate information in the configuration file
+ Support 300,301,302 redirect download link download
+ Support upload operation
+ Support [Multi-threaded download in blocks](https://aria.laoyuyu.me/aria_doc/start/config.html), which can more effectively play the machine IO performance
+ Support 300, 301, 302 redirect download link download
+ Support file download of m3u8 and hls protocol [m3u8 download](https://aria.laoyuyu.me/aria_doc/download/m3u8.html)
+ Support m3u8 download support, [click to view details](https://aria.laoyuyu.me/aria_doc/download/m3u8_vod.html)
+ The download support file length increases dynamically, and the file download initialization will no longer occupy too much memory space, see [Dynamic Length Configuration](https://aria.laoyuyu.me/aria_doc/start/config.html#%E4% B8%8B%E8%BD%BD%E5%8A%A8%E6%80%81%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E)
How do we to use Aria?
* [download](#Using)
* [upload](#Upload) s
[How to use Aria?] (#use)
If you think Aria is helpful to you, your star and issues will be my greatest support. Of course, you are also very welcome to PR, [PR Method](https://www.zhihu.com/question/21682976/answer /79489643)`^_^`
## Example
* Multitask download
![Multitask download](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif)
* Speed limit
![网速下载限制](https://github.com/AriaLyy/DownloadUtil/blob/master/img/max_speed.gif)
* Multi-file package download
<img src="https://github.com/AriaLyy/DownloadUtil/blob/master/img/group_task.gif" width="360" height="640"/>
* m3u8 download
![m3u8点播文件边下边看](https://github.com/AriaLyy/Aria/blob/master/img/m3u8VodDownload.gif)
## Lib
[![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE)
[![Core](https://img.shields.io/badge/Core-3.8.10-blue)](https://github.com/AriaLyy/Aria)
[![Compiler](https://img.shields.io/badge/Compiler-3.8.10-blue)](https://github.com/AriaLyy/Aria)
[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.8.10-orange)](https://github.com/AriaLyy/Aria)
[![FtpComponent](https://img.shields.io/badge/SFtpComponent-3.8.10-orange)](https://github.com/AriaLyy/Aria)
[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.8.10-orange)](https://github.com/AriaLyy/Aria)
If you feel that Aria is helpful to you, your star and issues will be the greatest support for me.`^_^`
## Download
[![Download](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion)
[![Download](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion)
```java
compile 'com.arialyy.aria:aria-core:3.2.0'
annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.0'
implementation 'com.arialyy.aria:core:3.8.10'
annotationProcessor 'com.arialyy.aria:compiler:3.8.10'
implementation 'com.arialyy.aria:ftpComponent:3.8.10' # If you need to use ftp, please add this component
implementation 'com.arialyy.aria:sftpComponent:3.8.10' # If you need to use sftp, please add this component
implementation 'com.arialyy.aria:m3u8Component:3.8.10' # If you need to use the m3u8 download function, please add this component
```
## For example
![Multi-task download](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif)
![download speed limit](https://github.com/AriaLyy/DownloadUtil/blob/master/img/max_speed.gif)
If you are using Kotlin, please use the official method provided by Kotlin to configure apt, [kotlin kapt official configuration portal](https://www.kotlincn.net/docs/reference/kapt.html)
## performance
![Performance display](https://github.com/AriaLyy/DownloadUtil/blob/master/img/performance.png)
__Note: When the version below 3.5.4 is upgraded, the [configuration file] needs to be updated(https://aria.laoyuyu.me/aria_doc/start/config.html)__
__Note: Version 3.8 and above have been adapted to AndroidX and support libraries can be used
***
## Using
Since Aria involves the operation of files and networks, you need to add the following permissions to the manifest file.
## Use
Because Aria involves file and network operations, you need to add the following permissions to the manifest file. If you want to use Aria in a system above 6.0, you need to dynamically apply for file system read and write permissions to the Android system, [How to use Android system permissions](https://developer.android.com/training/permissions/index.html?hl=zh-cn)
```xml
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
```
## Use Aria to download
* Add a task (do not download), when other download tasks are completed, will automatically download the waiting task
## Use Aria
### Basic use
The example is a single-task download, only a very simple code is needed to realize the download function.
* Create task
```java
long taskId = Aria.download(this)
.load(DOWNLOAD_URL) //Read download address
.setFilePath(DOWNLOAD_PATH) //Set the full path where the file is saved
.create(); //Create and start download
```
* Stop\Resume task
```java
Aria.download(this)
.load(DOWNLOAD_URL)
.setDownloadPath(DOWNLOAD_PATH) //file save path
.add();
.load(taskId) //task id
.stop(); // stop task
//.resume(); // resume task
```
* download
```java
Aria.download(this)
.load(DOWNLOAD_URL) //load download url
.setDownloadPath(DOWNLOAD_PATH) //file save path
.start(); //start download
```
* Pause
```java
Aria.download(this).load(DOWNLOAD_URL).pause();
```
* Resume download
```java
Aria.download(this).load(DOWNLOAD_URL).resume();
```
* Cancel download
```java
Aria.download(this).load(DOWNLOAD_URL).cancel();
```
### status
If you want to read the download progress or download the information, then you need to create an event class and register the event class into the Aria manager in the onResume (Activity, Fragment) or constructor (Dialog, PopupWindow).
### Obtaining task status
Based on the consideration of decoupling, the download function of Aria is separated from the state acquisition, and the state acquisition will not be integrated into the chain code, but Aria provides another simpler and more flexible solution.
Through annotations, you can easily get all the status of the task.
1. Register the object to Aria
`Aria.download(this).register();` or `Aria.upload(this).register();`
```java
@Override
protected void onCreate(Bundle savedInstanceState) {
```java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Aria.download(this).register();
}
```
2. Use`@Download` or `@Upload` to annotate your function<br>
**note**
- Annotation is done by using `Apt`, so you do not need to worry that this will affect your machine's performance
- The annotated method**can not be modified by private**
- The annotated method**can have only one argument, and the parameter type must be either`DownloadTask` or `UploadTask`**
- Method name can be any string
3. In addition to using annotation methods in widget (Activity, Fragment, Dialog, Popupwindow), you can also use annotation functions in components such as Service, Notification, and so on.
```java
@Download.onPre(DOWNLOAD_URL)
protected void onPre(DownloadTask task) {}
@Download.onTaskStart
void taskStart(DownloadTask task) {}
@Download.onTaskRunning
protected void running(DownloadTask task) {}
@Download.onTaskResume
void taskResume(DownloadTask task) {}
@Download.onTaskStop
void taskStop(DownloadTask task) {}
@Download.onTaskCancel
void taskCancel(DownloadTask task) {}
@Download.onTaskFail
void taskFail(DownloadTask task) {}
@Download.onTaskComplete
void taskComplete(DownloadTask task) {}
@Download.onNoSupportBreakPoint
public void onNoSupportBreakPoint(DownloadTask task) {}
```
4. If you want to set up a listener for a single task, or for some specific task.<br>
**Adding a download address for a task in an annotation means that only the task triggers the annotated method.**
```java
@Download.onTaskRunning({
"https://test.xx.apk",
"http://test.xx2.apk"
}) void taskRunning(DownloadTask task) {
mAdapter.setProgress(task.getDownloadEntity());
}
```
In the above exampleonly the download address is`https://test.xx.apk` and `http://test.xx2.apk`will trigger the`taskRunning(DownloadTask task)`method。
### parameters
#### [Configuration file setting parameters](https://github.com/AriaLyy/Aria/blob/master/app/src/main/assets/aria_config.xml)
#### Set the parameters in the code
In addition to the file mode to modify the Aria parameter, the same, you can also modify the code in the Aria parameters</br>
Get the configuration file directly through`Aria.get(this).getDownloadConfig()`or`Aria.get(this).getUploadConfig()`</br>
and then modify the parameters
```java
// 修改最大下载数,调用完成后,立即生效
// 如当前下载任务数是4修改完成后当前任务数会被Aria自动调度任务数
Aria.get(this).getDownloadConfig().setMaxTaskNum(3);
```
### interface
* Stop all tasks
```java
Aria.download(this).stopAllTask();
```
* Restore all stopped tasks
```java
Aria.download(this).resumeAllTask();
```
* Delete all tasks
```java
Aria.download(this).removeAllTask();
```
* Maximum download speed limit
```java
//单位为 kb
Aria.download(this).setMaxSpeed(speed);
```
* Get download speed for current tasks<br>
Speed parameters a bit specialneed to [download the event support](#status)
``` java
@Override public void onTaskRunning(DownloadTask task) {
//If you turn on the speed unit conversion configuration, you can get the download speed with units in the following ways, such as: 1 mb/s
String convertSpeed = task.getConvertSpeed();
//If you have your own unit format, you can get the original byte length by the following method
long speed = task.getSpeed();
}
```
* Get the downloaded file size, the current progress percentage</br>
Likewise, you can also get the downloaded file size in the DownloadTask object
```
@Override public void onTaskRunning(DownloadTask task) {
 //Get the file size
long fileSize = task.getFileSize();
//Get the file size after conversion
String fileSize1 = task.getConvertFileSize();
//The current percentage of progress
int percent = task.getPercent();
2. Obtain task execution status through annotations
**Note: **
- Annotation back is implemented using Apt, so you dont need to worry about this affecting the performance of your machine
- Annotated methods **cannot be modified by private**
- The annotated method ** can only have one parameter, and the parameter type must be `DownloadTask` or `UploadTask` or `DownloadGroupTask`**
- Method name can be any string
```java
//Process the status of the task execution here, such as the refresh of the progress bar
@Download.onTaskRunning protected void running(DownloadTask task) {
if(task.getKey().eques(url)){
....
You can judge whether it is the callback of the specified task by url
}
int p = task.getPercent(); //Task progress percentage
String speed = task.getConvertSpeed(); //Download speed after unit conversion, unit conversion needs to be opened in the configuration file
String speed1 = task.getSpeed(); //Original byte length speed
}
@Download.onTaskComplete void taskComplete(DownloadTask task) {
//Process the status of task completion here
}
```
* Set the high priority task<br>
If you want to give priority to download a task, you can
``` java
Aria.download(this).load(DOWNLOAD_URL).setDownloadPath(PATH).setHighestPriority();
```
### [Document address](https://aria.laoyuyu.me/aria_doc/)
* Set the extension field<br>
Sometimes, you may want to store some of your own data when you download it</br>
**TIP**: If you have more data, or the data is more complex, you can first convert the data to JSON, and then save it to Aria's download entity
```java
Aria.download(this).load(DOWNLOAD_URL).setExtendField(str)
```
## Upload
### Version Log
+ v_3.8.10 (2020/6/26)
- fix bug https://github.com/AriaLyy/Aria/issues/703
- fix bug https://github.com/AriaLyy/Aria/issues/702
- fix bug https://github.com/AriaLyy/Aria/issues/695
* Add a task (add only, do not upload)
[More Version Log](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md)
```java
Aria.upload(this)
.load(filePath) //file path
.setUploadUrl(uploadUrl) // upload the path
.setAttachment(fileKey) //The server reads the file's key
.add();
```
* Upload
```java
Aria.upload(this)
.load(filePath) //file path
.setUploadUrl(uploadUrl) //upload the path
.setAttachment(fileKey) //The server reads the file's key
.start();
```
* cancel upload
```java
Aria.upload(this).load(filePath).cancel();
```
## Confused configuration
## Confusion configuration
```
-dontwarn com.arialyy.aria.**
-keep class com.arialyy.aria.**{*;}
-keep class **$$DownloadListenerProxy{ *; }
-keep class **$$UploadListenerProxy{ *; }
-keep class **$$DownloadGroupListenerProxy{ *; }
-keep class **$$DGSubListenerProxy{ *; }
-keepclasseswithmembernames class * {
@Download.* <methods>;
@Upload.* <methods>;
@DownloadGroup.* <methods>;
}
```
## others
Have any questions that can give me a message in the[issues](https://github.com/AriaLyy/Aria/issues)
## Other
If you have any questions, you can leave me a feedback at [issues](https://github.com/AriaLyy/Aria/issues). </br>
Before submitting an issue, I hope you have checked [wiki](https://aria.laoyuyu.me/aria_doc/) or searched [issues](https://github.com/AriaLyy/Aria/issues). </br>
## Donate
https://paypal.me/arialyy
***
## 后续版本开发规划
* ~~http、scoket断点上传~~
* ~~实现上传队列调度功能~~
## Development log
+ v_3.1.9 Repair the stopAll queue without task when the problem of collapse, increase the function for a single task monitor
+ v_3.1.7 repair some files can not download the bug, increase the apt annotation method, the incident is more simple
+ v_3.1.6 When the task is canceled ontaskCancel callback twice
+ v_3.1.5 Optimize the code structure, increase the priority download task function.
+ v_3.1.4 Repair the fast switching, pause, and restore functions, the probability of re-download problems, add onPre () callback, onPre () used to request the interface before the implementation of interface UI update operation.
+ v_3.1.0 Add the Aria configuration file to optimize the code
+ v_3.0.3 Repair the pause after deleting the task, flashing the problem, add the api to delete the record
+ v_3.0.2 supports 30x redirect link download
+ v_3.0.0 add upload task support to fix some bugs that have been discovered
License
-------

View File

@@ -1,31 +1,50 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
namespace "com.example.ariaftpcomponent"
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':PublicComponent')
implementation project(path: ':PublicComponent')
}
apply from: 'bintray-release.gradle'
//apply from: 'bintray-release.gradle'
ext {
PUBLISH_ARTIFACT_ID = 'ftp'
}
apply from: '../gradle/mavenCentral-release.gradle'

View File

@@ -27,12 +27,12 @@ import aria.apache.commons.net.ftp.FTPSClient;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.FtpConnectionMode;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.loader.ILoaderVisitor;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.exception.AriaFTPException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
@@ -56,7 +56,8 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
private int mConnectTimeOut;
protected long mSize = 0;
protected String charSet = "UTF-8";
protected Callback callback;
private Callback callback;
private boolean isStop = false, isCancel = false;
public AbsFtpInfoTask(TASK_WRAPPER taskWrapper) {
mTaskWrapper = taskWrapper;
@@ -82,6 +83,14 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
protected abstract void handelFileInfo(FTPClient client, FTPFile[] files,
String convertedRemotePath) throws IOException;
@Override public void stop() {
isStop = true;
}
@Override public void cancel() {
isCancel = true;
}
@Override public void setCallback(Callback callback) {
this.callback = callback;
}
@@ -102,10 +111,10 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
handelFileInfo(client, files, convertedRemotePath);
} catch (IOException e) {
e.printStackTrace();
failDownload(client, "FTP错误信息", e, true);
handleFail(client, "FTP错误信息", e, true);
} catch (InterruptedException e) {
e.printStackTrace();
failDownload(client, "FTP错误信息", e, true);
handleFail(client, "FTP错误信息", e, true);
} finally {
closeClient(client);
}
@@ -147,7 +156,7 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
}
if (client == null) {
failDownload(client, String.format("链接失败, url: %s", mTaskOption.getUrlEntity().url), null,
handleFail(client, String.format("链接失败, url: %s", mTaskOption.getUrlEntity().url), null,
true);
return null;
}
@@ -161,22 +170,22 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
loginSuccess = client.login(urlEntity.user, urlEntity.password, urlEntity.account);
}
} catch (IOException e) {
ALog.e(TAG,
new TaskException(TAG, String.format("登录失败,错误码为:%s msg%s", client.getReplyCode(),
client.getReplyString()), e));
e.printStackTrace();
ALog.e(TAG, String.format("登录失败,错误码为:%s msg%s", client.getReplyCode(),
client.getReplyString()));
return null;
}
}
if (!loginSuccess) {
failDownload(client, "登录失败", null, false);
handleFail(client, "登录失败", null, false);
client.disconnect();
return null;
}
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
failDownload(client, String.format("无法连接到ftp服务器filePath: %s, url: %s", mEntity.getKey(),
handleFail(client, String.format("无法连接到ftp服务器filePath: %s, url: %s", mEntity.getKey(),
mTaskOption.getUrlEntity().url), null, true);
client.disconnect();
return null;
@@ -300,7 +309,7 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
for (FTPFile file : files) {
if (file.isFile()) {
size += file.getSize();
handleFile(path + file.getName(), file);
handleFile(client, path + file.getName(), file);
} else {
String remotePath = CommonUtil.convertFtpChar(charSet, path + file.getName());
size += getFileSize(client.listFiles(remotePath), client, path + file.getName());
@@ -315,10 +324,13 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
* @param remotePath ftp服务器文件夹路径
* @param ftpFile ftp服务器上对应的文件
*/
protected void handleFile(String remotePath, FTPFile ftpFile) {
protected void handleFile(FTPClient client, String remotePath, FTPFile ftpFile) {
}
protected void failDownload(FTPClient client, String msg, Exception e, boolean needRetry) {
protected void handleFail(FTPClient client, String msg, Exception e, boolean needRetry) {
if (isStop || isCancel) {
return;
}
if (callback != null) {
if (client == null) {
msg = "创建ftp客户端失败";
@@ -329,10 +341,14 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
needRetry = needRetry && !CheckUtil.ftpIsBadRequest(client.getReplyCode());
}
callback.onFail(mEntity, new AriaIOException(TAG, msg), needRetry);
callback.onFail(mEntity, new AriaFTPException(msg), needRetry);
}
}
protected void onSucceed(CompleteInfo info){
callback.onSucceed(mEntity.getKey(), info);
}
@Override public void accept(ILoaderVisitor visitor) {
visitor.addComponent(this);
}

View File

@@ -25,7 +25,7 @@ import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.FtpConnectionMode;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.AriaFTPException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.SSLContextUtil;
import java.io.IOException;
@@ -34,8 +34,8 @@ import java.net.UnknownHostException;
import javax.net.ssl.SSLContext;
/**
* @Author lyy
* @Date 2019-09-18
* @author lyy
* Date: 2019-09-18
*/
public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter {
@@ -105,7 +105,7 @@ public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter {
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
client.disconnect();
fail(new AriaIOException(TAG,
fail(new AriaFTPException(
String.format("无法连接到ftp服务器错误码为%smsg:%s", reply, client.getReplyString())), false);
return null;
}

View File

@@ -21,7 +21,7 @@ import aria.apache.commons.net.ftp.FTPReply;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.AriaFTPException;
import com.arialyy.aria.ftp.AbsFtpInfoTask;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
@@ -38,10 +38,10 @@ final class FtpDFileInfoTask extends AbsFtpInfoTask<DownloadEntity, DTaskWrapper
super(taskEntity);
}
@Override protected void handleFile(String remotePath, FTPFile ftpFile) {
super.handleFile(remotePath, ftpFile);
@Override protected void handleFile(FTPClient client, String remotePath, FTPFile ftpFile) {
super.handleFile(client, remotePath, ftpFile);
if (!FileUtil.checkMemorySpace(mEntity.getFilePath(), ftpFile.getSize())) {
callback.onFail(mEntity, new AriaIOException(TAG,
handleFail(client, "内存空间不足", new AriaFTPException(
String.format("获取ftp文件信息失败内存空间不足, filePath: %s", mEntity.getFilePath())),
false);
}
@@ -72,7 +72,7 @@ final class FtpDFileInfoTask extends AbsFtpInfoTask<DownloadEntity, DTaskWrapper
}
closeClient(client);
failDownload(client,
handleFail(client,
String.format("文件不存在url: %s, remotePath%s", mTaskOption.getUrlEntity().url,
getRemotePath()), null, false);
return;
@@ -90,7 +90,7 @@ final class FtpDFileInfoTask extends AbsFtpInfoTask<DownloadEntity, DTaskWrapper
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
closeClient(client);
failDownload(client, "获取文件信息错误url: " + mTaskOption.getUrlEntity().url, null, true);
handleFail(client, "获取文件信息错误url: " + mTaskOption.getUrlEntity().url, null, true);
return;
}
mTaskWrapper.setCode(reply);
@@ -112,6 +112,6 @@ final class FtpDFileInfoTask extends AbsFtpInfoTask<DownloadEntity, DTaskWrapper
mTaskWrapper.setNewTask(true);
}
mEntity.setFileSize(mSize);
callback.onSucceed(mEntity.getUrl(), new CompleteInfo(code, mTaskWrapper));
onSucceed(new CompleteInfo(code, mTaskWrapper));
}
}

View File

@@ -32,7 +32,7 @@ import com.arialyy.aria.ftp.FtpTaskOption;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.RecordUtil;
import com.arialyy.aria.util.DeleteDGRecord;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
@@ -49,7 +49,7 @@ final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrap
@Override public void run() {
if (mTaskWrapper.getEntity().getFileSize() > 1 && checkSubOption()) {
callback.onSucceed(mEntity.getKey(), new CompleteInfo(200, mTaskWrapper));
onSucceed(new CompleteInfo(200, mTaskWrapper));
} else {
super.run();
}
@@ -80,7 +80,7 @@ final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrap
}
closeClient(client);
failDownload(client,
handleFail(client,
String.format("文件不存在url: %s, remotePath%s", mTaskOption.getUrlEntity().url,
getRemotePath()), null, false);
return;
@@ -98,7 +98,7 @@ final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrap
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
closeClient(client);
failDownload(client, "获取文件信息错误url: " + mTaskOption.getUrlEntity().url, null, true);
handleFail(client, "获取文件信息错误url: " + mTaskOption.getUrlEntity().url, null, true);
return;
}
mTaskWrapper.setCode(reply);
@@ -127,8 +127,8 @@ final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrap
return mTaskOption.getUrlEntity().remotePath;
}
@Override protected void handleFile(String remotePath, FTPFile ftpFile) {
super.handleFile(remotePath, ftpFile);
@Override protected void handleFile(FTPClient client, String remotePath, FTPFile ftpFile) {
super.handleFile(client, remotePath, ftpFile);
addEntity(remotePath, ftpFile);
}
@@ -138,7 +138,7 @@ final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrap
for (DTaskWrapper wrapper : mTaskWrapper.getSubTaskWrapper()) {
cloneInfo(wrapper);
}
callback.onSucceed(mEntity.getKey(), new CompleteInfo(code, mTaskWrapper));
onSucceed(new CompleteInfo(code, mTaskWrapper));
}
private void cloneInfo(DTaskWrapper subWrapper) {
@@ -234,8 +234,8 @@ final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrap
}
@Override
protected void failDownload(FTPClient client, String msg, Exception e, boolean needRetry) {
super.failDownload(client, msg, e, needRetry);
RecordUtil.delGroupTaskRecord(mTaskWrapper.getEntity(), true, true);
protected void handleFail(FTPClient client, String msg, Exception e, boolean needRetry) {
super.handleFail(client, msg, e, needRetry);
DeleteDGRecord.getInstance().deleteRecord(mTaskWrapper.getEntity(), true, true);
}
}

View File

@@ -25,7 +25,7 @@ import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.util.ALog;
/**
@@ -45,7 +45,9 @@ final class FtpDGLoader extends AbsGroupLoader {
@Override
protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) {
return new FtpSubDLoaderUtil(wrapper, getScheduler(), needGetFileInfo, getKey());
FtpSubDLoaderUtil subUtil = new FtpSubDLoaderUtil(getScheduler(), needGetFileInfo, getKey());
subUtil.setParams(wrapper, null);
return subUtil;
}
/**
@@ -79,7 +81,7 @@ final class FtpDGLoader extends AbsGroupLoader {
}
}
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
//getListener().onFail(needRetry, e);
fail(e, needRetry);
}

View File

@@ -18,7 +18,6 @@ package com.arialyy.aria.ftp.download;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.group.AbsGroupLoader;
import com.arialyy.aria.core.group.AbsGroupLoaderUtil;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.ftp.FtpTaskOption;
@@ -29,13 +28,12 @@ import com.arialyy.aria.ftp.FtpTaskOption;
*/
public final class FtpDGLoaderUtil extends AbsGroupLoaderUtil {
public FtpDGLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
wrapper.generateTaskOption(FtpTaskOption.class);
}
@Override protected AbsGroupLoader getLoader() {
return mLoader == null ? new FtpDGLoader(getTaskWrapper(), getListener()) : mLoader;
if (mLoader == null) {
((AbsTaskWrapper) getTaskWrapper()).generateTaskOption(FtpTaskOption.class);
mLoader = new FtpDGLoader((AbsTaskWrapper) getTaskWrapper(), getListener());
}
return mLoader;
}
@Override protected LoaderStructure buildLoaderStructure() {

View File

@@ -16,29 +16,26 @@
package com.arialyy.aria.ftp.download;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalLoader;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.NormalThreadStateManager;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.ftp.FtpTaskOption;
/**
* @Author lyy
* @Date 2019-09-19
* @author lyy
* Date: 2019-09-19
*/
public final class FtpDLoaderUtil extends AbsNormalLoaderUtil {
public FtpDLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
wrapper.generateTaskOption(FtpTaskOption.class);
}
@Override public AbsNormalLoader getLoader() {
return mLoader == null ? new NormalLoader(getTaskWrapper(), getListener()) : mLoader;
if (mLoader == null){
getTaskWrapper().generateTaskOption(FtpTaskOption.class);
mLoader = new NormalLoader(getTaskWrapper(), getListener());
}
return mLoader ;
}
public LoaderStructure BuildLoaderStructure() {

View File

@@ -27,8 +27,8 @@ import com.arialyy.aria.util.RecordUtil;
import java.util.ArrayList;
/**
* @Author lyy
* @Date 2019-09-19
* @author lyy
* Date: 2019-09-19
*/
final class FtpDRecordHandler extends RecordHandler {

View File

@@ -22,6 +22,7 @@ import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.task.IThreadTaskAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
final class FtpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
@@ -31,6 +32,10 @@ final class FtpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
}
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
if (isUriMode()) {
// 分区存储Uri模式没有本地临时文件目标内容由ContentProvider管理无需清理
return true;
}
if (!record.isBlock) {
if (getTempFile().exists()) {
FileUtil.deleteFile(getTempFile());

View File

@@ -15,15 +15,15 @@
*/
package com.arialyy.aria.ftp.download;
import aria.apache.commons.net.ftp.FTPClient;
import aria.apache.commons.net.ftp.FTPReply;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.exception.AriaFTPException;
import com.arialyy.aria.ftp.BaseFtpThreadTaskAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.UriRandomAccessFile;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -32,9 +32,12 @@ import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import aria.apache.commons.net.ftp.FTPClient;
import aria.apache.commons.net.ftp.FTPReply;
/**
* @Author lyy
* @Date 2019-09-18
* @author lyy
* Date: 2019-09-18
*/
final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
@@ -57,7 +60,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
getThreadRecord().endLocation));
client = createClient();
if (client == null) {
fail(new TaskException(TAG, "ftp client 创建失败"), false);
fail(new AriaFTPException("ftp client 创建失败"), false);
return;
}
if (getThreadRecord().startLocation > 0) {
@@ -66,7 +69,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
//发送第二次指令时,还需要再做一次判断
int reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.COMMAND_OK) {
fail(new AriaIOException(TAG,
fail(new AriaFTPException(
String.format("获取文件信息错误,错误码为:%smsg%s", reply, client.getReplyString())), false);
client.disconnect();
return;
@@ -77,12 +80,33 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
is = client.retrieveFileStream(remotePath);
reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply)) {
fail(new AriaIOException(TAG,
fail(new AriaFTPException(
String.format("获取流失败,错误码为:%smsg%s", reply, client.getReplyString())), true);
client.disconnect();
return;
}
if (getThreadConfig().tempUri != null) {
// 分区存储:通过 Uri 写入,支持断点续传
UriRandomAccessFile uriFile = null;
try {
uriFile = new UriRandomAccessFile(AriaConfig.getInstance().getAPP(),
getThreadConfig().tempUri, getThreadRecord().startLocation);
readNormal(is, uriFile);
handleComplete();
} catch (IOException e) {
fail(new AriaFTPException(
String.format("通过 Uri 下载失败【%s】", getThreadConfig().url), e), true);
} finally {
if (uriFile != null) {
try {
uriFile.close();
} catch (IOException ignored) {
}
}
}
return;
}
if (getThreadConfig().isBlock) {
readDynamicFile(is);
} else {
@@ -90,9 +114,9 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
handleComplete();
}
} catch (IOException e) {
fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true);
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
} catch (Exception e) {
fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), false);
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), false);
} finally {
try {
if (is != null) {
@@ -154,7 +178,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
}
handleComplete();
} catch (IOException e) {
fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true);
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
} finally {
try {
if (fos != null) {
@@ -179,8 +203,9 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
BufferedRandomAccessFile file = null;
try {
file =
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize());
if (getThreadRecord().startLocation > 0){
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd",
getTaskConfig().getBuffSize());
if (getThreadRecord().startLocation > 0) {
file.seek(getThreadRecord().startLocation);
}
byte[] buffer = new byte[getTaskConfig().getBuffSize()];
@@ -203,7 +228,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
}
}
} catch (IOException e) {
fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true);
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
} finally {
try {
if (file != null) {
@@ -214,4 +239,29 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
}
}
}
/**
* 分区存储:通过 UriFileChannel写入数据支持断点续传
*/
private void readNormal(InputStream is, UriRandomAccessFile 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);
}
if (getThreadRecord().endLocation > 0
&& getRangeProgress() + len >= getThreadRecord().endLocation) {
len = (int) (getThreadRecord().endLocation - getRangeProgress());
file.write(buffer, 0, len);
progress(len);
break;
}
file.write(buffer, 0, len);
progress(len);
}
}
}

View File

@@ -19,22 +19,22 @@ import android.os.Handler;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.group.AbsSubDLoadUtil;
import com.arialyy.aria.core.group.SubRecordHandler;
import com.arialyy.aria.core.loader.GroupSubThreadStateManager;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.SubLoader;
/**
* @Author lyy
* @Date 2019-09-28
* @author lyy
* Date: 2019-09-28
*/
final class FtpSubDLoaderUtil extends AbsSubDLoadUtil {
/**
* @param schedulers 调度器
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
*/
FtpSubDLoaderUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo,
String parentKey) {
super(taskWrapper, schedulers, needGetInfo, parentKey);
FtpSubDLoaderUtil(Handler schedulers, boolean needGetInfo, String parentKey) {
super(schedulers, needGetInfo, parentKey);
}
@Override protected SubLoader getLoader() {
@@ -49,6 +49,7 @@ final class FtpSubDLoaderUtil extends AbsSubDLoadUtil {
@Override protected LoaderStructure buildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new SubRecordHandler(getWrapper()))
.addComponent(new GroupSubThreadStateManager(getSchedulers(),getKey()))
.addComponent(new NormalTTBuilder(getWrapper(), new FtpDTTBuilderAdapter()))
.addComponent(new FtpDFileInfoTask(getWrapper()));
structure.accept(getLoader());

View File

@@ -60,7 +60,7 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask<UploadEntity, UTaskWrapper>
return;
}
handleFile(getRemotePath(), files.length == 0 ? null : files[0]);
handleFile(client, getRemotePath(), files.length == 0 ? null : files[0]);
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
//服务器上没有该文件路径,表示该任务为新的上传任务
@@ -133,8 +133,8 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask<UploadEntity, UTaskWrapper>
* @param remotePath ftp服务器文件夹路径
* @param ftpFile ftp服务器上对应的文件
*/
@Override protected void handleFile(String remotePath, FTPFile ftpFile) {
super.handleFile(remotePath, ftpFile);
@Override protected void handleFile(FTPClient client,String remotePath, FTPFile ftpFile) {
super.handleFile(client, remotePath, ftpFile);
this.ftpFile = ftpFile;
if (ftpFile != null && ftpFile.getSize() == mEntity.getFileSize()) {
isComplete = true;
@@ -145,6 +145,6 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask<UploadEntity, UTaskWrapper>
super.onPreComplete(code);
CompleteInfo info = new CompleteInfo(isComplete ? CODE_COMPLETE : code, mTaskWrapper);
info.obj = ftpFile;
callback.onSucceed(mEntity.getKey(), info);
onSucceed(info);
}
}

View File

@@ -26,7 +26,7 @@ import com.arialyy.aria.core.loader.NormalLoader;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.AriaException;
final class FtpULoader extends NormalLoader<UTaskWrapper> {
private FTPFile ftpFile;
@@ -37,6 +37,9 @@ final class FtpULoader extends NormalLoader<UTaskWrapper> {
@Override
protected void startThreadTask() {
if (isBreak()) {
return;
}
// 检查记录
((FtpURecordHandler) mRecordHandler).setFtpFile(ftpFile);
if (mRecordHandler.checkTaskCompleted()) {
@@ -79,7 +82,7 @@ final class FtpULoader extends NormalLoader<UTaskWrapper> {
}
}
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
getListener().onFail(needRetry, e);
}
});

View File

@@ -17,38 +17,36 @@ package com.arialyy.aria.ftp.upload;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
import com.arialyy.aria.core.loader.AbsNormalTTBuilderAdapter;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.NormalThreadStateManager;
import com.arialyy.aria.core.loader.UploadThreadStateManager;
import com.arialyy.aria.core.task.IThreadTaskAdapter;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.ftp.FtpTaskOption;
/**
* @Author lyy
* @Date 2019-09-19
* @author lyy
* Date: 2019-09-19
*/
public final class FtpULoaderUtil extends AbsNormalLoaderUtil {
public FtpULoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
wrapper.generateTaskOption(FtpTaskOption.class);
}
@Override public AbsNormalLoader getLoader() {
return mLoader == null ? new FtpULoader((UTaskWrapper) getTaskWrapper(), getListener())
: mLoader;
if (mLoader == null) {
getTaskWrapper().generateTaskOption(FtpTaskOption.class);
mLoader = new FtpULoader((UTaskWrapper) getTaskWrapper(), getListener());
}
return mLoader;
}
@Override public LoaderStructure BuildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new FtpURecordHandler((UTaskWrapper) getTaskWrapper()))
.addComponent(new NormalThreadStateManager(getListener()))
// .addComponent(new NormalThreadStateManager(getListener()))
.addComponent(new UploadThreadStateManager(getListener()))
.addComponent(new FtpUFileInfoTask((UTaskWrapper) getTaskWrapper()))
.addComponent(new NormalTTBuilder(getTaskWrapper(), new AbsNormalTTBuilderAdapter() {
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {

View File

@@ -21,7 +21,7 @@ import aria.apache.commons.net.ftp.FTPReply;
import aria.apache.commons.net.ftp.OnFtpInputStreamListener;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.AriaFTPException;
import com.arialyy.aria.ftp.BaseFtpThreadTaskAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
@@ -58,12 +58,16 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
return;
}
initPath();
client.makeDirectory(dir);
boolean b = client.makeDirectory(dir);
if (!b) {
ALog.w(TAG, String.format("创建目录失败,错误码为:%s, msg%s", client.getReplyCode(),
client.getReplyString()));
}
client.changeWorkingDirectory(dir);
client.setRestartOffset(getThreadRecord().startLocation);
int reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.FILE_ACTION_OK) {
fail(new AriaIOException(TAG,
fail(new AriaFTPException(
String.format("文件上传错误,错误码为:%s, msg%s, filePath: %s", reply,
client.getReplyString(), getEntity().getFilePath())), false);
client.disconnect();
@@ -80,8 +84,8 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
if (getThreadTask().isBreak()) {
return;
}
if (!complete){
fail(new AriaIOException(TAG, "ftp文件上传失败"), false);
if (!complete) {
fail(new AriaFTPException("ftp文件上传失败"), false);
return;
}
ALog.i(TAG,
@@ -89,11 +93,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
complete();
} catch (IOException e) {
e.printStackTrace();
fail(new AriaIOException(TAG,
fail(new AriaFTPException(
String.format("上传失败filePath: %s, uploadUrl: %s", getEntity().getFilePath(),
getThreadConfig().url)), true);
} catch (Exception e) {
fail(new AriaIOException(TAG, null, e), false);
fail(new AriaFTPException(null, e), false);
} finally {
try {
if (file != null) {
@@ -132,7 +136,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
@Override public void run() {
try {
if (isTimeOut) {
fail(new AriaIOException(TAG, "socket连接失败该问题一般出现于网络断开客户端重新连接"
fail(new AriaFTPException("socket连接失败该问题一般出现于网络断开客户端重新连接"
+ "但是服务器端无法创建socket缺没有返回错误码的情况。"), false);
if (fa != null) {
fa.close();
@@ -195,7 +199,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
if (e.getMessage().contains("AriaIOException caught while copying")) {
e.printStackTrace();
} else {
fail(new AriaIOException(TAG, msg, e), !storeSuccess);
fail(new AriaFTPException(msg, e), !storeSuccess);
}
return false;
} finally {
@@ -204,7 +208,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
if (reply != FTPReply.TRANSFER_ABORTED) {
fail(new AriaIOException(TAG,
fail(new AriaFTPException(
String.format("文件上传错误,错误码为:%s, msg%s, filePath: %s", reply, client.getReplyString(),
getEntity().getFilePath())), false);
}

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@@ -0,0 +1,3 @@
com.arialyy.aria.ftp.download.FtpDLoaderUtil
com.arialyy.aria.ftp.download.FtpDGLoaderUtil
com.arialyy.aria.ftp.upload.FtpULoaderUtil

View File

@@ -1,30 +1,50 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
namespace "com.arialyy.aria.http"
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
consumerProguardFiles 'consumer-rules.pro'
}
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
consumerProguardFiles 'consumer-rules.pro'
}
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':PublicComponent')
implementation project(path: ':PublicComponent')
}
apply from: 'bintray-release.gradle'
//apply from: 'bintray-release.gradle'
ext {
PUBLISH_ARTIFACT_ID = 'http'
}
apply from: '../gradle/mavenCentral-release.gradle'

View File

@@ -1,2 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arialyy.aria.http" />
<manifest package="com.arialyy.aria.http" />

View File

@@ -20,8 +20,8 @@ import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
/**
* @Author lyy
* @Date 2019-09-22
* @author lyy
* Date: 2019-09-22
*/
public abstract class BaseHttpThreadTaskAdapter extends AbsThreadTaskAdapter {
protected HttpTaskOption mTaskOption;

View File

@@ -48,8 +48,6 @@ public final class ConnectionHelp {
/**
* 处理url参数
*
* @throws MalformedURLException
*/
public static URL handleUrl(String url, HttpTaskOption taskDelegate)
throws MalformedURLException {
@@ -127,8 +125,6 @@ public final class ConnectionHelp {
/**
* 设置头部参数
*
* @throws ProtocolException
*/
public static HttpURLConnection setConnectParam(HttpTaskOption delegate, HttpURLConnection conn) {
if (delegate.getRequestEnum() == RequestEnum.POST) {

View File

@@ -28,21 +28,14 @@ import com.arialyy.aria.util.RecordUtil;
import java.util.ArrayList;
/**
* @Author lyy
* @Date 2019-09-23
* @author lyy
* Date: 2019-09-23
*/
public final class HttpRecordHandler extends RecordHandler {
public HttpRecordHandler(AbsTaskWrapper wrapper) {
super(wrapper);
}
@Override public void onPre() {
super.onPre();
//if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP) {
// RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_UPLOAD);
//}
}
@Override public void handlerTaskRecord(TaskRecord record) {
RecordHelper helper = new RecordHelper(getWrapper(), record);
if (getWrapper().isSupportBP() && record.threadNum > 1) {

View File

@@ -20,6 +20,8 @@ import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.inf.ITaskOption;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
import com.arialyy.aria.core.processor.IHttpFileNameAdapter;
import java.lang.ref.SoftReference;
import java.net.CookieManager;
import java.net.Proxy;
@@ -81,17 +83,23 @@ public final class HttpTaskOption implements ITaskOption {
private SoftReference<IHttpFileLenAdapter> fileLenAdapter;
private SoftReference<IHttpFileNameAdapter> fileNameAdapter;
public IHttpFileLenAdapter getFileLenAdapter() {
return fileLenAdapter == null ? null : fileLenAdapter.get();
}
public IHttpFileNameAdapter getFileNameAdapter() {
return fileNameAdapter == null ? null : fileNameAdapter.get();
}
/**
* 如果是匿名内部类完成后需要将adapter设置为空否则会出现内存泄漏
*/
public void setFileLenAdapter(IHttpFileLenAdapter fileLenAdapter) {
this.fileLenAdapter = new SoftReference<>(fileLenAdapter);
}
public void setFileNameAdapter(IHttpFileNameAdapter fileNameAdapter) {
this.fileNameAdapter = new SoftReference<>(fileNameAdapter);
}
public Map<String, String> getFormFields() {
return formFields;
}
@@ -179,4 +187,5 @@ public final class HttpTaskOption implements ITaskOption {
public void setParams(Map<String, String> params) {
this.params = params;
}
}

View File

@@ -19,6 +19,7 @@ import android.net.TrafficStats;
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.RequestEnum;
@@ -27,9 +28,7 @@ import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.loader.ILoaderVisitor;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.util.ALog;
@@ -37,9 +36,11 @@ import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.RecordUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
@@ -58,12 +59,13 @@ import java.util.UUID;
* 下载文件信息获取
*/
final class HttpDFileInfoTask implements IInfoTask, Runnable {
private static final String TAG = "HttpFileInfoThread";
private static final String TAG = "HttpDFileInfoTask";
private DownloadEntity mEntity;
private DTaskWrapper mTaskWrapper;
private int mConnectTimeOut;
private Callback callback;
private HttpTaskOption taskOption;
private boolean isStop = false, isCancel = false;
HttpDFileInfoTask(DTaskWrapper taskWrapper) {
this.mTaskWrapper = taskWrapper;
@@ -81,18 +83,24 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
conn = ConnectionHelp.handleConnection(url, taskOption);
ConnectionHelp.setConnectParam(taskOption, conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()) {
ALog.d(TAG, "head请求");
conn.setRequestMethod("HEAD");
}
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
} catch (IOException e) {
e.printStackTrace();
failDownload(new AriaIOException(TAG,
String.format("下载失败filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl())),
true);
failDownload(new AriaHTTPException(
String.format("下载失败filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl()),
e), true);
} finally {
if (conn != null) {
try {
conn.getInputStream().close();
InputStream is = conn.getInputStream();
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
@@ -105,6 +113,14 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
this.callback = callback;
}
@Override public void stop() {
isStop = true;
}
@Override public void cancel() {
isCancel = true;
}
private void handleConnect(HttpURLConnection conn) throws IOException {
if (taskOption.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = taskOption.getParams();
@@ -131,8 +147,10 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
}
long len = lenAdapter.handleFileLen(conn.getHeaderFields());
if (!FileUtil.checkMemorySpace(mEntity.getFilePath(), len)) {
failDownload(new TaskException(TAG,
// 分区存储 Uri 任务写入的是 UriSAF / MediaStore不校验本地存储空间
if (TextUtils.isEmpty(mEntity.getFileUri())
&& !FileUtil.checkMemorySpace(mEntity.getFilePath(), len)) {
failDownload(new AriaHTTPException(
String.format("下载失败内存空间不足filePath: %s, url: %s", mEntity.getFilePath(),
mEntity.getUrl())), false);
return;
@@ -158,9 +176,15 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
mEntity.setDisposition(CommonUtil.encryptBASE64(disposition));
handleContentDisposition(disposition);
} else {
ALog.w(TAG, "Content-Disposition对于端字段为空使用服务器端文件名失败");
if (taskOption.getFileNameAdapter() != null) {
String newName =
taskOption.getFileNameAdapter().handleFileName(headers, mEntity.getKey());
mEntity.setServerFileName(newName);
renameFile(newName);
}
}
}
CookieManager msCookieManager = new CookieManager();
List<String> cookiesHeader = headers.get("Set-Cookie");
@@ -176,7 +200,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
if (!checkLen(len) && !isChunked) {
if (len < 0) {
failDownload(
new AriaIOException(TAG, String.format("任务下载失败文件长度小于0 url: %s", mEntity.getUrl())),
new AriaHTTPException(String.format("任务下载失败文件长度小于0 url: %s", mEntity.getUrl())),
false);
}
return;
@@ -203,7 +227,8 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
} else if (!checkLen(len) && !isChunked) {
if (len < 0) {
failDownload(
new AriaIOException(TAG, String.format("任务下载失败文件长度小于0 url: %s", mEntity.getUrl())),
new AriaHTTPException(
String.format("任务下载失败文件长度小于0 url: %s", mEntity.getUrl())),
false);
}
ALog.d(TAG, "len < 0");
@@ -213,26 +238,32 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
mTaskWrapper.setNewTask(true);
mTaskWrapper.setSupportBP(false);
end = true;
} else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
failDownload(new AriaIOException(TAG,
String.format("任务下载失败errorCode404, url: %s", mEntity.getUrl())), true);
} else if (code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_MOVED_PERM
|| code == HttpURLConnection.HTTP_SEE_OTHER
|| code == HttpURLConnection.HTTP_CREATED // 201 跳转
|| code == 307) {
handleUrlReTurn(conn, conn.getHeaderField("Location"));
} else if (code == 416) { // 处理0k长度的文件的情况
ALog.w(TAG, "文件长度为0不支持断点");
mTaskWrapper.setSupportBP(false);
mTaskWrapper.setNewTask(true);
end = true;
} else if (code >= HttpURLConnection.HTTP_BAD_REQUEST) {
failDownload(new AriaHTTPException(
String.format("任务下载失败errorCode%s, url: %s", code, mEntity.getUrl())), false);
} else {
failDownload(new AriaIOException(TAG,
failDownload(new AriaHTTPException(
String.format("任务下载失败errorCode%s, errorMsg: %s, url: %s", code,
conn.getResponseMessage(), mEntity.getUrl())), !CheckUtil.httpIsBadRequest(code));
}
if (isStop || isCancel) {
return;
}
if (end) {
taskOption.setChunked(isChunked);
if (callback != null) {
CompleteInfo info = new CompleteInfo(code, mTaskWrapper);
callback.onSucceed(mEntity.getUrl(), info);
}
CompleteInfo info = new CompleteInfo(code, mTaskWrapper);
callback.onSucceed(mEntity.getUrl(), info);
mEntity.update();
}
}
@@ -279,9 +310,19 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
ALog.w(TAG, "重命名失败【服务器返回的文件名为空】");
return;
}
// 分区存储 Uri 任务写入的是 Uri不支持重命名本地文件仅使用服务器文件名作为展示名
if (!TextUtils.isEmpty(mEntity.getFileUri())) {
ALog.d(TAG, String.format("Uri 任务直接使用服务器文件名:%s", newName));
mEntity.setFileName(newName);
return;
}
ALog.d(TAG, String.format("文件重命名为:%s", newName));
File oldFile = new File(mEntity.getFilePath());
String newPath = oldFile.getParent() + "/" + newName;
if (!CheckUtil.checkDPathConflicts(false, newPath, mTaskWrapper.getRequestType())) {
ALog.e(TAG, "文件重命名失败");
return;
}
if (oldFile.exists()) {
boolean b = oldFile.renameTo(new File(newPath));
ALog.d(TAG, String.format("文件重命名%s", b ? "成功" : "失败"));
@@ -298,7 +339,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
ALog.d(TAG, "30x跳转新url为【" + newUrl + "");
if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null")) {
if (callback != null) {
callback.onFail(mEntity, new TaskException(TAG, "获取重定向链接失败"), false);
callback.onFail(mEntity, new AriaHTTPException("获取重定向链接失败"), false);
}
return;
}
@@ -308,7 +349,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
}
if (!CheckUtil.checkUrl(newUrl)) {
failDownload(new TaskException(TAG, "下载失败重定向url错误"), false);
failDownload(new AriaHTTPException("下载失败重定向url错误"), false);
return;
}
taskOption.setRedirectUrl(newUrl);
@@ -321,6 +362,9 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
ConnectionHelp.setConnectParam(taskOption, conn);
conn.setRequestProperty("Cookie", cookies);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()) {
conn.setRequestMethod("HEAD");
}
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
@@ -341,7 +385,10 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
return true;
}
private void failDownload(BaseException e, boolean needRetry) {
private void failDownload(AriaHTTPException e, boolean needRetry) {
if (isStop || isCancel) {
return;
}
if (callback != null) {
callback.onFail(mEntity, e, needRetry);
}

View File

@@ -20,16 +20,16 @@ import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.listener.DownloadGroupListener;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.loader.ILoaderVisitor;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 组合任务文件信息,用于获取长度未知时,组合任务的长度
@@ -41,33 +41,63 @@ public final class HttpDGInfoTask implements IInfoTask {
private final Object LOCK = new Object();
private ExecutorService mPool = null;
private boolean getLenComplete = false;
private int count;
private int failCount;
private DownloadGroupListener listener;
private AtomicInteger count = new AtomicInteger();
private AtomicInteger failCount = new AtomicInteger();
private boolean isStop = false, isCancel = false;
public interface DGInfoCallback extends Callback {
/**
* 子任务失败
*/
void onSubFail(DownloadEntity subEntity, AriaHTTPException e, boolean needRetry);
/**
* 组合任务停止
*/
void onStop(long len);
}
/**
* 子任务回调
*/
private Callback subCallback = new Callback() {
@Override public void onSucceed(String url, CompleteInfo info) {
count++;
checkGetSizeComplete(count, failCount);
count.getAndIncrement();
checkGetSizeComplete(count.get(), failCount.get());
ALog.d(TAG, "获取子任务信息完成");
}
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
ALog.e(TAG, String.format("获取文件信息失败url%s", ((DownloadEntity) entity).getUrl()));
count++;
failCount++;
listener.onSubFail((DownloadEntity) entity, new AriaIOException(TAG,
String.format("子任务获取文件长度失败url%s", ((DownloadEntity) entity).getUrl())));
checkGetSizeComplete(count, failCount);
count.getAndIncrement();
failCount.getAndIncrement();
((DGInfoCallback) callback).onSubFail((DownloadEntity) entity, new AriaHTTPException(
String.format("子任务获取文件长度失败url%s", ((DownloadEntity) entity).getUrl())), needRetry);
checkGetSizeComplete(count.get(), failCount.get());
}
};
HttpDGInfoTask(DGTaskWrapper wrapper, DownloadGroupListener listener) {
HttpDGInfoTask(DGTaskWrapper wrapper) {
this.wrapper = wrapper;
this.listener = listener;
}
/**
* 停止
*/
@Override
public void stop() {
isStop = true;
if (mPool != null) {
mPool.shutdown();
}
}
@Override public void cancel() {
isCancel = true;
if (mPool != null) {
mPool.shutdown();
}
}
@Override public void run() {
@@ -75,11 +105,11 @@ public final class HttpDGInfoTask implements IInfoTask {
if (mPool != null && !getLenComplete) {
ALog.d(TAG, "获取长度未完成的情况下,停止组合任务");
mPool.shutdown();
listener.onStop(0);
((DGInfoCallback)callback).onStop(0);
return;
}
// 处理组合任务大小未知的情况
if (wrapper.isUnknownSize() && wrapper.getEntity().getFileSize() < 1) {
if (wrapper.isUnknownSize()) {
mPool = Executors.newCachedThreadPool();
getGroupSize();
try {
@@ -89,7 +119,7 @@ public final class HttpDGInfoTask implements IInfoTask {
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!mPool.isShutdown()){
if (!mPool.isShutdown()) {
mPool.shutdown();
}
} else {
@@ -107,6 +137,16 @@ public final class HttpDGInfoTask implements IInfoTask {
new Thread(new Runnable() {
@Override public void run() {
for (DTaskWrapper dTaskWrapper : wrapper.getSubTaskWrapper()) {
DownloadEntity subEntity = dTaskWrapper.getEntity();
if (subEntity.getFileSize() > 0) {
count.getAndIncrement();
if (subEntity.getCurrentProgress() < subEntity.getFileSize()) {
// 如果没有完成需要拷贝一份数据
cloneHeader(dTaskWrapper);
}
checkGetSizeComplete(count.get(), failCount.get());
continue;
}
cloneHeader(dTaskWrapper);
HttpDFileInfoTask infoTask = new HttpDFileInfoTask(dTaskWrapper);
infoTask.setCallback(subCallback);
@@ -120,8 +160,13 @@ public final class HttpDGInfoTask implements IInfoTask {
* 检查组合任务大小是否获取完成,获取完成后取消阻塞,并设置组合任务大小
*/
private void checkGetSizeComplete(int count, int failCount) {
if (isStop || isCancel) {
ALog.w(TAG, "任务已停止或已取消isStop = " + isStop + ", isCancel = " + isCancel);
notifyLock();
return;
}
if (failCount == wrapper.getSubTaskWrapper().size()) {
callback.onFail(wrapper.getEntity(), new AriaIOException(TAG, "获取子任务长度失败"), false);
callback.onFail(wrapper.getEntity(), new AriaHTTPException("获取子任务长度失败"), false);
notifyLock();
return;
}
@@ -155,6 +200,10 @@ public final class HttpDGInfoTask implements IInfoTask {
// 设置属性
subOption.setFileLenAdapter(groupOption.getFileLenAdapter());
subOption.setFileNameAdapter(groupOption.getFileNameAdapter());
subOption.setUseServerFileName(groupOption.isUseServerFileName());
subOption.setFileNameAdapter(groupOption.getFileNameAdapter());
subOption.setRequestEnum(groupOption.getRequestEnum());
subOption.setHeaders(groupOption.getHeaders());
subOption.setProxy(groupOption.getProxy());

View File

@@ -25,7 +25,9 @@ import com.arialyy.aria.core.group.AbsSubDLoadUtil;
import com.arialyy.aria.core.listener.DownloadGroupListener;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.exception.AriaHTTPException;
import java.io.File;
/**
* http 组合任务加载器
@@ -44,7 +46,9 @@ final class HttpDGLoader extends AbsGroupLoader {
@Override
protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) {
return new HttpSubDLoaderUtil(wrapper, getScheduler(), needGetFileInfo, getKey());
HttpSubDLoaderUtil subUtil = new HttpSubDLoaderUtil(getScheduler(), needGetFileInfo, getKey());
subUtil.setParams(wrapper, null);
return subUtil;
}
private void startSub() {
@@ -54,18 +58,29 @@ final class HttpDGLoader extends AbsGroupLoader {
onPostStart();
for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) {
DownloadEntity dEntity = wrapper.getEntity();
startSubLoader(createSubLoader(wrapper, dEntity.getFileSize() < 0));
}
}
@Override public void addComponent(IInfoTask infoTask) {
mInfoTask = infoTask;
mInfoTask.setCallback(new IInfoTask.Callback() {
mInfoTask.setCallback(new HttpDGInfoTask.DGInfoCallback() {
@Override
public void onSubFail(DownloadEntity subEntity, AriaHTTPException e, boolean needRetry) {
getState().countFailNum(subEntity.getKey());
}
@Override public void onStop(long len) {
getListener().onStop(len);
}
@Override public void onSucceed(String key, CompleteInfo info) {
startSub();
}
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
fail(e, needRetry);
}
});

View File

@@ -19,9 +19,7 @@ import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.group.AbsGroupLoader;
import com.arialyy.aria.core.group.AbsGroupLoaderUtil;
import com.arialyy.aria.core.listener.DownloadGroupListener;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.http.HttpTaskOption;
/**
@@ -30,20 +28,17 @@ import com.arialyy.aria.http.HttpTaskOption;
*/
public final class HttpDGLoaderUtil extends AbsGroupLoaderUtil {
public HttpDGLoaderUtil(AbsTaskWrapper taskWrapper, IEventListener listener) {
super(taskWrapper, listener);
taskWrapper.generateTaskOption(HttpTaskOption.class);
}
@Override protected AbsGroupLoader getLoader() {
return mLoader == null ? new HttpDGLoader(getTaskWrapper(),
(DownloadGroupListener) getListener()) : mLoader;
if (mLoader == null) {
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
mLoader = new HttpDGLoader(getTaskWrapper(), (DownloadGroupListener) getListener());
}
return mLoader;
}
@Override protected LoaderStructure buildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new HttpDGInfoTask((DGTaskWrapper) getTaskWrapper(),
(DownloadGroupListener) getListener()));
structure.addComponent(new HttpDGInfoTask((DGTaskWrapper) getTaskWrapper()));
structure.accept(getLoader());
return structure;
}

View File

@@ -15,40 +15,31 @@
*/
package com.arialyy.aria.http.download;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
import com.arialyy.aria.core.loader.AbsNormalTTBuilderAdapter;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalLoader;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.NormalThreadStateManager;
import com.arialyy.aria.core.task.IThreadTaskAdapter;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.http.HttpRecordHandler;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.io.IOException;
/**
* @Author lyy
* @Date 2019-09-21
* @author lyy
* Date: 2019-09-21
*/
public final class HttpDLoaderUtil extends AbsNormalLoaderUtil {
public HttpDLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
wrapper.generateTaskOption(HttpTaskOption.class);
}
@Override public AbsNormalLoader getLoader() {
return mLoader == null ? new NormalLoader(getTaskWrapper(), getListener()) : mLoader;
if (mLoader == null){
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
mLoader = new NormalLoader(getTaskWrapper(), getListener());
}
return mLoader;
}
public LoaderStructure BuildLoaderStructure() {
@@ -60,6 +51,4 @@ public final class HttpDLoaderUtil extends AbsNormalLoaderUtil {
structure.accept(getLoader());
return structure;
}
}

View File

@@ -8,6 +8,7 @@ import com.arialyy.aria.core.task.IThreadTaskAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.io.IOException;
@@ -18,6 +19,10 @@ final class HttpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
}
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
if (isUriMode()) {
// 分区存储Uri模式没有本地临时文件目标内容由ContentProvider管理无需清理
return true;
}
if (!record.isBlock) {
if (getTempFile().exists()) {
FileUtil.deleteFile(getTempFile());
@@ -35,10 +40,13 @@ final class HttpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
BufferedRandomAccessFile file = null;
try {
if (totalThreadNum > 1 && !record.isBlock) {
file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192);
file = new BufferedRandomAccessFile(getTempFile().getPath(), "rwd", 8192);
//设置文件长度
file.setLength(getEntity().getFileSize());
}
if (getTempFile().exists()) {
FileUtil.deleteFile(getTempFile());
}
return true;
} catch (IOException e) {
e.printStackTrace();

View File

@@ -15,15 +15,17 @@
*/
package com.arialyy.aria.http.download;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.BaseHttpThreadTaskAdapter;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.UriRandomAccessFile;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -44,7 +46,7 @@ import java.util.Set;
* Created by lyy on 2017/1/18. 下载线程
*/
final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
private final String TAG = "HttpThreadTask";
private final String TAG = "HttpDThreadTaskAdapter";
private DTaskWrapper mTaskWrapper;
HttpDThreadTaskAdapter(SubThreadConfig config) {
@@ -60,10 +62,6 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
HttpURLConnection conn = null;
BufferedInputStream is = null;
BufferedRandomAccessFile file = null;
if (getThreadRecord().threadId == 1){
fail(null, false);
return;
}
try {
URL url = ConnectionHelp.handleUrl(getThreadConfig().url, mTaskOption);
conn = ConnectionHelp.handleConnection(url, mTaskOption);
@@ -105,6 +103,29 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
}
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (getThreadConfig().tempUri != null) {
// 分区存储:通过 Uri 写入,支持断点续传
UriRandomAccessFile uriFile = null;
try {
uriFile = new UriRandomAccessFile(AriaConfig.getInstance().getAPP(),
getThreadConfig().tempUri, getThreadRecord().startLocation);
readNormal(is, uriFile);
handleComplete();
return;
} catch (IOException e) {
fail(new AriaHTTPException(String.format("任务【%s】通过 Uri 下载失败uri: %s, url: %s",
getFileName(), getThreadConfig().tempUri, getThreadConfig().url), e), true);
return;
} finally {
if (uriFile != null) {
try {
uriFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
if (mTaskOption.isChunked()) {
readChunked(is);
} else if (getThreadConfig().isBlock) {
@@ -122,21 +143,17 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
handleComplete();
}
} catch (MalformedURLException e) {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (IOException e) {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), true);
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), true);
} catch (ArrayIndexOutOfBoundsException e) {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (Exception e) {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} finally {
try {
if (file != null) {
@@ -176,9 +193,9 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
}
handleComplete();
} catch (IOException e) {
fail(new AriaIOException(TAG,
fail(new AriaHTTPException(
String.format("文件下载失败savePath: %s, url: %s", getEntity().getFilePath(),
getThreadConfig().url)), true);
getThreadConfig().url), e), true);
} finally {
if (fos != null) {
try {
@@ -229,8 +246,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
}
handleComplete();
} catch (IOException e) {
fail(new AriaIOException(TAG,
String.format("文件下载失败savePath: %s, url: %s", getEntity().getFilePath(),
fail(new AriaHTTPException(String.format("文件下载失败savePath: %s, url: %s", getEntity().getFilePath(),
getThreadConfig().url), e), true);
} finally {
try {
@@ -269,6 +285,31 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
}
}
/**
* 分区存储:通过 UriFileChannel写入数据支持断点续传
*/
private void readNormal(InputStream is, UriRandomAccessFile 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);
}
if (getThreadRecord().endLocation > 0
&& getRangeProgress() + len >= getThreadRecord().endLocation) {
len = (int) (getThreadRecord().endLocation - getRangeProgress());
file.write(buffer, 0, len);
progress(len);
break;
}
file.write(buffer, 0, len);
progress(len);
}
}
/**
* 处理完成配置文件的更新或事件回调
*/

View File

@@ -19,22 +19,22 @@ import android.os.Handler;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.group.AbsSubDLoadUtil;
import com.arialyy.aria.core.group.SubRecordHandler;
import com.arialyy.aria.core.loader.GroupSubThreadStateManager;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.SubLoader;
/**
* @Author lyy
* @Date 2019-09-28
* @author lyy
* Date: 2019-09-28
*/
final class HttpSubDLoaderUtil extends AbsSubDLoadUtil {
/**
* @param schedulers 调度器
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
*/
HttpSubDLoaderUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo,
String parentKey) {
super(taskWrapper, schedulers, needGetInfo, parentKey);
HttpSubDLoaderUtil( Handler schedulers, boolean needGetInfo, String parentKey) {
super(schedulers, needGetInfo, parentKey);
}
@Override protected SubLoader getLoader() {
@@ -49,8 +49,9 @@ final class HttpSubDLoaderUtil extends AbsSubDLoadUtil {
@Override protected LoaderStructure buildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new SubRecordHandler(getWrapper()))
.addComponent(new NormalTTBuilder(getWrapper(), new HttpDTTBuilderAdapter()))
.addComponent(new HttpDFileInfoTask(getWrapper()));
.addComponent(new GroupSubThreadStateManager(getSchedulers(),getKey()))
.addComponent(new NormalTTBuilder(getWrapper(), new HttpDTTBuilderAdapter()))
.addComponent(new HttpDFileInfoTask(getWrapper()));
structure.accept(getLoader());
return structure;
}

View File

@@ -26,7 +26,7 @@ import com.arialyy.aria.core.loader.IThreadTaskBuilder;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.util.ALog;
import java.util.List;
@@ -62,7 +62,7 @@ final class HttpULoader extends AbsNormalLoader<UTaskWrapper> {
new Handler(looper, mStateManager.getHandlerCallback()));
if (tt == null || tt.isEmpty()) {
ALog.e(TAG, "创建线程任务失败");
getListener().onFail(false, new AriaIOException(TAG, "创建线程任务失败"));
getListener().onFail(false, new AriaHTTPException("创建线程任务失败"));
return;
}

View File

@@ -17,38 +17,37 @@ package com.arialyy.aria.http.upload;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
import com.arialyy.aria.core.loader.AbsNormalTTBuilderAdapter;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.NormalThreadStateManager;
import com.arialyy.aria.core.loader.UploadThreadStateManager;
import com.arialyy.aria.core.task.IThreadTaskAdapter;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.http.HttpRecordHandler;
import com.arialyy.aria.http.HttpTaskOption;
/**
* @Author lyy
* @Date 2019-09-19
* @author lyy
* Date: 2019-09-19
*/
public final class HttpULoaderUtil extends AbsNormalLoaderUtil {
public HttpULoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
wrapper.generateTaskOption(HttpTaskOption.class);
}
@Override public AbsNormalLoader getLoader() {
return mLoader == null ? new HttpULoader((UTaskWrapper) getTaskWrapper(), getListener())
: mLoader;
if (mLoader == null) {
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
mLoader = new HttpULoader((UTaskWrapper) getTaskWrapper(), getListener());
}
return mLoader;
}
@Override public LoaderStructure BuildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new HttpRecordHandler(getTaskWrapper()))
.addComponent(new NormalThreadStateManager(getListener()))
// .addComponent(new NormalThreadStateManager(getListener()))
.addComponent(new UploadThreadStateManager(getListener()))
.addComponent(new NormalTTBuilder(getTaskWrapper(), new AbsNormalTTBuilderAdapter() {
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
return new HttpUThreadTaskAdapter(config);

View File

@@ -18,15 +18,15 @@ package com.arialyy.aria.http.upload;
import android.text.TextUtils;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.BaseHttpThreadTaskAdapter;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
@@ -56,7 +56,7 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
@Override protected void handlerThreadTask() {
File uploadFile = new File(getEntity().getFilePath());
if (!uploadFile.exists()) {
fail(new TaskException(TAG,
fail(new AriaHTTPException(
String.format("上传失败文件不存在filePath: %s, url: %s", getEntity().getFilePath(),
getEntity().getUrl())));
return;
@@ -76,7 +76,6 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
mHttpConn.setRequestProperty("User-Agent", getUserAgent());
mHttpConn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
mHttpConn.setReadTimeout(getTaskConfig().getIOTimeOut());
//mHttpConn.setRequestProperty("Range", "bytes=" + 0 + "-" + "100");
//内部缓冲区---分段上传防止oom
mHttpConn.setChunkedStreamingMode(getTaskConfig().getBuffSize());
@@ -103,16 +102,16 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
}
uploadFile(writer, mTaskOption.getAttachment(), uploadFile);
getEntity().setResponseStr(finish(writer));
finish(writer);
} catch (Exception e) {
e.printStackTrace();
fail(new TaskException(TAG,
fail(new AriaHTTPException(
String.format("上传失败filePath: %s, url: %s", getEntity().getFilePath(),
getEntity().getUrl()), e));
}
}
private void fail(BaseException e1) {
private void fail(AriaHTTPException e1) {
try {
fail(e1, false);
if (mOutputStream != null) {
@@ -124,9 +123,10 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
}
private String getContentType() {
return (mTaskOption.getHeaders() == null || TextUtils.isEmpty(
mTaskOption.getHeaders().get("Content-Type"))) ? "multipart/form-data"
: mTaskOption.getHeaders().get("Content-Type");
//return (mTaskOption.getHeaders() == null || TextUtils.isEmpty(
// mTaskOption.getHeaders().get("Content-Type"))) ? "multipart/form-data"
// : mTaskOption.getHeaders().get("Content-Type");
return "multipart/form-data";
}
private String getUserAgent() {
@@ -177,23 +177,23 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
FileInputStream inputStream = new FileInputStream(uploadFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
progress(bytesRead);
mOutputStream.write(buffer, 0, bytesRead);
int bytesLen;
while ((bytesLen = inputStream.read(buffer)) != -1) {
mOutputStream.write(buffer, 0, bytesLen);
progress(bytesLen);
if (getThreadTask().isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(bytesRead);
mSpeedBandUtil.limitNextBytes(bytesLen);
}
}
mOutputStream.flush();
//outputStream.close(); //不能调用,否则服务器端异常
inputStream.close();
writer.append(LINE_END);
writer.flush();
writer.append(LINE_END).flush();
// 保证上传的文件和本地的一致https://www.cnblogs.com/tugenhua0707/p/8975121.html
writer.append(PREFIX).append(BOUNDARY).append(PREFIX).append(LINE_END).flush();
}
/**
@@ -203,26 +203,25 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
*/
private String finish(PrintWriter writer) throws IOException {
StringBuilder response = new StringBuilder();
writer.append(LINE_END).flush();
writer.append(PREFIX).append(BOUNDARY).append(PREFIX).append(LINE_END);
writer.flush();
int status = mHttpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(mHttpConn.getInputStream()));
BufferedReader reader =
new BufferedReader(new InputStreamReader(ConnectionHelp.convertInputStream(mHttpConn)));
String line;
while (getThreadTask().isLive() && (line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
mHttpConn.disconnect();
getEntity().setResponseStr(response.toString());
complete();
} else {
String msg = "response msg: " + mHttpConn.getResponseMessage() + "code: " + status;
ALog.e(TAG, msg);
fail(new AriaIOException(TAG, msg), false);
getEntity().setResponseStr(response.toString());
fail(new AriaHTTPException(msg), false);
response.append(status);
}
writer.flush();
writer.close();

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@@ -0,0 +1,3 @@
com.arialyy.aria.http.download.HttpDLoaderUtil
com.arialyy.aria.http.download.HttpDGLoaderUtil
com.arialyy.aria.http.upload.HttpULoaderUtil

View File

@@ -1,32 +1,52 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
namespace "com.arialyy.aria.m3u8"
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':HttpComponent')
implementation project(path: ':PublicComponent')
implementation project(path: ':HttpComponent')
implementation project(path: ':PublicComponent')
}
apply from: 'bintray-release.gradle'
//apply from: 'bintray-release.gradle'
ext {
PUBLISH_ARTIFACT_ID = 'm3u8'
}
apply from: '../gradle/mavenCentral-release.gradle'

View File

@@ -1,2 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arialyy.aria.m3u8" />
<manifest package="com.arialyy.aria.m3u8" />

View File

@@ -0,0 +1,29 @@
/*
* 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.m3u8;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
/**
* 点播文件默认的码率转换器
*/
class BandWidthDefConverter implements IBandWidthUrlConverter {
@Override public String convert(String m3u8Url, String bandWidthUrl) {
int index = m3u8Url.lastIndexOf("/");
return m3u8Url.substring(0, index + 1) + bandWidthUrl;
}
}

View File

@@ -95,9 +95,21 @@ public abstract class BaseM3U8Loader extends AbsNormalLoader<DTaskWrapper> {
i++;
} else if (line.startsWith("#EXT-X-KEY")) {
M3U8Entity m3U8Entity = getEntity().getM3U8Entity();
String keyInfo = String.format("#EXT-X-KEY:METHOD=%s,URI=%s,IV=%s\r\n", m3U8Entity.method,
m3U8Entity.keyPath, m3U8Entity.iv);
bytes = keyInfo.getBytes(Charset.forName("UTF-8"));
StringBuilder sb = new StringBuilder("#EXT-X-KEY:");
sb.append("METHOD=").append(m3U8Entity.method);
sb.append(",URI=\"").append(m3U8Entity.keyPath).append("\"");
if (!TextUtils.isEmpty(m3U8Entity.iv)) {
sb.append(",IV=").append(m3U8Entity.iv);
}
if (!TextUtils.isEmpty(m3U8Entity.keyFormat)) {
sb.append(",KEYFORMAT=\"").append(m3U8Entity.keyFormat).append("\"");
sb.append(",KEYFORMATVERSIONS=\"")
.append(TextUtils.isEmpty(m3U8Entity.keyFormatVersion) ? "1"
: m3U8Entity.keyFormatVersion)
.append("\"");
}
sb.append("\r\n");
bytes = sb.toString().getBytes(Charset.forName("UTF-8"));
} else {
bytes = line.concat("\r\n").getBytes(Charset.forName("UTF-8"));
}

View File

@@ -7,11 +7,8 @@ public final class IdGenerator {
* <p>
* 主要就是三部分: 时间戳进程id序列号 时间戳41id10位序列号12位
*
* @author chiwei
* @param args
* @since JDK 1.6
*/
private static volatile IdGenerator INSTANCE = null;
private final static long beginTs = 1483200000000L;

View File

@@ -30,8 +30,7 @@ import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.processor.IKeyUrlConverter;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.M3U8Exception;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.exception.AriaM3U8Exception;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.util.ALog;
@@ -102,7 +101,7 @@ final public class M3U8InfoTask implements IInfoTask {
ConnectionHelp.setConnectParam(mHttpOption, conn);
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
handleConnect(mEntity.getUrl(), conn);
} catch (IOException e) {
failDownload(e.getMessage(), false);
} finally {
@@ -116,10 +115,19 @@ final public class M3U8InfoTask implements IInfoTask {
mCallback = callback;
}
private void handleConnect(HttpURLConnection conn) throws IOException {
@Override public void stop() {
this.isStop = true;
}
@Override public void cancel() {
this.isStop = true;
}
private void handleConnect(String tsListUrl, HttpURLConnection conn) throws IOException {
int code = conn.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
BufferedReader reader =
new BufferedReader(new InputStreamReader(ConnectionHelp.convertInputStream(conn)));
String line = reader.readLine();
if (TextUtils.isEmpty(line) || !line.equalsIgnoreCase("#EXTM3U")) {
failDownload("读取M3U8信息失败读取不到#EXTM3U标签", false);
@@ -141,7 +149,7 @@ final public class M3U8InfoTask implements IInfoTask {
}
fos = new FileOutputStream(indexFile);
ALog.d(TAG, line);
addIndexInfo(isGenerateIndexFile, fos, line);
addIndexInfo(true, fos, line);
}
while ((line = reader.readLine()) != null) {
if (isStop) {
@@ -152,7 +160,9 @@ final public class M3U8InfoTask implements IInfoTask {
// 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志
addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
break;
} else if (line.startsWith("#EXTINF")) {
}
if (line.startsWith("#EXTINF")) {
String url = reader.readLine();
if (isLive) {
if (onGetPeerCallback != null) {
@@ -164,7 +174,10 @@ final public class M3U8InfoTask implements IInfoTask {
ALog.d(TAG, url);
addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
addIndexInfo(isGenerateIndexFile && !isLive, fos, url);
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
continue;
}
if (line.startsWith("#EXT-X-STREAM-INF")) {
addIndexInfo(isGenerateIndexFile, fos, line);
int setBand = mM3U8Option.getBandWidth();
int bandWidth = getBandWidth(line);
@@ -177,15 +190,17 @@ final public class M3U8InfoTask implements IInfoTask {
} else if (bandWidth == setBand) {
handleBandWidth(conn, reader.readLine());
} else {
failDownload(String.format("【%s】码率不存在", bandWidth), false);
failDownload(String.format("【%s】码率不存在", setBand), false);
}
return;
} else if (line.startsWith("#EXT-X-KEY")) {
addIndexInfo(isGenerateIndexFile, fos, line);
getKeyInfo(line);
} else {
addIndexInfo(isGenerateIndexFile, fos, line);
}
if (line.startsWith("#EXT-X-KEY")) {
addIndexInfo(isGenerateIndexFile, fos, line);
getKeyInfo(tsListUrl, line);
continue;
}
addIndexInfo(isGenerateIndexFile, fos, line);
}
if (!isLive && extInf.isEmpty()) {
@@ -198,8 +213,7 @@ final public class M3U8InfoTask implements IInfoTask {
}
CompleteInfo info = new CompleteInfo();
info.obj = extInf;
mCallback.onSucceed(mEntity.getKey(), info);
onSucceed(info);
if (fos != null) {
fos.close();
}
@@ -209,13 +223,20 @@ final public class M3U8InfoTask implements IInfoTask {
|| code == HttpURLConnection.HTTP_CREATED // 201 跳转
|| code == 307) {
handleUrlReTurn(conn, conn.getHeaderField("Location"));
} else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
failDownload("404错误", false);
} else if (code >= HttpURLConnection.HTTP_BAD_REQUEST) {
failDownload("下载失败错误,错误码:" + code, false);
} else {
failDownload(String.format("不支持的响应code: %s", code), true);
}
}
private void onSucceed(CompleteInfo info) {
if (isStop) {
return;
}
mCallback.onSucceed(mEntity.getKey(), info);
}
/**
* 添加切片信息到索引文件中
* 直播下载的索引只记录头部信息不记录EXTINF中的信息该信息在onGetPeer的方法中添加。
@@ -249,7 +270,7 @@ final public class M3U8InfoTask implements IInfoTask {
/**
* 获取加密的密钥信息
*/
private void getKeyInfo(String line) {
private void getKeyInfo(String tsListUrl, String line) {
String temp = line.substring(line.indexOf(":") + 1);
String[] params = temp.split(",");
M3U8Entity m3U8Entity = mEntity.getM3U8Entity();
@@ -274,7 +295,7 @@ final public class M3U8InfoTask implements IInfoTask {
m3U8Entity.keyFormatVersion = param.split("=")[1];
}
}
downloadKey(m3U8Entity);
downloadKey(tsListUrl, m3U8Entity);
}
/**
@@ -296,7 +317,7 @@ final public class M3U8InfoTask implements IInfoTask {
ALog.d(TAG, "30x跳转新url为【" + newUrl + "");
if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null")) {
if (mCallback != null) {
mCallback.onFail(mEntity, new TaskException(TAG, "获取重定向链接失败"), false);
mCallback.onFail(mEntity, new AriaM3U8Exception("获取重定向链接失败"), false);
}
return;
}
@@ -321,7 +342,7 @@ final public class M3U8InfoTask implements IInfoTask {
conn.setRequestProperty("Cookie", cookies);
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
handleConnect(newUrl, conn);
conn.disconnect();
}
@@ -329,9 +350,10 @@ final public class M3U8InfoTask implements IInfoTask {
* 处理码率
*/
private void handleBandWidth(HttpURLConnection conn, String bandWidthM3u8Url) throws IOException {
IBandWidthUrlConverter converter = mM3U8Option.getBandWidthUrlConverter();
IBandWidthUrlConverter converter = mM3U8Option.isUseDefConvert() ? new BandWidthDefConverter()
: mM3U8Option.getBandWidthUrlConverter();
if (converter != null) {
bandWidthM3u8Url = converter.convert(bandWidthM3u8Url);
bandWidthM3u8Url = converter.convert(mEntity.getUrl(), bandWidthM3u8Url);
if (!bandWidthM3u8Url.startsWith("http")) {
failDownload(String.format("码率转换器转换后的url地址无效转换后的url%s", bandWidthM3u8Url), false);
return;
@@ -349,18 +371,21 @@ final public class M3U8InfoTask implements IInfoTask {
conn.setRequestProperty("Cookie", cookies);
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
handleConnect(bandWidthM3u8Url, conn);
conn.disconnect();
}
private void failDownload(String errorInfo, boolean needRetry) {
mCallback.onFail(mEntity, new M3U8Exception(TAG, errorInfo), needRetry);
if (isStop) {
return;
}
mCallback.onFail(mEntity, new AriaM3U8Exception(errorInfo), needRetry);
}
/**
* 密钥不存在,下载密钥
*/
private void downloadKey(M3U8Entity info) {
private void downloadKey(String tsListUr, M3U8Entity info) {
HttpURLConnection conn = null;
FileOutputStream fos = null;
try {
@@ -375,7 +400,7 @@ final public class M3U8InfoTask implements IInfoTask {
IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter();
String keyUrl = info.keyUrl;
if (keyUrlConverter != null) {
keyUrl = keyUrlConverter.convert(mEntity.getUrl(), keyUrl);
keyUrl = keyUrlConverter.convert(mEntity.getUrl(), tsListUr, keyUrl);
}
if (TextUtils.isEmpty(keyUrl)) {
ALog.e(TAG, "m3u8密钥key url 为空");
@@ -387,7 +412,7 @@ final public class M3U8InfoTask implements IInfoTask {
ConnectionHelp.setConnectParam(mHttpOption, conn);
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
InputStream is = conn.getInputStream();
InputStream is = ConnectionHelp.convertInputStream(conn);
fos = new FileOutputStream(keyF);
byte[] buffer = new byte[1024];
int len;

Some files were not shown because too many files have changed in this diff Show More