Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 51eb7e9cb7 | |||
| cef92d6a2f | |||
| cb50a2dcd2 | |||
| 3dfeabdf3b | |||
| c494c06265 | |||
| 9eca197acb | |||
| 31cee5b0f5 | |||
| 8b74586403 | |||
|
|
aa292e4389 | ||
|
|
cba9483f3a | ||
|
|
eeb0bea97a | ||
|
|
3fc99cd01e | ||
|
|
48af5509f5 | ||
|
|
56893b0d88 | ||
|
|
ada1f9f993 | ||
|
|
7392eba667 | ||
|
|
3e2c3bc055 | ||
|
|
6e9f674fab | ||
|
|
afa6434664 | ||
|
|
00a81220cb | ||
|
|
ebbbbee1f8 | ||
|
|
6eab496b6a | ||
|
|
3679e0cb23 | ||
|
|
66f9aa9cd5 | ||
|
|
edb3e66a96 | ||
|
|
1c0765be02 | ||
|
|
5853fe9e3e | ||
|
|
9db8f1e9c0 | ||
|
|
35e66caab5 | ||
|
|
bcfb78109b | ||
|
|
d7d081ffb1 | ||
|
|
840facd1cc | ||
|
|
f67f829dfc | ||
|
|
0152187aa8 | ||
|
|
68c992962b | ||
|
|
514fa4321d | ||
|
|
313e047d1f | ||
|
|
62ad99e252 | ||
|
|
8785bb2875 | ||
|
|
7be93b38c2 | ||
|
|
3024b85091 | ||
|
|
c78fa1b66a | ||
|
|
428fbab9d2 | ||
|
|
07a1f45f1e | ||
|
|
22a3b61dbf | ||
|
|
eccbb37647 | ||
|
|
ab358e7dbf | ||
|
|
86aeac0ad8 | ||
|
|
ff0796c580 | ||
|
|
fed2b4111f | ||
|
|
1a04a6d221 | ||
|
|
5f60b9ed57 | ||
|
|
9d8bf579b6 | ||
|
|
bebc9b926d | ||
|
|
ae1a9e3870 | ||
|
|
288dcf633e | ||
|
|
fed6ae55e1 | ||
|
|
41bae31d6a | ||
|
|
8787030fa6 | ||
|
|
b5cbbd9d3d | ||
|
|
1241bc5991 | ||
|
|
a3df4016ff | ||
|
|
981917fa52 | ||
|
|
023f648d07 | ||
|
|
9a174311d9 | ||
|
|
3edf40f533 | ||
|
|
46c10f6ffd | ||
|
|
32cb6152dd | ||
|
|
f657bbcc53 | ||
|
|
e6f5c4f472 | ||
|
|
b7e0f6f0e8 | ||
|
|
0bc083fa98 | ||
|
|
d931b75800 | ||
|
|
418ec74b7b | ||
|
|
29f9bdcdbd | ||
|
|
9a5e491e37 | ||
|
|
e64f3b1240 | ||
|
|
dcde3f07b8 | ||
|
|
290e58c58b | ||
|
|
b109726c7c | ||
|
|
4d9a887076 | ||
|
|
a1c294bcbd | ||
|
|
1f2ac36580 | ||
|
|
203eeb10ad | ||
|
|
06a0d764ed | ||
|
|
baf4bac091 | ||
|
|
4951dda5b0 | ||
|
|
ba0162e413 | ||
|
|
0476c3a941 | ||
|
|
73292b6405 | ||
|
|
e99d4a3d7e | ||
|
|
e0dc7f3644 | ||
|
|
cd31276591 | ||
|
|
a65b2e31f9 | ||
|
|
de09add3a3 | ||
|
|
41088dfa23 | ||
|
|
14901d4c0c | ||
|
|
9cb6490da9 | ||
|
|
a1fd63b5c6 | ||
|
|
b90450f1cd | ||
|
|
370dca27ff | ||
|
|
669ac6b09c | ||
|
|
54cbdb7ee0 | ||
|
|
e770818cee | ||
|
|
d92f1bba94 | ||
|
|
ea2e27f3e9 | ||
|
|
ad58dce395 | ||
|
|
472006ef61 | ||
|
|
604fb91800 | ||
|
|
63ec23f241 | ||
|
|
2f2d783655 | ||
|
|
e015c2a2f7 | ||
|
|
96efa35c4e | ||
|
|
7f81995bd8 |
8
.github/FUNDING.yml
vendored
Normal file
8
.github/FUNDING.yml
vendored
Normal 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"]
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Custom.md
vendored
2
.github/ISSUE_TEMPLATE/Custom.md
vendored
@@ -11,6 +11,8 @@ about: 提交问题前,请先阅读文档和搜索issue
|
||||
|
||||
* 系统版本
|
||||
|
||||
## 错误的url
|
||||
|
||||
## 错误日志
|
||||
<!-- 请提供详细的错误日志 -->
|
||||
|
||||
|
||||
20
.github/stale.yml
vendored
Normal file
20
.github/stale.yml
vendored
Normal 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
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
/**
|
||||
* AES加密
|
||||
*
|
||||
* @author lyy
|
||||
* author lyy
|
||||
*/
|
||||
public class AESEncryption {
|
||||
/**
|
||||
|
||||
@@ -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'
|
||||
@@ -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)");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 10(Q):同上,仍可请求 legacy 存储</li>
|
||||
* <li>Android 11/12(R/S):{@link Manifest.permission#WRITE_EXTERNAL_STORAGE} 已废弃,
|
||||
* 仅需 {@link Manifest.permission#READ_EXTERNAL_STORAGE}</li>
|
||||
* <li>Android 13+(TIRAMISU):READ/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/12:WRITE_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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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文件为一个
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -49,5 +49,11 @@ public interface IReceiver {
|
||||
*
|
||||
* @return {@link ReceiverType}
|
||||
*/
|
||||
String getType();
|
||||
ReceiverType getType();
|
||||
|
||||
/**
|
||||
* 判断是否是fragment,如果是fragment,在activity销毁时,需要将其从receiver中移除
|
||||
*/
|
||||
boolean isFragment();
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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'
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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'
|
||||
59
DEV_LOG.md
59
DEV_LOG.md
@@ -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
|
||||
|
||||
@@ -1,292 +1,173 @@
|
||||
# Aria
|
||||
</br>
|
||||
</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
|
||||
|
||||

|
||||
|
||||
* Speed limit
|
||||
|
||||

|
||||
|
||||
* Multi-file package download
|
||||
|
||||
<img src="https://github.com/AriaLyy/DownloadUtil/blob/master/img/group_task.gif" width="360" height="640"/>
|
||||
|
||||
* m3u8 download
|
||||
|
||||

|
||||
|
||||
## Lib
|
||||
[](https://github.com/AriaLyy/Aria/blob/master/LICENSE)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](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
|
||||
[](https://bintray.com/arialyy/maven/AriaApi/_latestVersion)
|
||||
[](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
|
||||

|
||||

|
||||
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
|
||||

|
||||
__⚠️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 example,only 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 special,need 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 don’t 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
|
||||
-------
|
||||
|
||||
|
||||
@@ -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'
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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服务器,错误码为:%s,msg:%s", reply, client.getReplyString())), false);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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("获取文件信息错误,错误码为:%s,msg:%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("获取流失败,错误码为:%s,msg:%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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分区存储:通过 Uri(FileChannel)写入数据,支持断点续传
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
2
FtpComponent/src/main/resources/META-INF/MANIFEST.MF
Normal file
2
FtpComponent/src/main/resources/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
com.arialyy.aria.ftp.download.FtpDLoaderUtil
|
||||
com.arialyy.aria.ftp.download.FtpDGLoaderUtil
|
||||
com.arialyy.aria.ftp.upload.FtpULoaderUtil
|
||||
@@ -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'
|
||||
@@ -1,2 +1 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.arialyy.aria.http" />
|
||||
<manifest package="com.arialyy.aria.http" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 任务写入的是 Uri(SAF / 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("任务下载失败,errorCode:404, 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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分区存储:通过 Uri(FileChannel)写入数据,支持断点续传
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理完成配置文件的更新或事件回调
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
2
HttpComponent/src/main/resources/META-INF/MANIFEST.MF
Normal file
2
HttpComponent/src/main/resources/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
com.arialyy.aria.http.download.HttpDLoaderUtil
|
||||
com.arialyy.aria.http.download.HttpDGLoaderUtil
|
||||
com.arialyy.aria.http.upload.HttpULoaderUtil
|
||||
@@ -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'
|
||||
@@ -1,2 +1 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.arialyy.aria.m3u8" />
|
||||
<manifest package="com.arialyy.aria.m3u8" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -7,11 +7,8 @@ public final class IdGenerator {
|
||||
* <p>
|
||||
* 主要就是三部分: 时间戳,进程id,序列号 时间戳41,id10位,序列号12位
|
||||
*
|
||||
* @author chiwei
|
||||
* @param args
|
||||
* @since JDK 1.6
|
||||
*/
|
||||
|
||||
private static volatile IdGenerator INSTANCE = null;
|
||||
|
||||
private final static long beginTs = 1483200000000L;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user