Compare commits
73 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 |
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,23 +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.DeleteDGRecord;
|
||||
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;
|
||||
|
||||
@@ -63,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();
|
||||
}
|
||||
@@ -253,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);
|
||||
@@ -292,138 +267,19 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
@@ -432,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,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"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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,10 +149,63 @@ 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())) {
|
||||
// 检查路径冲突
|
||||
|
||||
@@ -23,6 +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.FileUtil;
|
||||
import com.arialyy.aria.util.RecordUtil;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@@ -62,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;
|
||||
}
|
||||
@@ -123,6 +132,13 @@ public class CheckDGEntityUtil implements ICheckEntityUtil {
|
||||
* @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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,7 +440,7 @@ public class DownloadReceiver extends AbsReceiver {
|
||||
* @return 如果没有任务组列表,则返回null
|
||||
*/
|
||||
public List<DownloadGroupEntity> getGroupTaskList() {
|
||||
return getGroupTaskList(1,10);
|
||||
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> {
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class M3U8Option<OP extends M3U8Option> extends BaseOption {
|
||||
* @param useDefConvert true 使用默认的转换器,false 关闭默认的转换器
|
||||
*/
|
||||
public OP setUseDefConvert(boolean useDefConvert) {
|
||||
this.useDefConvert = true;
|
||||
this.useDefConvert = useDefConvert;
|
||||
ALog.d(TAG, "使用默认的码率转换器和TS转换器,如果无法下载,请参考:https://github.com/AriaLyy/Aria/issues/597 定制转换器");
|
||||
return (OP) this;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2019/6/26.
|
||||
* m3u8切片事件回调类
|
||||
*/
|
||||
public class AptM3U8PeerTaskListener implements M3U8PeerTaskListener, ISchedulerListener{
|
||||
|
||||
@Override public void onPeerStart(final String m3u8Url, final String peerPath, final int peerIndex) {
|
||||
}
|
||||
|
||||
@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 {
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package com.arialyy.aria.core.scheduler;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import com.arialyy.annotations.TaskEnum;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
@@ -54,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() {
|
||||
@@ -99,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);
|
||||
@@ -124,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;
|
||||
}
|
||||
|
||||
@@ -137,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();
|
||||
@@ -202,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;
|
||||
}
|
||||
@@ -235,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);
|
||||
}
|
||||
@@ -250,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;
|
||||
}
|
||||
@@ -358,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);
|
||||
@@ -368,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) {
|
||||
@@ -399,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) {
|
||||
@@ -420,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,回调失败");
|
||||
@@ -497,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);
|
||||
|
||||
@@ -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'
|
||||
34
DEV_LOG.md
34
DEV_LOG.md
@@ -1,4 +1,38 @@
|
||||
## 开发日志
|
||||
+ 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
|
||||
|
||||
@@ -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,6 +27,7 @@ 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;
|
||||
@@ -55,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;
|
||||
@@ -81,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;
|
||||
}
|
||||
@@ -101,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);
|
||||
}
|
||||
@@ -146,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;
|
||||
}
|
||||
@@ -168,14 +178,14 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -299,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());
|
||||
@@ -314,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客户端失败";
|
||||
@@ -328,10 +341,14 @@ public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER exte
|
||||
needRetry = needRetry && !CheckUtil.ftpIsBadRequest(client.getReplyCode());
|
||||
}
|
||||
|
||||
callback.onFail(mEntity, new AriaFTPException(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);
|
||||
}
|
||||
|
||||
@@ -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 AriaFTPException(TAG,
|
||||
fail(new AriaFTPException(
|
||||
String.format("无法连接到ftp服务器,错误码为:%s,msg:%s", reply, client.getReplyString())), false);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -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 AriaFTPException(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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
protected void handleFail(FTPClient client, String msg, Exception e, boolean needRetry) {
|
||||
super.handleFail(client, msg, e, needRetry);
|
||||
DeleteDGRecord.getInstance().deleteRecord(mTaskWrapper.getEntity(), true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,14 +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.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;
|
||||
@@ -31,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 {
|
||||
|
||||
@@ -56,7 +60,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
getThreadRecord().endLocation));
|
||||
client = createClient();
|
||||
if (client == null) {
|
||||
fail(new AriaFTPException(TAG, "ftp client 创建失败"), false);
|
||||
fail(new AriaFTPException("ftp client 创建失败"), false);
|
||||
return;
|
||||
}
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
@@ -65,7 +69,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
//发送第二次指令时,还需要再做一次判断
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.COMMAND_OK) {
|
||||
fail(new AriaFTPException(TAG,
|
||||
fail(new AriaFTPException(
|
||||
String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString())), false);
|
||||
client.disconnect();
|
||||
return;
|
||||
@@ -76,12 +80,33 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
is = client.retrieveFileStream(remotePath);
|
||||
reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositivePreliminary(reply)) {
|
||||
fail(new AriaFTPException(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 {
|
||||
@@ -89,9 +114,9 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
handleComplete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fail(new AriaFTPException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
} catch (Exception e) {
|
||||
fail(new AriaFTPException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), false);
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), false);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) {
|
||||
@@ -153,7 +178,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(new AriaFTPException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
} finally {
|
||||
try {
|
||||
if (fos != null) {
|
||||
@@ -203,7 +228,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fail(new AriaFTPException(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -67,7 +67,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
client.setRestartOffset(getThreadRecord().startLocation);
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.FILE_ACTION_OK) {
|
||||
fail(new AriaFTPException(TAG,
|
||||
fail(new AriaFTPException(
|
||||
String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply,
|
||||
client.getReplyString(), getEntity().getFilePath())), false);
|
||||
client.disconnect();
|
||||
@@ -85,7 +85,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
return;
|
||||
}
|
||||
if (!complete) {
|
||||
fail(new AriaFTPException(TAG, "ftp文件上传失败"), false);
|
||||
fail(new AriaFTPException("ftp文件上传失败"), false);
|
||||
return;
|
||||
}
|
||||
ALog.i(TAG,
|
||||
@@ -93,11 +93,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
complete();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
fail(new AriaFTPException(TAG,
|
||||
fail(new AriaFTPException(
|
||||
String.format("上传失败,filePath: %s, uploadUrl: %s", getEntity().getFilePath(),
|
||||
getThreadConfig().url)), true);
|
||||
} catch (Exception e) {
|
||||
fail(new AriaFTPException(TAG, null, e), false);
|
||||
fail(new AriaFTPException(null, e), false);
|
||||
} finally {
|
||||
try {
|
||||
if (file != null) {
|
||||
@@ -136,7 +136,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
@Override public void run() {
|
||||
try {
|
||||
if (isTimeOut) {
|
||||
fail(new AriaFTPException(TAG, "socket连接失败,该问题一般出现于网络断开,客户端重新连接,"
|
||||
fail(new AriaFTPException("socket连接失败,该问题一般出现于网络断开,客户端重新连接,"
|
||||
+ "但是服务器端无法创建socket缺没有返回错误码的情况。"), false);
|
||||
if (fa != null) {
|
||||
fa.close();
|
||||
@@ -199,7 +199,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
if (e.getMessage().contains("AriaIOException caught while copying")) {
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
fail(new AriaFTPException(TAG, msg, e), !storeSuccess);
|
||||
fail(new AriaFTPException(msg, e), !storeSuccess);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
@@ -208,7 +208,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
if (reply != FTPReply.TRANSFER_ABORTED) {
|
||||
fail(new AriaFTPException(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,8 +28,8 @@ 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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -35,6 +36,7 @@ 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;
|
||||
@@ -63,6 +65,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
private int mConnectTimeOut;
|
||||
private Callback callback;
|
||||
private HttpTaskOption taskOption;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
HttpDFileInfoTask(DTaskWrapper taskWrapper) {
|
||||
this.mTaskWrapper = taskWrapper;
|
||||
@@ -80,11 +83,15 @@ 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) {
|
||||
failDownload(new AriaHTTPException(TAG,
|
||||
failDownload(new AriaHTTPException(
|
||||
String.format("下载失败,filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl()),
|
||||
e), true);
|
||||
} finally {
|
||||
@@ -106,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();
|
||||
@@ -132,8 +147,10 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
}
|
||||
long len = lenAdapter.handleFileLen(conn.getHeaderFields());
|
||||
|
||||
if (!FileUtil.checkMemorySpace(mEntity.getFilePath(), len)) {
|
||||
failDownload(new AriaHTTPException(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;
|
||||
@@ -183,8 +200,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
if (!checkLen(len) && !isChunked) {
|
||||
if (len < 0) {
|
||||
failDownload(
|
||||
new AriaHTTPException(TAG,
|
||||
String.format("任务下载失败,文件长度小于0, url: %s", mEntity.getUrl())),
|
||||
new AriaHTTPException(String.format("任务下载失败,文件长度小于0, url: %s", mEntity.getUrl())),
|
||||
false);
|
||||
}
|
||||
return;
|
||||
@@ -211,7 +227,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
} else if (!checkLen(len) && !isChunked) {
|
||||
if (len < 0) {
|
||||
failDownload(
|
||||
new AriaHTTPException(TAG,
|
||||
new AriaHTTPException(
|
||||
String.format("任务下载失败,文件长度小于0, url: %s", mEntity.getUrl())),
|
||||
false);
|
||||
}
|
||||
@@ -228,20 +244,26 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
|| 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(TAG,
|
||||
failDownload(new AriaHTTPException(
|
||||
String.format("任务下载失败,errorCode:%s, url: %s", code, mEntity.getUrl())), false);
|
||||
} else {
|
||||
failDownload(new AriaHTTPException(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();
|
||||
}
|
||||
}
|
||||
@@ -288,6 +310,12 @@ 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;
|
||||
@@ -311,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 AriaHTTPException(TAG, "获取重定向链接失败"), false);
|
||||
callback.onFail(mEntity, new AriaHTTPException("获取重定向链接失败"), false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -321,7 +349,7 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
}
|
||||
|
||||
if (!CheckUtil.checkUrl(newUrl)) {
|
||||
failDownload(new AriaHTTPException(TAG, "下载失败,重定向url错误"), false);
|
||||
failDownload(new AriaHTTPException("下载失败,重定向url错误"), false);
|
||||
return;
|
||||
}
|
||||
taskOption.setRedirectUrl(newUrl);
|
||||
@@ -334,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);
|
||||
@@ -355,6 +386,9 @@ final class HttpDFileInfoTask implements IInfoTask, Runnable {
|
||||
}
|
||||
|
||||
private void failDownload(AriaHTTPException e, boolean needRetry) {
|
||||
if (isStop || isCancel) {
|
||||
return;
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.onFail(mEntity, e, needRetry);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ 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.AriaException;
|
||||
@@ -44,7 +43,20 @@ public final class HttpDGInfoTask implements IInfoTask {
|
||||
private boolean getLenComplete = false;
|
||||
private AtomicInteger count = new AtomicInteger();
|
||||
private AtomicInteger failCount = new AtomicInteger();
|
||||
private DownloadGroupListener listener;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
public interface DGInfoCallback extends Callback {
|
||||
|
||||
/**
|
||||
* 子任务失败
|
||||
*/
|
||||
void onSubFail(DownloadEntity subEntity, AriaHTTPException e, boolean needRetry);
|
||||
|
||||
/**
|
||||
* 组合任务停止
|
||||
*/
|
||||
void onStop(long len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 子任务回调
|
||||
@@ -60,15 +72,32 @@ public final class HttpDGInfoTask implements IInfoTask {
|
||||
ALog.e(TAG, String.format("获取文件信息失败,url:%s", ((DownloadEntity) entity).getUrl()));
|
||||
count.getAndIncrement();
|
||||
failCount.getAndIncrement();
|
||||
listener.onSubFail((DownloadEntity) entity, new AriaHTTPException(TAG,
|
||||
String.format("子任务获取文件长度失败,url:%s", ((DownloadEntity) entity).getUrl())));
|
||||
((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() {
|
||||
@@ -76,7 +105,7 @@ public final class HttpDGInfoTask implements IInfoTask {
|
||||
if (mPool != null && !getLenComplete) {
|
||||
ALog.d(TAG, "获取长度未完成的情况下,停止组合任务");
|
||||
mPool.shutdown();
|
||||
listener.onStop(0);
|
||||
((DGInfoCallback)callback).onStop(0);
|
||||
return;
|
||||
}
|
||||
// 处理组合任务大小未知的情况
|
||||
@@ -111,7 +140,7 @@ public final class HttpDGInfoTask implements IInfoTask {
|
||||
DownloadEntity subEntity = dTaskWrapper.getEntity();
|
||||
if (subEntity.getFileSize() > 0) {
|
||||
count.getAndIncrement();
|
||||
if (subEntity.getCurrentProgress() < subEntity.getFileSize()){
|
||||
if (subEntity.getCurrentProgress() < subEntity.getFileSize()) {
|
||||
// 如果没有完成需要拷贝一份数据
|
||||
cloneHeader(dTaskWrapper);
|
||||
}
|
||||
@@ -131,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 AriaHTTPException(TAG, "获取子任务长度失败"), false);
|
||||
callback.onFail(wrapper.getEntity(), new AriaHTTPException("获取子任务长度失败"), false);
|
||||
notifyLock();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ 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.AriaException;
|
||||
import com.arialyy.aria.exception.AriaHTTPException;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
@@ -45,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() {
|
||||
@@ -62,7 +65,17 @@ final class HttpDGLoader extends AbsGroupLoader {
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -24,21 +24,22 @@ 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.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.http.HttpRecordHandler;
|
||||
import com.arialyy.aria.http.HttpTaskOption;
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
|
||||
@@ -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,6 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
@@ -23,6 +24,8 @@ 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;
|
||||
@@ -100,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) {
|
||||
@@ -117,21 +143,17 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
handleComplete();
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
fail(new AriaHTTPException(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 AriaHTTPException(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 AriaHTTPException(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 AriaHTTPException(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) {
|
||||
@@ -171,7 +193,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(new AriaHTTPException(TAG,
|
||||
fail(new AriaHTTPException(
|
||||
String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(),
|
||||
getThreadConfig().url), e), true);
|
||||
} finally {
|
||||
@@ -224,8 +246,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(new AriaHTTPException(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 {
|
||||
@@ -264,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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理完成配置文件的更新或事件回调
|
||||
*/
|
||||
|
||||
@@ -25,17 +25,16 @@ 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() {
|
||||
|
||||
@@ -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 AriaHTTPException(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);
|
||||
|
||||
@@ -20,11 +20,13 @@ import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
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;
|
||||
@@ -54,7 +56,7 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
@Override protected void handlerThreadTask() {
|
||||
File uploadFile = new File(getEntity().getFilePath());
|
||||
if (!uploadFile.exists()) {
|
||||
fail(new AriaHTTPException(TAG,
|
||||
fail(new AriaHTTPException(
|
||||
String.format("上传失败,文件不存在;filePath: %s, url: %s", getEntity().getFilePath(),
|
||||
getEntity().getUrl())));
|
||||
return;
|
||||
@@ -74,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());
|
||||
|
||||
@@ -101,10 +102,10 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
}
|
||||
|
||||
uploadFile(writer, mTaskOption.getAttachment(), uploadFile);
|
||||
getEntity().setResponseStr(finish(writer));
|
||||
finish(writer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(new AriaHTTPException(TAG,
|
||||
fail(new AriaHTTPException(
|
||||
String.format("上传失败,filePath: %s, url: %s", getEntity().getFilePath(),
|
||||
getEntity().getUrl()), e));
|
||||
}
|
||||
@@ -122,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() {
|
||||
@@ -175,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,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 AriaHTTPException(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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -101,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 {
|
||||
@@ -115,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);
|
||||
@@ -140,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) {
|
||||
@@ -151,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) {
|
||||
@@ -163,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);
|
||||
@@ -179,12 +193,14 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
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()) {
|
||||
@@ -197,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();
|
||||
}
|
||||
@@ -215,6 +230,13 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
}
|
||||
}
|
||||
|
||||
private void onSucceed(CompleteInfo info) {
|
||||
if (isStop) {
|
||||
return;
|
||||
}
|
||||
mCallback.onSucceed(mEntity.getKey(), info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加切片信息到索引文件中
|
||||
* 直播下载的索引只记录头部信息,不记录EXTINF中的信息,该信息在onGetPeer的方法中添加。
|
||||
@@ -248,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();
|
||||
@@ -273,7 +295,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
m3U8Entity.keyFormatVersion = param.split("=")[1];
|
||||
}
|
||||
}
|
||||
downloadKey(m3U8Entity);
|
||||
downloadKey(tsListUrl, m3U8Entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,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 AriaM3U8Exception(TAG, "获取重定向链接失败"), false);
|
||||
mCallback.onFail(mEntity, new AriaM3U8Exception("获取重定向链接失败"), false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -320,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();
|
||||
}
|
||||
|
||||
@@ -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 AriaM3U8Exception(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;
|
||||
|
||||
@@ -16,29 +16,20 @@
|
||||
package com.arialyy.aria.m3u8;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
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.inf.TaskSchedulerType;
|
||||
import com.arialyy.aria.core.listener.BaseListener;
|
||||
import com.arialyy.aria.core.listener.IDLoadListener;
|
||||
import com.arialyy.aria.core.listener.ISchedulers;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.core.task.DownloadTask;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.DeleteM3u8Record;
|
||||
|
||||
/**
|
||||
* 下载监听类
|
||||
*/
|
||||
public final class M3U8Listener
|
||||
extends BaseListener<DownloadEntity, DTaskWrapper, AbsTask<DTaskWrapper>>
|
||||
implements IDLoadListener {
|
||||
|
||||
public M3U8Listener(AbsTask<DTaskWrapper> task, Handler outHandler) {
|
||||
super(task, outHandler);
|
||||
}
|
||||
public final class M3U8Listener extends BaseListener implements IDLoadListener {
|
||||
|
||||
@Override
|
||||
public void onPostPre(long fileSize) {
|
||||
@@ -88,7 +79,7 @@ public final class M3U8Listener
|
||||
}
|
||||
|
||||
@Override protected void handleCancel() {
|
||||
int sType = getTask().getSchedulerType();
|
||||
int sType = getTask(DownloadTask.class).getSchedulerType();
|
||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||
mEntity.setComplete(false);
|
||||
mEntity.setState(IEntity.STATE_WAIT);
|
||||
|
||||
@@ -91,15 +91,15 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
|
||||
handleConn(conn);
|
||||
} catch (MalformedURLException e) {
|
||||
fail(new AriaM3U8Exception(TAG,
|
||||
fail(new AriaM3U8Exception(
|
||||
String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId,
|
||||
getThreadConfig().tempFile.getPath(), getEntity().getUrl()), e), false);
|
||||
} catch (IOException e) {
|
||||
fail(new AriaM3U8Exception(TAG,
|
||||
fail(new AriaM3U8Exception(
|
||||
String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId,
|
||||
getThreadConfig().tempFile.getPath(), getEntity().getUrl()), e), true);
|
||||
} catch (Exception e) {
|
||||
fail(new AriaM3U8Exception(TAG,
|
||||
fail(new AriaM3U8Exception(
|
||||
String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId,
|
||||
getThreadConfig().tempFile.getPath(), getEntity().getUrl()), e), false);
|
||||
} finally {
|
||||
@@ -137,7 +137,7 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
|| code == 307) {
|
||||
handleUrlReTurn(conn, conn.getHeaderField("Location"));
|
||||
} else {
|
||||
fail(new AriaM3U8Exception(TAG,
|
||||
fail(new AriaM3U8Exception(
|
||||
String.format("连接错误,http错误码:%s,url:%s", code, getThreadConfig().url)),
|
||||
false);
|
||||
}
|
||||
@@ -150,7 +150,7 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
private void handleUrlReTurn(HttpURLConnection conn, String newUrl) throws IOException {
|
||||
ALog.d(TAG, "30x跳转,新url为【" + newUrl + "】");
|
||||
if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null")) {
|
||||
fail(new AriaM3U8Exception(TAG, "下载失败,重定向url为空"), false);
|
||||
fail(new AriaM3U8Exception("下载失败,重定向url为空"), false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
}
|
||||
|
||||
if (!CheckUtil.checkUrl(newUrl)) {
|
||||
fail(new AriaM3U8Exception(TAG, "下载失败,重定向url错误"), false);
|
||||
fail(new AriaM3U8Exception("下载失败,重定向url错误"), false);
|
||||
return;
|
||||
}
|
||||
String cookies = conn.getHeaderField("Set-Cookie");
|
||||
@@ -198,7 +198,7 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(new AriaM3U8Exception(TAG,
|
||||
fail(new AriaM3U8Exception(
|
||||
String.format("文件下载失败,savePath: %s, url: %s", getThreadConfig().tempFile.getPath(),
|
||||
getThreadConfig().url), e), true);
|
||||
} finally {
|
||||
@@ -241,7 +241,7 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(new AriaM3U8Exception(TAG,
|
||||
fail(new AriaM3U8Exception(
|
||||
String.format("文件下载失败,savePath: %s, url: %s", getThreadConfig().tempFile.getPath(),
|
||||
getThreadConfig().url), e), true);
|
||||
} finally {
|
||||
|
||||
@@ -196,6 +196,7 @@ final class M3U8LiveLoader extends BaseM3U8Loader {
|
||||
config.peerIndex = indexId;
|
||||
config.threadType = SubThreadConfig.getThreadType(ITaskWrapper.M3U8_LIVE);
|
||||
config.updateInterval = SubThreadConfig.getUpdateInterval(ITaskWrapper.M3U8_LIVE);
|
||||
config.ignoreFailure = mM3U8Option.isIgnoreFailureTs();
|
||||
if (!config.tempFile.exists()) {
|
||||
FileUtil.createFile(config.tempFile);
|
||||
}
|
||||
@@ -276,7 +277,7 @@ final class M3U8LiveLoader extends BaseM3U8Loader {
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(url) || !url.startsWith("http")) {
|
||||
fail(new AriaM3U8Exception(TAG, String.format("ts地址错误,url:%s", url)), false);
|
||||
fail(new AriaM3U8Exception(String.format("ts地址错误,url:%s", url)), false);
|
||||
return;
|
||||
}
|
||||
offerPeer(new M3U8LiveLoader.ExtInfo(url, extInf));
|
||||
@@ -297,13 +298,13 @@ final class M3U8LiveLoader extends BaseM3U8Loader {
|
||||
if (generateIndexFile(true)) {
|
||||
getListener().onComplete();
|
||||
} else {
|
||||
getListener().onFail(false, new AriaM3U8Exception(TAG, "创建索引文件失败"));
|
||||
getListener().onFail(false, new AriaM3U8Exception("创建索引文件失败"));
|
||||
}
|
||||
} else if (mM3U8Option.isMergeFile()) {
|
||||
if (mergeFile()) {
|
||||
getListener().onComplete();
|
||||
} else {
|
||||
getListener().onFail(false, new AriaM3U8Exception(TAG, "合并文件失败"));
|
||||
getListener().onFail(false, new AriaM3U8Exception("合并文件失败"));
|
||||
}
|
||||
} else {
|
||||
getListener().onComplete();
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
package com.arialyy.aria.m3u8.live;
|
||||
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
|
||||
import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.http.HttpTaskOption;
|
||||
import com.arialyy.aria.m3u8.M3U8InfoTask;
|
||||
import com.arialyy.aria.m3u8.M3U8Listener;
|
||||
@@ -36,8 +34,7 @@ import com.arialyy.aria.m3u8.M3U8TaskOption;
|
||||
*/
|
||||
public class M3U8LiveUtil extends AbsNormalLoaderUtil {
|
||||
|
||||
public M3U8LiveUtil(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
public M3U8LiveUtil() {
|
||||
}
|
||||
|
||||
@Override public DTaskWrapper getTaskWrapper() {
|
||||
@@ -45,11 +42,12 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
|
||||
}
|
||||
|
||||
@Override public M3U8LiveLoader getLoader() {
|
||||
getTaskWrapper().generateM3u8Option(M3U8TaskOption.class);
|
||||
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
|
||||
return
|
||||
mLoader == null ? new M3U8LiveLoader(getTaskWrapper(), (M3U8Listener) getListener())
|
||||
: (M3U8LiveLoader) mLoader;
|
||||
if (mLoader == null) {
|
||||
getTaskWrapper().generateM3u8Option(M3U8TaskOption.class);
|
||||
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
|
||||
mLoader = new M3U8LiveLoader(getTaskWrapper(), (M3U8Listener) getListener());
|
||||
}
|
||||
return (M3U8LiveLoader) mLoader;
|
||||
}
|
||||
|
||||
@Override public LoaderStructure BuildLoaderStructure() {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.arialyy.aria.m3u8.vod;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
@@ -160,6 +161,11 @@ final class M3U8VodLoader extends BaseM3U8Loader {
|
||||
|
||||
// 启动定时器
|
||||
startTimer();
|
||||
if (getStateManager().isComplete()){
|
||||
Log.d(TAG, "任务已完成");
|
||||
getStateManager().handleTaskComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动线程开始下载ts切片
|
||||
Thread th = new Thread(new Runnable() {
|
||||
@@ -486,6 +492,7 @@ final class M3U8VodLoader extends BaseM3U8Loader {
|
||||
config.peerIndex = index;
|
||||
config.threadType = SubThreadConfig.getThreadType(ITaskWrapper.M3U8_LIVE);
|
||||
config.updateInterval = SubThreadConfig.getUpdateInterval(ITaskWrapper.M3U8_LIVE);
|
||||
config.ignoreFailure = mM3U8Option.isIgnoreFailureTs();
|
||||
if (!config.tempFile.exists()) {
|
||||
FileUtil.createFile(config.tempFile);
|
||||
}
|
||||
@@ -519,10 +526,10 @@ final class M3U8VodLoader extends BaseM3U8Loader {
|
||||
urls.addAll((Collection<? extends String>) info.obj);
|
||||
}
|
||||
if (urls.isEmpty()) {
|
||||
fail(new AriaM3U8Exception(TAG, "获取地址失败"), false);
|
||||
fail(new AriaM3U8Exception("获取地址失败"), false);
|
||||
return;
|
||||
} else if (!urls.get(0).startsWith("http")) {
|
||||
fail(new AriaM3U8Exception(TAG, "地址错误,请使用IVodTsUrlConverter处理你的url信息"), false);
|
||||
fail(new AriaM3U8Exception("地址错误,请使用IVodTsUrlConverter处理你的url信息"), false);
|
||||
return;
|
||||
}
|
||||
mM3U8Option.setUrls(urls);
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
package com.arialyy.aria.m3u8.vod;
|
||||
|
||||
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.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.http.HttpTaskOption;
|
||||
import com.arialyy.aria.m3u8.M3U8InfoTask;
|
||||
import com.arialyy.aria.m3u8.M3U8Listener;
|
||||
@@ -36,8 +34,7 @@ import com.arialyy.aria.m3u8.M3U8TaskOption;
|
||||
*/
|
||||
public final class M3U8VodUtil extends AbsNormalLoaderUtil {
|
||||
|
||||
public M3U8VodUtil(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
public M3U8VodUtil() {
|
||||
}
|
||||
|
||||
@Override public DTaskWrapper getTaskWrapper() {
|
||||
@@ -45,10 +42,12 @@ public final class M3U8VodUtil extends AbsNormalLoaderUtil {
|
||||
}
|
||||
|
||||
@Override public AbsNormalLoader getLoader() {
|
||||
getTaskWrapper().generateM3u8Option(M3U8TaskOption.class);
|
||||
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
|
||||
return mLoader == null ? new M3U8VodLoader(getTaskWrapper(), (M3U8Listener) getListener())
|
||||
: mLoader;
|
||||
if (mLoader == null) {
|
||||
getTaskWrapper().generateM3u8Option(M3U8TaskOption.class);
|
||||
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
|
||||
mLoader = new M3U8VodLoader(getTaskWrapper(), (M3U8Listener) getListener());
|
||||
}
|
||||
return mLoader;
|
||||
}
|
||||
|
||||
@Override public LoaderStructure BuildLoaderStructure() {
|
||||
|
||||
@@ -31,8 +31,8 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @Author lyy
|
||||
* @Date 2019-09-24
|
||||
* @author lyy
|
||||
* Date: 2019-09-24
|
||||
*/
|
||||
final class VodRecordHandler extends RecordHandler {
|
||||
private M3U8TaskOption mOption;
|
||||
|
||||
@@ -139,28 +139,7 @@ public final class VodStateManager implements IThreadStateManager {
|
||||
loader.notifyWaitLock(true);
|
||||
}
|
||||
if (isComplete()) {
|
||||
ALog.d(TAG, String.format(
|
||||
"startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s",
|
||||
startThreadNum, stopNum, cancelNum, failNum, loader.getCompleteNum(),
|
||||
loader.getCurrentFlagSize()));
|
||||
ALog.d(TAG, String.format("vod任务【%s】完成", loader.getTempFile().getName()));
|
||||
|
||||
if (m3U8Option.isGenerateIndexFile()) {
|
||||
if (loader.generateIndexFile(false)) {
|
||||
listener.onComplete();
|
||||
} else {
|
||||
listener.onFail(false, new AriaM3U8Exception(TAG, "创建索引文件失败"));
|
||||
}
|
||||
} else if (m3U8Option.isMergeFile()) {
|
||||
if (mergeFile()) {
|
||||
listener.onComplete();
|
||||
} else {
|
||||
listener.onFail(false, null);
|
||||
}
|
||||
} else {
|
||||
listener.onComplete();
|
||||
}
|
||||
quitLooper();
|
||||
handleTaskComplete();
|
||||
}
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
@@ -175,6 +154,34 @@ public final class VodStateManager implements IThreadStateManager {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理m3u8以完成
|
||||
*/
|
||||
void handleTaskComplete() {
|
||||
ALog.d(TAG, String.format(
|
||||
"startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s",
|
||||
startThreadNum, stopNum, cancelNum, failNum, loader.getCompleteNum(),
|
||||
loader.getCurrentFlagSize()));
|
||||
ALog.d(TAG, String.format("vod任务【%s】完成", loader.getTempFile().getName()));
|
||||
|
||||
if (m3U8Option.isGenerateIndexFile()) {
|
||||
if (loader.generateIndexFile(false)) {
|
||||
listener.onComplete();
|
||||
} else {
|
||||
listener.onFail(false, new AriaM3U8Exception("创建索引文件失败"));
|
||||
}
|
||||
} else if (m3U8Option.isMergeFile()) {
|
||||
if (mergeFile()) {
|
||||
listener.onComplete();
|
||||
} else {
|
||||
listener.onFail(false, null);
|
||||
}
|
||||
} else {
|
||||
listener.onComplete();
|
||||
}
|
||||
quitLooper();
|
||||
}
|
||||
|
||||
void updateStateCount() {
|
||||
cancelNum.set(0);
|
||||
stopNum.set(0);
|
||||
|
||||
2
M3U8Component/src/main/resources/META-INF/MANIFEST.MF
Normal file
2
M3U8Component/src/main/resources/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
com.arialyy.aria.m3u8.live.M3U8LiveUtil
|
||||
com.arialyy.aria.m3u8.vod.M3U8VodUtil
|
||||
@@ -0,0 +1 @@
|
||||
com.arialyy.aria.m3u8.M3U8Listener
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user