源代码备份
This commit is contained in:
1
PublicComponent/.gitignore
vendored
Normal file
1
PublicComponent/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
13
PublicComponent/bintray-release.gradle
Normal file
13
PublicComponent/bintray-release.gradle
Normal file
@ -0,0 +1,13 @@
|
||||
apply plugin: 'com.novoda.bintray-release'
|
||||
publish {
|
||||
// artifactId = 'aria-compiler'
|
||||
// uploadName = 'AriaCompiler'
|
||||
artifactId = 'publicComponent'
|
||||
uploadName = 'PublicComponent'
|
||||
userOrg = rootProject.ext.userOrg
|
||||
groupId = rootProject.ext.groupId
|
||||
publishVersion = rootProject.ext.publishVersion
|
||||
desc = rootProject.ext.desc
|
||||
website = rootProject.ext.website
|
||||
licences = rootProject.ext.licences
|
||||
}
|
42
PublicComponent/build.gradle
Normal file
42
PublicComponent/build.gradle
Normal file
@ -0,0 +1,42 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
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
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles 'consumer-rules.pro'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug{
|
||||
debuggable true
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
//apply from: 'bintray-release.gradle'
|
||||
ext{
|
||||
PUBLISH_ARTIFACT_ID = 'public'
|
||||
}
|
||||
apply from: '../gradle/mavenCentral-release.gradle'
|
0
PublicComponent/consumer-rules.pro
Normal file
0
PublicComponent/consumer-rules.pro
Normal file
21
PublicComponent/proguard-rules.pro
vendored
Normal file
21
PublicComponent/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
5
PublicComponent/src/main/AndroidManifest.xml
Normal file
5
PublicComponent/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.arialyy.aria.publiccomponent" >
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.config.AppConfig;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.config.DGroupConfig;
|
||||
import com.arialyy.aria.core.config.DownloadConfig;
|
||||
import com.arialyy.aria.core.config.UploadConfig;
|
||||
import com.arialyy.aria.core.config.XMLReader;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class AriaConfig {
|
||||
private static final String TAG = "AriaConfig";
|
||||
|
||||
public static final String DOWNLOAD_TEMP_DIR = "/Aria/temp/download/";
|
||||
public static final String UPLOAD_TEMP_DIR = "/Aria/temp/upload/";
|
||||
public static final String IGNORE_CLASS_KLASS = "shadow$_klass_";
|
||||
public static final String IGNORE_CLASS_MONITOR = "shadow$_monitor_";
|
||||
|
||||
private static volatile AriaConfig Instance;
|
||||
private static Context APP;
|
||||
private DownloadConfig mDConfig;
|
||||
private UploadConfig mUConfig;
|
||||
private AppConfig mAConfig;
|
||||
private DGroupConfig mDGConfig;
|
||||
/**
|
||||
* 是否已经联网,true 已经联网
|
||||
*/
|
||||
private static boolean isConnectedNet = true;
|
||||
private Handler mAriaHandler;
|
||||
|
||||
private AriaConfig(Context context) {
|
||||
APP = context.getApplicationContext();
|
||||
}
|
||||
|
||||
public static AriaConfig init(Context context) {
|
||||
if (Instance == null) {
|
||||
synchronized (AriaConfig.class) {
|
||||
if (Instance == null) {
|
||||
Instance = new AriaConfig(context);
|
||||
Instance.initData();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public static AriaConfig getInstance() {
|
||||
if (Instance == null) {
|
||||
ALog.e(TAG, "请使用init()初始化");
|
||||
}
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public Context getAPP() {
|
||||
return APP;
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
initConfig();
|
||||
regNetCallBack(APP);
|
||||
}
|
||||
|
||||
public DownloadConfig getDConfig() {
|
||||
return mDConfig;
|
||||
}
|
||||
|
||||
public UploadConfig getUConfig() {
|
||||
return mUConfig;
|
||||
}
|
||||
|
||||
public AppConfig getAConfig() {
|
||||
return mAConfig;
|
||||
}
|
||||
|
||||
public DGroupConfig getDGConfig() {
|
||||
return mDGConfig;
|
||||
}
|
||||
|
||||
public synchronized Handler getAriaHandler() {
|
||||
if (mAriaHandler == null) {
|
||||
mAriaHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
return mAriaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册网络监听,只有配置了检查网络{@link AppConfig#isNetCheck()}才会注册事件
|
||||
*/
|
||||
private void regNetCallBack(Context context) {
|
||||
isConnectedNet = isNetworkAvailable();
|
||||
if (!getAConfig().isNetCheck()) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (cm == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||
NetworkRequest request = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||
.build();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() {
|
||||
|
||||
@Override public void onLost(Network network) {
|
||||
super.onLost(network);
|
||||
isConnectedNet = isNetworkAvailable();
|
||||
ALog.d(TAG, "onLost, isConnectNet = " + isConnectedNet);
|
||||
}
|
||||
|
||||
@Override public void onAvailable(Network network) {
|
||||
super.onAvailable(network);
|
||||
isConnectedNet = true;
|
||||
ALog.d(TAG, "onAvailable, isConnectNet = true");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNetworkAvailable() {
|
||||
// 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) getAPP().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (connectivityManager == null) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
// 获取NetworkInfo对象
|
||||
NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo();
|
||||
|
||||
if (networkInfo != null && networkInfo.length > 0) {
|
||||
for (NetworkInfo info : networkInfo) {
|
||||
// 判断当前网络状态是否为连接状态
|
||||
if (info.getState() == NetworkInfo.State.CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean isConnectedNet() {
|
||||
return isConnectedNet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置文件
|
||||
*/
|
||||
private void initConfig() {
|
||||
mDConfig = Configuration.getInstance().downloadCfg;
|
||||
mUConfig = Configuration.getInstance().uploadCfg;
|
||||
mAConfig = Configuration.getInstance().appCfg;
|
||||
mDGConfig = Configuration.getInstance().dGroupCfg;
|
||||
|
||||
File xmlFile = new File(APP.getFilesDir().getPath() + Configuration.XML_FILE);
|
||||
File tempDir = new File(APP.getFilesDir().getPath() + "/temp");
|
||||
if (!xmlFile.exists()) {
|
||||
loadConfig();
|
||||
} else {
|
||||
try {
|
||||
String md5Code = CommonUtil.getFileMD5(xmlFile);
|
||||
File file = new File(APP.getFilesDir().getPath() + "/temp.xml");
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
FileUtil.createFileFormInputStream(APP.getAssets().open("aria_config.xml"),
|
||||
file.getPath());
|
||||
if (!CommonUtil.checkMD5(md5Code, file) || !Configuration.getInstance().configExists()) {
|
||||
loadConfig();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (tempDir.exists()) {
|
||||
File newDir = new File(APP.getFilesDir().getPath() + AriaConfig.DOWNLOAD_TEMP_DIR);
|
||||
newDir.mkdirs();
|
||||
tempDir.renameTo(newDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置文件
|
||||
*/
|
||||
private void loadConfig() {
|
||||
try {
|
||||
XMLReader helper = new XMLReader();
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
SAXParser parser = factory.newSAXParser();
|
||||
parser.parse(APP.getAssets().open("aria_config.xml"), helper);
|
||||
FileUtil.createFileFormInputStream(APP.getAssets().open("aria_config.xml"),
|
||||
APP.getFilesDir().getPath() + Configuration.XML_FILE);
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
ALog.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/10/24.
|
||||
* ftp url 信息链接实体
|
||||
*/
|
||||
public class FtpUrlEntity implements Cloneable {
|
||||
/**
|
||||
* 如:ftp://127.0.0.1:21/download/AriaPrj.zip
|
||||
* remotePath便是:download/AriaPrj.zip
|
||||
*/
|
||||
public String remotePath;
|
||||
|
||||
public String account;
|
||||
|
||||
/**
|
||||
* 是否是ftps
|
||||
* {@code true}ftps协议的地址,{@code false}不是ftps协议的地址
|
||||
*/
|
||||
public boolean isFtps = false;
|
||||
|
||||
/**
|
||||
* 原始url
|
||||
*/
|
||||
public String url;
|
||||
|
||||
/**
|
||||
* ftp协议:ftp
|
||||
*/
|
||||
public String scheme;
|
||||
|
||||
/**
|
||||
* 登录的用户名
|
||||
*/
|
||||
public String user;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
public String password;
|
||||
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
public String port;
|
||||
|
||||
/**
|
||||
* 主机域名
|
||||
*/
|
||||
public String hostName;
|
||||
|
||||
/**
|
||||
* 是否需要登录
|
||||
*/
|
||||
public boolean needLogin = false;
|
||||
|
||||
/**
|
||||
* 有效的ip地址
|
||||
*/
|
||||
public InetAddress validAddr;
|
||||
|
||||
/**
|
||||
* 连接协议
|
||||
* {@link ProtocolType}
|
||||
*/
|
||||
public String protocol = ProtocolType.Default;
|
||||
|
||||
/**
|
||||
* 安全模式 true 隐式,false 显式
|
||||
*/
|
||||
public boolean isImplicit = true;
|
||||
|
||||
public IdEntity idEntity;
|
||||
|
||||
@Override public FtpUrlEntity clone() {
|
||||
FtpUrlEntity entity = null;
|
||||
try {
|
||||
entity = (FtpUrlEntity) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 证书信息
|
||||
*/
|
||||
public class IdEntity {
|
||||
|
||||
/**
|
||||
* 私钥证书路径
|
||||
*/
|
||||
public String prvKey;
|
||||
|
||||
/**
|
||||
* 私钥证书密码
|
||||
*/
|
||||
public String prvPass;
|
||||
|
||||
/**
|
||||
* 公钥证书路径
|
||||
*/
|
||||
public String pubKey;
|
||||
|
||||
/**
|
||||
* knowhost文件路径
|
||||
*/
|
||||
public String knowHost;
|
||||
|
||||
/**
|
||||
* ca 证书密码
|
||||
*/
|
||||
public String storePass;
|
||||
|
||||
/**
|
||||
* ca证书路径
|
||||
*/
|
||||
public String storePath;
|
||||
|
||||
/**
|
||||
* ca证书别名
|
||||
*/
|
||||
public String keyAlias;
|
||||
}
|
@ -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;
|
||||
|
||||
public interface ProtocolType {
|
||||
String Default = "TLS";
|
||||
String SSL = "SSL";
|
||||
String SSLv3 = "SSLv3";
|
||||
String TLS = "TLS";
|
||||
String TLSv1 = "TLSv1";
|
||||
String TLSv1_1 = "TLSv1.1";
|
||||
String TLSv1_2 = "TLSv1.2";
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.arialyy.aria.core.common.BaseOption;
|
||||
import com.arialyy.aria.core.inf.IEventHandler;
|
||||
import com.arialyy.aria.core.inf.IOptionConstant;
|
||||
import com.arialyy.aria.core.processor.FtpInterceptHandler;
|
||||
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
|
||||
import com.arialyy.aria.core.processor.IFtpUploadInterceptor;
|
||||
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
|
||||
import com.arialyy.aria.core.processor.IHttpFileNameAdapter;
|
||||
import com.arialyy.aria.core.processor.IKeyUrlConverter;
|
||||
import com.arialyy.aria.core.processor.ILiveTsUrlConverter;
|
||||
import com.arialyy.aria.core.processor.ITsMergeHandler;
|
||||
import com.arialyy.aria.core.processor.IVodTsUrlConverter;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 任务配置参数
|
||||
*
|
||||
* @author lyy
|
||||
* Date: 2019-09-10
|
||||
*/
|
||||
public class TaskOptionParams {
|
||||
|
||||
private static List<Class> PROCESSORES = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 普通参数
|
||||
*/
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 事件处理对象
|
||||
*/
|
||||
private Map<String, IEventHandler> handler = new HashMap<>();
|
||||
|
||||
static {
|
||||
PROCESSORES.add(FtpInterceptHandler.class);
|
||||
PROCESSORES.add(IBandWidthUrlConverter.class);
|
||||
PROCESSORES.add(IFtpUploadInterceptor.class);
|
||||
PROCESSORES.add(IHttpFileLenAdapter.class);
|
||||
PROCESSORES.add(IHttpFileNameAdapter.class);
|
||||
PROCESSORES.add(ILiveTsUrlConverter.class);
|
||||
PROCESSORES.add(ITsMergeHandler.class);
|
||||
PROCESSORES.add(IVodTsUrlConverter.class);
|
||||
PROCESSORES.add(IKeyUrlConverter.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置任务参数
|
||||
*
|
||||
* @param option 任务配置
|
||||
*/
|
||||
public void setParams(BaseOption option) {
|
||||
List<Field> fields = CommonUtil.getAllFields(option.getClass());
|
||||
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
|
||||
if (PROCESSORES.contains(field.getType())) {
|
||||
Object eventHandler = field.get(option);
|
||||
if (eventHandler != null) {
|
||||
setObjs(field.getName(), (IEventHandler) eventHandler);
|
||||
}
|
||||
} else {
|
||||
Object params = field.get(option);
|
||||
if (params != null) {
|
||||
setParams(field.getName(), params);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置普通参数
|
||||
*
|
||||
* @param key {@link IOptionConstant}
|
||||
*/
|
||||
public TaskOptionParams setParams(String key, Object value) {
|
||||
params.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对象参数
|
||||
*/
|
||||
public TaskOptionParams setObjs(String key, IEventHandler handler) {
|
||||
this.handler.put(key, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public Object getParam(String key) {
|
||||
return params.get(key);
|
||||
}
|
||||
|
||||
public IEventHandler getHandler(String key) {
|
||||
return handler.get(key);
|
||||
}
|
||||
|
||||
public Map<String, IEventHandler> getHandler() {
|
||||
return handler;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.orm.annotation.Ignore;
|
||||
import com.arialyy.aria.orm.annotation.NoNull;
|
||||
import com.arialyy.aria.orm.annotation.Unique;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by laoyuyu on 2018/3/21.
|
||||
* 任务上传或下载的任务记录
|
||||
*/
|
||||
public class TaskRecord extends DbEntity {
|
||||
//public static final int TYPE_HTTP_FTP = 0;
|
||||
//public static final int TYPE_M3U8_VOD = 1;
|
||||
//public static final int TYPE_M3U8_LIVE = 2;
|
||||
|
||||
@Ignore
|
||||
public List<ThreadRecord> threadRecords;
|
||||
|
||||
/**
|
||||
* 任务线程数
|
||||
*/
|
||||
public int threadNum;
|
||||
|
||||
/**
|
||||
* 任务文件路径
|
||||
*/
|
||||
public String filePath;
|
||||
|
||||
/**
|
||||
* 文件长度
|
||||
*/
|
||||
public long fileLength;
|
||||
|
||||
/**
|
||||
* 任务文件名
|
||||
*/
|
||||
@NoNull
|
||||
public String fileName;
|
||||
|
||||
/**
|
||||
* 是否是任务组的子任务记录
|
||||
* {@code true}是
|
||||
*/
|
||||
public boolean isGroupRecord = false;
|
||||
|
||||
/**
|
||||
* 下载任务组名
|
||||
*/
|
||||
public String dGroupHash;
|
||||
|
||||
/**
|
||||
* 上传组任务名,暂时没有用
|
||||
*/
|
||||
@Ignore
|
||||
@Deprecated
|
||||
public String uGroupHash;
|
||||
|
||||
/**
|
||||
* 是否是分块{@code true}是,{@code false} 不是
|
||||
*/
|
||||
public boolean isBlock = false;
|
||||
|
||||
/**
|
||||
* 任务类型
|
||||
* {@link ITaskWrapper}
|
||||
*/
|
||||
public int taskType = 0;
|
||||
|
||||
/**
|
||||
* m3u8文件码率
|
||||
*/
|
||||
public long bandWidth = 0;
|
||||
}
|
@ -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;
|
||||
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
|
||||
/**
|
||||
* Created by laoyuyu on 2018/5/8.
|
||||
* 任务的线程记录
|
||||
*/
|
||||
public class ThreadRecord extends DbEntity {
|
||||
|
||||
/**
|
||||
* 任务的文件路径,不是当前线程记录的的分块文件路径
|
||||
*/
|
||||
public String taskKey;
|
||||
|
||||
/**
|
||||
* 开始位置
|
||||
*/
|
||||
public long startLocation;
|
||||
|
||||
/**
|
||||
* 结束位置
|
||||
*/
|
||||
public long endLocation;
|
||||
|
||||
/**
|
||||
* 线程是否完成
|
||||
* {@code true}完成,{@code false}未完成
|
||||
*/
|
||||
public boolean isComplete = false;
|
||||
|
||||
/**
|
||||
* 线程id
|
||||
*/
|
||||
public int threadId = 0;
|
||||
|
||||
/**
|
||||
* 分块长度
|
||||
*/
|
||||
public long blockLen = 0;
|
||||
|
||||
/**
|
||||
* 线程类型
|
||||
* {@link ITaskWrapper}
|
||||
*/
|
||||
public int threadType = 0;
|
||||
|
||||
/**
|
||||
* ts文件的下载地址
|
||||
*/
|
||||
public String tsUrl;
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.orm.annotation.Default;
|
||||
import com.arialyy.aria.orm.annotation.Ignore;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/29.
|
||||
*/
|
||||
public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable, Serializable {
|
||||
/**
|
||||
* 速度
|
||||
*/
|
||||
@Ignore private long speed = 0;
|
||||
/**
|
||||
* 单位转换后的速度
|
||||
*/
|
||||
@Ignore private String convertSpeed;
|
||||
/**
|
||||
* 下载失败计数,每次开始都重置为0
|
||||
*/
|
||||
@Ignore private int failNum = 0;
|
||||
|
||||
/**
|
||||
* 剩余时间,单位:s
|
||||
*/
|
||||
@Ignore private int timeLeft = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
private String str;
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private long fileSize = 0;
|
||||
/**
|
||||
* 转换后的文件大小
|
||||
*/
|
||||
private String convertFileSize;
|
||||
|
||||
/**
|
||||
* 任务状态{@link IEntity}
|
||||
*/
|
||||
@Default("3")
|
||||
private int state = STATE_WAIT;
|
||||
/**
|
||||
* 当前下载进度
|
||||
*/
|
||||
private long currentProgress = 0;
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
private long completeTime;
|
||||
|
||||
/**
|
||||
* 进度百分比
|
||||
*/
|
||||
private int percent;
|
||||
|
||||
@Default("false")
|
||||
private boolean isComplete = false;
|
||||
|
||||
/**
|
||||
* 上一次停止时间
|
||||
*/
|
||||
private long stopTime = 0;
|
||||
|
||||
@Ignore
|
||||
private int netCode = 200;
|
||||
|
||||
public int getNetCode() {
|
||||
return netCode;
|
||||
}
|
||||
|
||||
public void setNetCode(int netCode) {
|
||||
this.netCode = netCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剩余时间,单位:s
|
||||
* 如果是m3u8任务,无法获取剩余时间;m2u8任务如果需要获取剩余时间,请设置文件长度{@link #setFileSize(long)}
|
||||
*/
|
||||
public int getTimeLeft() {
|
||||
return timeLeft;
|
||||
}
|
||||
|
||||
public void setTimeLeft(int timeLeft) {
|
||||
this.timeLeft = timeLeft;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
public void setComplete(boolean complete) {
|
||||
isComplete = complete;
|
||||
}
|
||||
|
||||
public String getConvertFileSize() {
|
||||
return convertFileSize;
|
||||
}
|
||||
|
||||
public void setConvertFileSize(String convertFileSize) {
|
||||
this.convertFileSize = convertFileSize;
|
||||
}
|
||||
|
||||
public int getFailNum() {
|
||||
return failNum;
|
||||
}
|
||||
|
||||
public void setFailNum(int failNum) {
|
||||
this.failNum = failNum;
|
||||
}
|
||||
|
||||
public long getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public void setSpeed(long speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
public String getConvertSpeed() {
|
||||
return convertSpeed;
|
||||
}
|
||||
|
||||
public void setConvertSpeed(String convertSpeed) {
|
||||
this.convertSpeed = convertSpeed;
|
||||
}
|
||||
|
||||
public String getStr() {
|
||||
return str;
|
||||
}
|
||||
|
||||
public void setStr(String str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public long getCurrentProgress() {
|
||||
return currentProgress;
|
||||
}
|
||||
|
||||
public void setCurrentProgress(long currentProgress) {
|
||||
this.currentProgress = currentProgress;
|
||||
}
|
||||
|
||||
public long getCompleteTime() {
|
||||
return completeTime;
|
||||
}
|
||||
|
||||
public void setCompleteTime(long completeTime) {
|
||||
this.completeTime = completeTime;
|
||||
}
|
||||
|
||||
public int getPercent() {
|
||||
return percent;
|
||||
}
|
||||
|
||||
public void setPercent(int percent) {
|
||||
this.percent = percent;
|
||||
}
|
||||
|
||||
public long getStopTime() {
|
||||
return stopTime;
|
||||
}
|
||||
|
||||
public void setStopTime(long stopTime) {
|
||||
this.stopTime = stopTime;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return getRowID();
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体唯一标识符
|
||||
* 下载实体:下载url
|
||||
* 上传实体:文件路径
|
||||
* 下载任务组:组名
|
||||
* ftp文件夹下载:下载url
|
||||
*/
|
||||
public abstract String getKey();
|
||||
|
||||
/**
|
||||
* 实体驱动的下载任务类型
|
||||
*
|
||||
* @return {@link ITaskWrapper#D_FTP}、{@link ITaskWrapper#D_FTP_DIR}、{@link
|
||||
* ITaskWrapper#U_HTTP}...
|
||||
*/
|
||||
public abstract int getTaskType();
|
||||
|
||||
public AbsEntity() {
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(this.rowID);
|
||||
dest.writeLong(this.speed);
|
||||
dest.writeString(this.convertSpeed);
|
||||
dest.writeInt(this.failNum);
|
||||
dest.writeString(this.str);
|
||||
dest.writeLong(this.fileSize);
|
||||
dest.writeString(this.convertFileSize);
|
||||
dest.writeInt(this.state);
|
||||
dest.writeLong(this.currentProgress);
|
||||
dest.writeLong(this.completeTime);
|
||||
dest.writeInt(this.percent);
|
||||
dest.writeByte(this.isComplete ? (byte) 1 : (byte) 0);
|
||||
dest.writeLong(this.stopTime);
|
||||
}
|
||||
|
||||
protected AbsEntity(Parcel in) {
|
||||
this.rowID = in.readLong();
|
||||
this.speed = in.readLong();
|
||||
this.convertSpeed = in.readString();
|
||||
this.failNum = in.readInt();
|
||||
this.str = in.readString();
|
||||
this.fileSize = in.readLong();
|
||||
this.convertFileSize = in.readString();
|
||||
this.state = in.readInt();
|
||||
this.currentProgress = in.readLong();
|
||||
this.completeTime = in.readLong();
|
||||
this.percent = in.readInt();
|
||||
this.isComplete = in.readByte() != 0;
|
||||
this.stopTime = in.readLong();
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.arialyy.aria.orm.annotation.Unique;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/3.
|
||||
*/
|
||||
public abstract class AbsGroupEntity extends AbsEntity implements Parcelable {
|
||||
/**
|
||||
* 组合任务等hash为: 为子任务地址相加的url的Md5
|
||||
* ftpdir为:ftpdir下载地址
|
||||
*/
|
||||
@Unique protected String groupHash;
|
||||
|
||||
/**
|
||||
* 任务组别名
|
||||
*/
|
||||
private String alias;
|
||||
|
||||
/**
|
||||
* 任务组下载文件的文件夹地址
|
||||
*/
|
||||
@Unique private String dirPath;
|
||||
|
||||
/**
|
||||
* 子任务url地址
|
||||
*/
|
||||
private List<String> urls = new ArrayList<>();
|
||||
|
||||
public String getDirPath() {
|
||||
return dirPath;
|
||||
}
|
||||
|
||||
public void setDirPath(String dirPath) {
|
||||
this.dirPath = dirPath;
|
||||
}
|
||||
|
||||
public List<String> getUrls() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
public void setUrls(List<String> urls) {
|
||||
this.urls = urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组合任务等hash为: 为子任务地址相加的url的Md5
|
||||
* ftpdir为:ftpdir下载地址
|
||||
*/
|
||||
public String getGroupHash() {
|
||||
return groupHash;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return groupHash;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public AbsGroupEntity() {
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(this.groupHash);
|
||||
dest.writeString(this.alias);
|
||||
}
|
||||
|
||||
protected AbsGroupEntity(Parcel in) {
|
||||
super(in);
|
||||
this.groupHash = in.readString();
|
||||
this.alias = in.readString();
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.annotation.Default;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/3.
|
||||
*/
|
||||
public abstract class AbsNormalEntity extends AbsEntity implements Parcelable {
|
||||
|
||||
/**
|
||||
* 服务器地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 是否是任务组里面的下载实体
|
||||
*/
|
||||
@Default("false")
|
||||
private boolean isGroupChild = false;
|
||||
|
||||
@Default("false")
|
||||
private boolean isRedirect = false; //是否重定向
|
||||
private String redirectUrl; //重定向链接
|
||||
|
||||
/**
|
||||
* 任务类型
|
||||
* {@link ITaskWrapper}
|
||||
*/
|
||||
private int taskType;
|
||||
|
||||
|
||||
public void setTaskType(int taskType) {
|
||||
this.taskType = taskType;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public boolean isGroupChild() {
|
||||
return isGroupChild;
|
||||
}
|
||||
|
||||
public void setGroupChild(boolean groupChild) {
|
||||
isGroupChild = groupChild;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public boolean isRedirect() {
|
||||
return isRedirect;
|
||||
}
|
||||
|
||||
public void setRedirect(boolean redirect) {
|
||||
isRedirect = redirect;
|
||||
}
|
||||
|
||||
public String getRedirectUrl() {
|
||||
return redirectUrl;
|
||||
}
|
||||
|
||||
public void setRedirectUrl(String redirectUrl) {
|
||||
this.redirectUrl = redirectUrl;
|
||||
}
|
||||
|
||||
public abstract String getFilePath();
|
||||
|
||||
public AbsNormalEntity() {
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(this.url);
|
||||
dest.writeString(this.fileName);
|
||||
dest.writeByte(this.isGroupChild ? (byte) 1 : (byte) 0);
|
||||
dest.writeByte(this.isRedirect ? (byte) 1 : (byte) 0);
|
||||
dest.writeString(this.redirectUrl);
|
||||
}
|
||||
|
||||
protected AbsNormalEntity(Parcel in) {
|
||||
super(in);
|
||||
this.url = in.readString();
|
||||
this.fileName = in.readString();
|
||||
this.isGroupChild = in.readByte() != 0;
|
||||
this.isRedirect = in.readByte() != 0;
|
||||
this.redirectUrl = in.readString();
|
||||
}
|
||||
}
|
@ -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.common;
|
||||
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
public abstract class BaseOption {
|
||||
protected final String TAG;
|
||||
|
||||
public BaseOption() {
|
||||
TAG = CommonUtil.getClassName(getClass());
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2018/3/3.
|
||||
* 获取文件信息完成后 回调给下载线程的信息
|
||||
*/
|
||||
public class CompleteInfo {
|
||||
/**
|
||||
* 自定义的状态码
|
||||
*/
|
||||
public int code;
|
||||
|
||||
public ITaskWrapper wrapper;
|
||||
|
||||
public Object obj;
|
||||
|
||||
public CompleteInfo() {
|
||||
|
||||
}
|
||||
|
||||
public CompleteInfo(int code, ITaskWrapper wrapper) {
|
||||
this.code = code;
|
||||
this.wrapper = wrapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.arialyy.aria.core.common;
|
||||
|
||||
public enum ErrorCode {
|
||||
ERROR_CODE_NORMAL(0, "正常"), ERROR_CODE_TASK_ID_NULL(1, "任务id为空的错误码"),
|
||||
ERROR_CODE_URL_NULL(2, "url 为空"), ERROR_CODE_URL_INVALID(3, "url 无效"),
|
||||
ERROR_CODE_PAGE_NUM(4, "page和num不能小于1"), ERROR_CODE_GROUP_URL_NULL(5, "组合任务url列表为空"),
|
||||
ERROR_CODE_UPLOAD_FILE_NULL(7, "上传文件不存在"),
|
||||
ERROR_CODE_MEMBER_WARNING(8, "为了防止内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"),
|
||||
ERROR_CODE_TASK_NOT_EXIST(9, "任务信息不存在");
|
||||
|
||||
public int code;
|
||||
public String msg;
|
||||
|
||||
ErrorCode(int code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* FTP 连接模式
|
||||
*/
|
||||
public interface FtpConnectionMode {
|
||||
/**
|
||||
* 被动模式
|
||||
*/
|
||||
int DATA_CONNECTION_MODE_PASV = 0;
|
||||
/**
|
||||
* 主动模式
|
||||
*/
|
||||
int DATA_CONNECTION_MODE_ACTIVITY = 1;
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.loader.ILoaderVisitor;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.RecordWrapper;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.DbDataHelper;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 处理任务记录,分配线程区间
|
||||
*/
|
||||
public abstract class RecordHandler implements IRecordHandler {
|
||||
protected final String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
@Deprecated private File mConfigFile;
|
||||
private TaskRecord mTaskRecord;
|
||||
private AbsTaskWrapper mTaskWrapper;
|
||||
private AbsNormalEntity mEntity;
|
||||
protected String mFilePath;
|
||||
protected long mFileSize;
|
||||
|
||||
public RecordHandler(AbsTaskWrapper wrapper) {
|
||||
mTaskWrapper = wrapper;
|
||||
mEntity = (AbsNormalEntity) mTaskWrapper.getEntity();
|
||||
}
|
||||
|
||||
public AbsTaskWrapper getWrapper() {
|
||||
return mTaskWrapper;
|
||||
}
|
||||
|
||||
public AbsNormalEntity getEntity() {
|
||||
return mEntity;
|
||||
}
|
||||
|
||||
@Override public void onPre() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务记录,如果任务记录存在,检查任务记录
|
||||
* 检查记录 对于分块任务: 子分块不存在或被删除,子线程将重新下载
|
||||
* 对于普通任务: 预下载文件不存在,则任务任务呗删除
|
||||
* 如果任务记录不存在或线程记录不存在,初始化记录
|
||||
*
|
||||
* @return 任务记录
|
||||
*/
|
||||
@Override
|
||||
public TaskRecord getRecord(long fileSize) {
|
||||
mFileSize = fileSize;
|
||||
mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName()));
|
||||
if (mConfigFile.exists()) {
|
||||
convertDb();
|
||||
} else {
|
||||
onPre();
|
||||
mTaskRecord = DbDataHelper.getTaskRecord(getFilePath(), mEntity.getTaskType());
|
||||
if (mTaskRecord == null) {
|
||||
initRecord(true);
|
||||
}else if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.size() == 0){
|
||||
if (mTaskRecord.threadRecords == null){
|
||||
mTaskRecord.threadRecords = new ArrayList<>();
|
||||
}
|
||||
initRecord(false);
|
||||
}
|
||||
handlerTaskRecord(mTaskRecord);
|
||||
}
|
||||
saveRecord();
|
||||
return mTaskRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* convertDb 是兼容性代码 从3.4.1开始,线程配置信息将存储在数据库中。 将配置文件的内容复制到数据库中,并将配置文件删除
|
||||
*/
|
||||
private void convertDb() {
|
||||
List<RecordWrapper> records =
|
||||
DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?",
|
||||
getFilePath());
|
||||
if (records == null || records.size() == 0) {
|
||||
Properties pro = FileUtil.loadConfig(mConfigFile);
|
||||
if (pro.isEmpty()) {
|
||||
ALog.d(TAG, "老版本的线程记录为空,任务为新任务");
|
||||
initRecord(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Object> keys = pro.keySet();
|
||||
// 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录...
|
||||
// 第一步应该是record 和 state去重取正确的线程数
|
||||
Set<Integer> set = new HashSet<>();
|
||||
for (Object key : keys) {
|
||||
String str = String.valueOf(key);
|
||||
int i = Integer.parseInt(str.substring(str.length() - 1));
|
||||
set.add(i);
|
||||
}
|
||||
int threadNum = set.size();
|
||||
if (threadNum == 0) {
|
||||
ALog.d(TAG, "线程数为空,任务为新任务");
|
||||
initRecord(true);
|
||||
return;
|
||||
}
|
||||
mTaskWrapper.setNewTask(false);
|
||||
mTaskRecord = createTaskRecord(threadNum);
|
||||
mTaskRecord.isBlock = false;
|
||||
File tempFile = new File(getFilePath());
|
||||
for (int i = 0; i < threadNum; i++) {
|
||||
ThreadRecord tRecord = new ThreadRecord();
|
||||
tRecord.taskKey = mTaskRecord.filePath;
|
||||
Object state = pro.getProperty(tempFile.getName() + STATE + i);
|
||||
Object record = pro.getProperty(tempFile.getName() + RECORD + i);
|
||||
if (state != null && Integer.parseInt(String.valueOf(state)) == 1) {
|
||||
tRecord.isComplete = true;
|
||||
continue;
|
||||
}
|
||||
if (record != null) {
|
||||
long temp = Long.parseLong(String.valueOf(record));
|
||||
tRecord.startLocation = temp > 0 ? temp : 0;
|
||||
} else {
|
||||
tRecord.startLocation = 0;
|
||||
}
|
||||
mTaskRecord.threadRecords.add(tRecord);
|
||||
}
|
||||
FileUtil.deleteFile(mConfigFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化任务记录,分配线程区间,如果任务记录不存在,则创建新的任务记录
|
||||
*
|
||||
* @param newRecord {@code true} 需要创建新{@link TaskRecord}
|
||||
*/
|
||||
private void initRecord(boolean newRecord) {
|
||||
if (newRecord) {
|
||||
mTaskRecord = createTaskRecord(initTaskThreadNum());
|
||||
}
|
||||
mTaskWrapper.setNewTask(true);
|
||||
int requestType = mTaskWrapper.getRequestType();
|
||||
if (requestType == ITaskWrapper.M3U8_LIVE) {
|
||||
return;
|
||||
}
|
||||
long blockSize = getFileSize() / mTaskRecord.threadNum;
|
||||
// 处理线程区间记录
|
||||
for (int i = 0; i < mTaskRecord.threadNum; i++) {
|
||||
long startL = i * blockSize, endL = (i + 1) * blockSize;
|
||||
ThreadRecord tr = createThreadRecord(mTaskRecord, i, startL, endL);
|
||||
mTaskRecord.threadRecords.add(tr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务记录
|
||||
*/
|
||||
private void saveRecord() {
|
||||
mTaskRecord.threadNum = mTaskRecord.threadRecords.size();
|
||||
mTaskRecord.save();
|
||||
if (mTaskRecord.threadRecords != null && !mTaskRecord.threadRecords.isEmpty()) {
|
||||
DbEntity.saveAll(mTaskRecord.threadRecords);
|
||||
}
|
||||
ALog.d(TAG, String.format("保存记录,线程记录数:%s", mTaskRecord.threadRecords.size()));
|
||||
}
|
||||
|
||||
protected long getFileSize() {
|
||||
return mFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务路径
|
||||
*
|
||||
* @return 任务文件路径
|
||||
*/
|
||||
private String getFilePath() {
|
||||
if (mEntity instanceof DownloadEntity) {
|
||||
return ((DownloadEntity) mTaskWrapper.getEntity()).getFilePath();
|
||||
} else {
|
||||
return ((UploadEntity) mTaskWrapper.getEntity()).getFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
|
||||
@Override public boolean checkTaskCompleted() {
|
||||
if (mTaskRecord == null
|
||||
|| mTaskRecord.threadRecords == null
|
||||
|| mTaskRecord.threadRecords.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
int completeNum = 0;
|
||||
for (ThreadRecord tr : mTaskRecord.threadRecords) {
|
||||
if (tr.isComplete) {
|
||||
completeNum++;
|
||||
}
|
||||
}
|
||||
return completeNum != 0 && completeNum == mTaskRecord.threadNum;
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 任务记录帮助类,用于处理统一的逻辑
|
||||
*
|
||||
* @author lyy
|
||||
* Date: 2019-09-19
|
||||
*/
|
||||
public class RecordHelper {
|
||||
private String TAG = CommonUtil.getClassName(getClass());
|
||||
|
||||
private AbsTaskWrapper mWrapper;
|
||||
protected TaskRecord mTaskRecord;
|
||||
|
||||
public RecordHelper(AbsTaskWrapper wrapper, TaskRecord record) {
|
||||
mWrapper = wrapper;
|
||||
mTaskRecord = record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理非分块的,多线程任务
|
||||
*/
|
||||
public void handleMultiRecord() {
|
||||
// 默认线程分块长度
|
||||
long blockSize = mWrapper.getEntity().getFileSize() / mTaskRecord.threadRecords.size();
|
||||
File temp = new File(mTaskRecord.filePath);
|
||||
boolean fileExists = false;
|
||||
if (!temp.exists()) {
|
||||
createPlaceHolderFile(temp);
|
||||
} else {
|
||||
if (temp.length() != mWrapper.getEntity().getFileSize()) {
|
||||
FileUtil.deleteFile(temp);
|
||||
createPlaceHolderFile(temp);
|
||||
}
|
||||
fileExists = true;
|
||||
}
|
||||
// 处理文件被删除的情况
|
||||
if (!fileExists) {
|
||||
ALog.w(TAG, String.format("文件【%s】被删除,重新分配线程区间", mTaskRecord.filePath));
|
||||
for (int i = 0; i < mTaskRecord.threadNum; i++) {
|
||||
long startL = i * blockSize, endL = (i + 1) * blockSize;
|
||||
ThreadRecord tr = mTaskRecord.threadRecords.get(i);
|
||||
tr.startLocation = startL;
|
||||
tr.isComplete = false;
|
||||
|
||||
//最后一个线程的结束位置即为文件的总长度
|
||||
if (tr.threadId == (mTaskRecord.threadNum - 1)) {
|
||||
endL = mWrapper.getEntity().getFileSize();
|
||||
}
|
||||
tr.endLocation = endL;
|
||||
}
|
||||
}
|
||||
//mWrapper.setNewTask(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建非分块的占位文件
|
||||
*/
|
||||
private void createPlaceHolderFile(File temp) {
|
||||
BufferedRandomAccessFile tempFile;
|
||||
try {
|
||||
tempFile = new BufferedRandomAccessFile(temp, "rw");
|
||||
tempFile.setLength(mWrapper.getEntity().getFileSize());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度
|
||||
*/
|
||||
public void handleBlockRecord() {
|
||||
// 默认线程分块长度
|
||||
long normalRectLen = mWrapper.getEntity().getFileSize() / mTaskRecord.threadRecords.size();
|
||||
for (ThreadRecord tr : mTaskRecord.threadRecords) {
|
||||
long threadRect = tr.blockLen;
|
||||
|
||||
File temp =
|
||||
new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, tr.threadId));
|
||||
if (!temp.exists()) {
|
||||
ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getPath()));
|
||||
tr.isComplete = false;
|
||||
tr.startLocation = tr.threadId * normalRectLen;
|
||||
} else {
|
||||
if (!tr.isComplete) {
|
||||
ALog.i(TAG, String.format(
|
||||
"startLocation = %s; endLocation = %s; block = %s; tempLen = %s; threadId = %s",
|
||||
tr.startLocation, tr.endLocation, threadRect, temp.length(), tr.threadId));
|
||||
|
||||
long blockFileLen = temp.length(); // 磁盘中的分块文件长度
|
||||
/*
|
||||
* 检查磁盘中的分块文件
|
||||
*/
|
||||
if (blockFileLen > threadRect) {
|
||||
ALog.i(TAG, String.format("分块【%s】错误,分块长度【%s】 > 线程区间长度【%s】,将重新开始该分块",
|
||||
tr.threadId, blockFileLen, threadRect));
|
||||
temp.delete();
|
||||
tr.startLocation = tr.threadId * threadRect;
|
||||
continue;
|
||||
}
|
||||
|
||||
//正常情况下,该线程的startLocation的位置
|
||||
long realLocation = tr.threadId * normalRectLen + blockFileLen;
|
||||
/*
|
||||
* 检查记录文件
|
||||
*/
|
||||
if (blockFileLen == threadRect && blockFileLen != 0) {
|
||||
ALog.i(TAG, String.format("分块【%s】已完成,更新记录", temp.getPath()));
|
||||
tr.startLocation = blockFileLen;
|
||||
tr.isComplete = true;
|
||||
} else if (tr.startLocation != realLocation) { // 处理记录小于分块文件长度的情况
|
||||
ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation));
|
||||
tr.startLocation = realLocation;
|
||||
} else {
|
||||
ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation));
|
||||
tr.startLocation = realLocation;
|
||||
tr.isComplete = false;
|
||||
}
|
||||
} else {
|
||||
ALog.i(TAG, String.format("分块【%s】已完成", temp.getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
//mWrapper.setNewTask(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单线程的任务的记录
|
||||
*/
|
||||
public void handleSingleThreadRecord() {
|
||||
// mTaskRecord.isBlock是为了兼容以前的文件格式
|
||||
File file = new File(
|
||||
mTaskRecord.isBlock ? String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)
|
||||
: mTaskRecord.filePath);
|
||||
ThreadRecord tr = mTaskRecord.threadRecords.get(0);
|
||||
if (!file.exists()) {
|
||||
// 目标文件
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
// 处理组合任务其中一个子任务完成的情况
|
||||
if (tr.isComplete
|
||||
&& targetFile.exists()
|
||||
&& targetFile.length() != 0
|
||||
&& targetFile.length() == mWrapper.getEntity().getFileSize()) {
|
||||
tr.isComplete = true;
|
||||
} else {
|
||||
ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath()));
|
||||
tr.startLocation = 0;
|
||||
tr.isComplete = false;
|
||||
tr.endLocation = mWrapper.getEntity().getFileSize();
|
||||
}
|
||||
} else if (file.length() > mWrapper.getEntity().getFileSize()) {
|
||||
ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath()));
|
||||
FileUtil.deleteFile(file);
|
||||
tr.startLocation = 0;
|
||||
tr.isComplete = false;
|
||||
tr.endLocation = mWrapper.getEntity().getFileSize();
|
||||
} else if (file.length() != 0 && file.length() == mWrapper.getEntity().getFileSize()) {
|
||||
ALog.d(TAG, "文件长度一致,线程完成");
|
||||
tr.isComplete = true;
|
||||
} else {
|
||||
if (file.length() != tr.startLocation) {
|
||||
ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length()));
|
||||
tr.startLocation = file.length();
|
||||
tr.isComplete = false;
|
||||
}
|
||||
}
|
||||
//mWrapper.setNewTask(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理不支持断点的记录
|
||||
*/
|
||||
public void handleNoSupportBPRecord() {
|
||||
ThreadRecord tr = mTaskRecord.threadRecords.get(0);
|
||||
tr.startLocation = 0;
|
||||
tr.endLocation = mWrapper.getEntity().getFileSize();
|
||||
tr.taskKey = mTaskRecord.filePath;
|
||||
tr.blockLen = tr.endLocation;
|
||||
tr.isComplete = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/1/23.
|
||||
* url请求方式,目前支持GET、POST
|
||||
*/
|
||||
public enum RequestEnum {
|
||||
GET("GET"), POST("POST");
|
||||
|
||||
public String name;
|
||||
|
||||
RequestEnum(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 子线程下载信息类
|
||||
*/
|
||||
public class SubThreadConfig {
|
||||
public static final int TYPE_HTTP = 1;
|
||||
public static final int TYPE_FTP = 2;
|
||||
public static final int TYPE_M3U8_PEER = 3;
|
||||
public static final int TYPE_HTTP_DG_SUB = 4;
|
||||
public static final int TYPE_FTP_DG_SUB = 5;
|
||||
|
||||
public AbsTaskWrapper taskWrapper;
|
||||
public boolean isBlock = false;
|
||||
// 启动的线程
|
||||
public int startThreadNum;
|
||||
// 真正的下载地址,如果是30x,则是30x后的地址
|
||||
public String url;
|
||||
public File tempFile;
|
||||
// 线程记录
|
||||
public ThreadRecord record;
|
||||
// 状态处理器
|
||||
public Handler stateHandler;
|
||||
// m3u8切片索引
|
||||
public int peerIndex;
|
||||
// 线程任务类型
|
||||
public int threadType = TYPE_HTTP;
|
||||
// 进度更新间隔,单位:毫秒
|
||||
public long updateInterval = 1000;
|
||||
// 扩展数据
|
||||
public Object obj;
|
||||
// 忽略失败
|
||||
public boolean ignoreFailure = false;
|
||||
|
||||
/**
|
||||
* 转换线程任务类型
|
||||
*
|
||||
* @param requestType {@link AbsTaskWrapper#getRequestType()}
|
||||
* @return {@link #threadType}
|
||||
*/
|
||||
public static int getThreadType(int requestType) {
|
||||
int threadType = SubThreadConfig.TYPE_HTTP;
|
||||
switch (requestType) {
|
||||
case ITaskWrapper.D_HTTP:
|
||||
case ITaskWrapper.U_HTTP:
|
||||
threadType = SubThreadConfig.TYPE_HTTP;
|
||||
break;
|
||||
case ITaskWrapper.D_FTP:
|
||||
case ITaskWrapper.U_FTP:
|
||||
threadType = SubThreadConfig.TYPE_FTP;
|
||||
break;
|
||||
case ITaskWrapper.D_FTP_DIR:
|
||||
threadType = SubThreadConfig.TYPE_FTP_DG_SUB;
|
||||
break;
|
||||
case ITaskWrapper.DG_HTTP:
|
||||
threadType = SubThreadConfig.TYPE_HTTP_DG_SUB;
|
||||
break;
|
||||
case ITaskWrapper.M3U8_LIVE:
|
||||
case ITaskWrapper.M3U8_VOD:
|
||||
threadType = SubThreadConfig.TYPE_M3U8_PEER;
|
||||
break;
|
||||
}
|
||||
return threadType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置肚脐更新间隔
|
||||
*
|
||||
* @param requestType {@link AbsTaskWrapper#getRequestType()}
|
||||
* @return {@link #updateInterval}
|
||||
*/
|
||||
public static long getUpdateInterval(int requestType) {
|
||||
long updateInterval = 1000;
|
||||
switch (requestType) {
|
||||
case ITaskWrapper.D_HTTP:
|
||||
case ITaskWrapper.D_FTP:
|
||||
case ITaskWrapper.M3U8_LIVE:
|
||||
case ITaskWrapper.M3U8_VOD:
|
||||
updateInterval = AriaConfig.getInstance().getDConfig().getUpdateInterval();
|
||||
break;
|
||||
case ITaskWrapper.D_FTP_DIR:
|
||||
case ITaskWrapper.DG_HTTP:
|
||||
updateInterval = AriaConfig.getInstance().getDGConfig().getUpdateInterval();
|
||||
break;
|
||||
case ITaskWrapper.U_HTTP:
|
||||
case ITaskWrapper.U_FTP:
|
||||
updateInterval = AriaConfig.getInstance().getUConfig().getUpdateInterval();
|
||||
}
|
||||
return updateInterval;
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.AriaCrashHandler;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 应用配置
|
||||
*/
|
||||
public class AppConfig extends BaseConfig implements Serializable {
|
||||
/**
|
||||
* 是否使用{@link AriaCrashHandler}来捕获异常 {@code true} 使用;{@code false} 不使用
|
||||
*/
|
||||
boolean useAriaCrashHandler;
|
||||
|
||||
/**
|
||||
* 设置Aria的日志级别
|
||||
*
|
||||
* {@link ALog#LOG_LEVEL_VERBOSE}
|
||||
*/
|
||||
int logLevel;
|
||||
|
||||
/**
|
||||
* 是否检查网络,{@code true}检查网络
|
||||
*/
|
||||
boolean netCheck = true;
|
||||
|
||||
/**
|
||||
* 是否使用广播 除非无法使用注解,否则不建议使用广播来接受任务 {@code true} 使用广播,{@code false} 不适用广播
|
||||
*/
|
||||
boolean useBroadcast = false;
|
||||
|
||||
/**
|
||||
* 断网的时候是否重试,{@code true}断网也重试;{@code false}断网不重试,直接走失败的回调
|
||||
*/
|
||||
boolean notNetRetry = false;
|
||||
|
||||
public boolean isNotNetRetry() {
|
||||
return notNetRetry;
|
||||
}
|
||||
|
||||
public AppConfig setNotNetRetry(boolean notNetRetry) {
|
||||
this.notNetRetry = notNetRetry;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isUseBroadcast() {
|
||||
return useBroadcast;
|
||||
}
|
||||
|
||||
public AppConfig setUseBroadcast(boolean useBroadcast) {
|
||||
this.useBroadcast = useBroadcast;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNetCheck() {
|
||||
return netCheck;
|
||||
}
|
||||
|
||||
public AppConfig setNetCheck(boolean netCheck) {
|
||||
this.netCheck = netCheck;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppConfig setLogLevel(int level) {
|
||||
this.logLevel = level;
|
||||
ALog.LOG_LEVEL = level;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public boolean getUseAriaCrashHandler() {
|
||||
return useAriaCrashHandler;
|
||||
}
|
||||
|
||||
public AppConfig setUseAriaCrashHandler(boolean useAriaCrashHandler) {
|
||||
this.useAriaCrashHandler = useAriaCrashHandler;
|
||||
if (useAriaCrashHandler) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new AriaCrashHandler());
|
||||
} else {
|
||||
Thread.setDefaultUncaughtExceptionHandler(null);
|
||||
}
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override int getType() {
|
||||
return TYPE_APP;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.arialyy.aria.core.config;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.Serializable;
|
||||
|
||||
abstract class BaseConfig implements Serializable {
|
||||
private static final String TAG = "BaseConfig";
|
||||
static final int TYPE_DOWNLOAD = 1;
|
||||
static final int TYPE_UPLOAD = 2;
|
||||
static final int TYPE_APP = 3;
|
||||
static final int TYPE_DGROUP = 4;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* @return {@link #TYPE_DOWNLOAD}、{@link #TYPE_UPLOAD}、{@link #TYPE_APP}、{@link #TYPE_DGROUP}
|
||||
*/
|
||||
abstract int getType();
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
*/
|
||||
void save() {
|
||||
String basePath = AriaConfig.getInstance().getAPP().getFilesDir().getPath();
|
||||
String path = null;
|
||||
switch (getType()) {
|
||||
case TYPE_DOWNLOAD:
|
||||
path = Configuration.DOWNLOAD_CONFIG_FILE;
|
||||
break;
|
||||
case TYPE_UPLOAD:
|
||||
path = Configuration.UPLOAD_CONFIG_FILE;
|
||||
break;
|
||||
case TYPE_APP:
|
||||
path = Configuration.APP_CONFIG_FILE;
|
||||
break;
|
||||
case TYPE_DGROUP:
|
||||
path = Configuration.DGROUP_CONFIG_FILE;
|
||||
break;
|
||||
}
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
String tempPath = String.format("%s%s", basePath, path);
|
||||
FileUtil.deleteFile(tempPath);
|
||||
FileUtil.writeObjToFile(tempPath, this);
|
||||
} else {
|
||||
ALog.e(TAG, String.format("保存配置失败,配置类型:%s,原因:路径错误", getType()));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 通用任务配置
|
||||
*/
|
||||
public abstract class BaseTaskConfig extends BaseConfig implements Serializable {
|
||||
protected String TAG = CommonUtil.getClassName(getClass());
|
||||
|
||||
/**
|
||||
* 设置写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢
|
||||
*/
|
||||
int buffSize = 8192;
|
||||
|
||||
/**
|
||||
* 进度刷新间隔,默认1秒
|
||||
*/
|
||||
long updateInterval = 1000;
|
||||
|
||||
/**
|
||||
* 旧任务数
|
||||
*/
|
||||
public int oldMaxTaskNum = 2;
|
||||
|
||||
/**
|
||||
* 任务队列最大任务数, 默认为2
|
||||
*/
|
||||
int maxTaskNum = 2;
|
||||
/**
|
||||
* 下载失败,重试次数,默认为10
|
||||
*/
|
||||
int reTryNum = 10;
|
||||
/**
|
||||
* 设置重试间隔,单位为毫秒,默认2000毫秒
|
||||
*/
|
||||
int reTryInterval = 2000;
|
||||
/**
|
||||
* 设置url连接超时时间,单位为毫秒,默认5000毫秒
|
||||
*/
|
||||
int connectTimeOut = 5000;
|
||||
|
||||
/**
|
||||
* 是否需要转换速度单位,转换完成后为:1b/s、1k/s、1m/s、1g/s、1t/s,如果不需要将返回byte长度
|
||||
*/
|
||||
boolean isConvertSpeed = false;
|
||||
|
||||
/**
|
||||
* 执行队列类型
|
||||
*/
|
||||
String queueMod = "wait";
|
||||
|
||||
/**
|
||||
* 设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒
|
||||
*/
|
||||
int iOTimeOut = 20 * 1000;
|
||||
|
||||
/**
|
||||
* 设置最大下载/上传速度,单位:kb, 为0表示不限速
|
||||
*/
|
||||
int maxSpeed = 0;
|
||||
|
||||
/**
|
||||
* 设置https ca 证书信息;path 为assets目录下的CA证书完整路径
|
||||
*/
|
||||
String caPath;
|
||||
/**
|
||||
* name 为CA证书名
|
||||
*/
|
||||
String caName;
|
||||
|
||||
public String getCaPath() {
|
||||
return caPath;
|
||||
}
|
||||
|
||||
public BaseConfig setCaPath(String caPath) {
|
||||
this.caPath = caPath;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCaName() {
|
||||
return caName;
|
||||
}
|
||||
|
||||
public BaseConfig setCaName(String caName) {
|
||||
this.caName = caName;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setMaxTaskNum(int maxTaskNum) {
|
||||
oldMaxTaskNum = this.maxTaskNum;
|
||||
this.maxTaskNum = maxTaskNum;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMaxSpeed() {
|
||||
return maxSpeed;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setMaxSpeed(int maxSpeed) {
|
||||
this.maxSpeed = maxSpeed;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getUpdateInterval() {
|
||||
return updateInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度更新间隔,该设置对正在运行的任务无效,默认为1000毫秒
|
||||
*
|
||||
* @param updateInterval 不能小于0
|
||||
*/
|
||||
public BaseTaskConfig setUpdateInterval(long updateInterval) {
|
||||
if (updateInterval <= 0) {
|
||||
ALog.w("Configuration", "进度更新间隔不能小于0");
|
||||
return this;
|
||||
}
|
||||
this.updateInterval = updateInterval;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getQueueMod() {
|
||||
return queueMod;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setQueueMod(String queueMod) {
|
||||
this.queueMod = queueMod;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMaxTaskNum() {
|
||||
return maxTaskNum;
|
||||
}
|
||||
|
||||
public int getReTryNum() {
|
||||
return reTryNum;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setReTryNum(int reTryNum) {
|
||||
this.reTryNum = reTryNum;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getReTryInterval() {
|
||||
return reTryInterval;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setReTryInterval(int reTryInterval) {
|
||||
this.reTryInterval = reTryInterval;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isConvertSpeed() {
|
||||
return isConvertSpeed;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setConvertSpeed(boolean convertSpeed) {
|
||||
isConvertSpeed = convertSpeed;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getConnectTimeOut() {
|
||||
return connectTimeOut;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setConnectTimeOut(int connectTimeOut) {
|
||||
this.connectTimeOut = connectTimeOut;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getIOTimeOut() {
|
||||
return iOTimeOut;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setIOTimeOut(int iOTimeOut) {
|
||||
this.iOTimeOut = iOTimeOut;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBuffSize() {
|
||||
return buffSize;
|
||||
}
|
||||
|
||||
public BaseTaskConfig setBuffSize(int buffSize) {
|
||||
this.buffSize = buffSize;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
public interface ConfigType {
|
||||
int DOWNLOAD = 1;
|
||||
int UPLOAD = 2;
|
||||
int APP = 3;
|
||||
int D_GROUP = 4;
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/12/8. 信息配置 kotlin 方式有bug,不能将public去掉
|
||||
*/
|
||||
public final class Configuration {
|
||||
private static final String TAG = "Configuration";
|
||||
private static volatile Configuration INSTANCE = null;
|
||||
public static final String XML_FILE = "/Aria/aria_config.xml";
|
||||
static final String DOWNLOAD_CONFIG_FILE = "/Aria/AriaDownload.cfg";
|
||||
static final String UPLOAD_CONFIG_FILE = "/Aria/AriaUpload.cfg";
|
||||
static final String APP_CONFIG_FILE = "/Aria/AriaApp.cfg";
|
||||
static final String DGROUP_CONFIG_FILE = "/Aria/AriaDGroup.cfg";
|
||||
public DownloadConfig downloadCfg;
|
||||
public UploadConfig uploadCfg;
|
||||
public AppConfig appCfg;
|
||||
public DGroupConfig dGroupCfg;
|
||||
|
||||
private Configuration() {
|
||||
//删除老版本的配置文件
|
||||
String basePath = AriaConfig.getInstance().getAPP().getFilesDir().getPath();
|
||||
del351Config(basePath);
|
||||
File newDCfg = new File(String.format("%s%s", basePath, DOWNLOAD_CONFIG_FILE));
|
||||
File newUCfg = new File(String.format("%s%s", basePath, UPLOAD_CONFIG_FILE));
|
||||
File newACfg = new File(String.format("%s%s", basePath, APP_CONFIG_FILE));
|
||||
File dgCfg = new File(String.format("%s%s", basePath, DGROUP_CONFIG_FILE));
|
||||
// 加载下载配置
|
||||
if (newDCfg.exists()) {
|
||||
downloadCfg = (DownloadConfig) FileUtil.readObjFromFile(newDCfg.getPath());
|
||||
}
|
||||
if (downloadCfg == null) {
|
||||
downloadCfg = new DownloadConfig();
|
||||
}
|
||||
// 加载上传配置
|
||||
if (newUCfg.exists()) {
|
||||
uploadCfg = (UploadConfig) FileUtil.readObjFromFile(newUCfg.getPath());
|
||||
}
|
||||
if (uploadCfg == null) {
|
||||
uploadCfg = new UploadConfig();
|
||||
}
|
||||
// 加载app配置
|
||||
if (newACfg.exists()) {
|
||||
appCfg = (AppConfig) FileUtil.readObjFromFile(newACfg.getPath());
|
||||
}
|
||||
if (appCfg == null) {
|
||||
appCfg = new AppConfig();
|
||||
}
|
||||
// 加载下载类型组合任务的配置
|
||||
if (dgCfg.exists()) {
|
||||
dGroupCfg = (DGroupConfig) FileUtil.readObjFromFile(dgCfg.getPath());
|
||||
}
|
||||
if (dGroupCfg == null) {
|
||||
dGroupCfg = new DGroupConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public static Configuration getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (AppConfig.class) {
|
||||
INSTANCE = new Configuration();
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置文件是否存在,只要{@link DownloadConfig}、{@link UploadConfig}、{@link AppConfig}、{@link
|
||||
* DGroupConfig}其中一个不存在 则任务配置文件不存在
|
||||
*
|
||||
* @return {@code true}配置存在,{@code false}配置不存在
|
||||
*/
|
||||
public boolean configExists() {
|
||||
String basePath = AriaConfig.getInstance().getAPP().getFilesDir().getPath();
|
||||
return (new File(String.format("%s%s", basePath, DOWNLOAD_CONFIG_FILE))).exists()
|
||||
&& (new File(String.format("%s%s", basePath, UPLOAD_CONFIG_FILE))).exists()
|
||||
&& (new File(String.format("%s%s", basePath, APP_CONFIG_FILE))).exists()
|
||||
&& (new File(String.format("%s%s", basePath, DGROUP_CONFIG_FILE))).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除3.5.2之前版本的配置文件,从3.5.2开始,配置文件的保存不再使用properties文件
|
||||
*/
|
||||
private void del351Config(String basePath) {
|
||||
File oldDCfg = new File(String.format("%s/Aria/DownloadConfig.properties", basePath));
|
||||
if (oldDCfg.exists()) { // 只需要判断一个
|
||||
File oldUCfg = new File(String.format("%s/Aria/UploadConfig.properties", basePath));
|
||||
File oldACfg = new File(String.format("%s/Aria/AppConfig.properties", basePath));
|
||||
oldDCfg.delete();
|
||||
oldUCfg.delete();
|
||||
oldACfg.delete();
|
||||
// 删除配置触发更新
|
||||
File temp = new File(String.format("%s%s", basePath, XML_FILE));
|
||||
if (temp.exists()) {
|
||||
temp.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.event.DGMaxNumEvent;
|
||||
import com.arialyy.aria.core.event.DSpeedEvent;
|
||||
import com.arialyy.aria.core.event.EventMsgUtil;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 下载类型的组合任务
|
||||
*/
|
||||
public class DGroupConfig extends BaseTaskConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* 能同时下载的子任务最大任务数,默认3
|
||||
*/
|
||||
int subMaxTaskNum = 3;
|
||||
|
||||
/**
|
||||
* 子任务失败时回调stop,默认true
|
||||
*/
|
||||
private boolean subFailAsStop = true;
|
||||
|
||||
/**
|
||||
* 子任务重试次数,默认为5
|
||||
*/
|
||||
int subReTryNum = 5;
|
||||
|
||||
/**
|
||||
* 子任务下载失败时的重试间隔,单位为毫秒,默认2000毫秒-
|
||||
*/
|
||||
int subReTryInterval = 2000;
|
||||
|
||||
private DownloadConfig subConfig;
|
||||
|
||||
DGroupConfig() {
|
||||
getSubConfig();
|
||||
}
|
||||
|
||||
@Override int getType() {
|
||||
return TYPE_DGROUP;
|
||||
}
|
||||
|
||||
@Override public DGroupConfig setMaxSpeed(int maxSpeed) {
|
||||
super.setMaxSpeed(maxSpeed);
|
||||
EventMsgUtil.getDefault().post(new DSpeedEvent(maxSpeed));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadConfig getSubConfig() {
|
||||
subConfig = AriaConfig.getInstance().getDConfig();
|
||||
return subConfig;
|
||||
}
|
||||
|
||||
public DGroupConfig setMaxTaskNum(int maxTaskNum) {
|
||||
if (maxTaskNum <= 0) {
|
||||
ALog.e(TAG, "组合任务最大任务数不能小于0");
|
||||
return this;
|
||||
}
|
||||
super.setMaxTaskNum(maxTaskNum);
|
||||
EventMsgUtil.getDefault().post(new DGMaxNumEvent(maxTaskNum));
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getSubMaxTaskNum() {
|
||||
return subMaxTaskNum;
|
||||
}
|
||||
|
||||
public DGroupConfig setSubMaxTaskNum(int subMaxTaskNum) {
|
||||
this.subMaxTaskNum = subMaxTaskNum;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getSubReTryNum() {
|
||||
return subReTryNum;
|
||||
}
|
||||
|
||||
public DGroupConfig setSubReTryNum(int subReTryNum) {
|
||||
this.subReTryNum = subReTryNum;
|
||||
subConfig.reTryNum = subReTryNum;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getSubReTryInterval() {
|
||||
return subReTryInterval;
|
||||
}
|
||||
|
||||
public DGroupConfig setSubReTryInterval(int subReTryInterval) {
|
||||
this.subReTryInterval = subReTryInterval;
|
||||
subConfig.reTryInterval = subReTryInterval;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSubFailAsStop() {
|
||||
return subFailAsStop;
|
||||
}
|
||||
|
||||
public DGroupConfig setSubFailAsStop(boolean subFailAsStop) {
|
||||
this.subFailAsStop = subFailAsStop;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.arialyy.aria.core.event.DMaxNumEvent;
|
||||
import com.arialyy.aria.core.event.DSpeedEvent;
|
||||
import com.arialyy.aria.core.event.EventMsgUtil;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 下载配置
|
||||
*/
|
||||
public class DownloadConfig extends BaseTaskConfig implements Serializable {
|
||||
/**
|
||||
* 下载线程数,下载线程数不能小于1
|
||||
* 注意:
|
||||
* 1、线程下载数改变后,新的下载任务才会生效;
|
||||
* 2、如果任务大小小于1m,该设置不会生效;
|
||||
* 3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间;
|
||||
* 对于采用多线程的任务或旧任务,依然采用原来的文件空间占用方式;
|
||||
*/
|
||||
int threadNum = 3;
|
||||
|
||||
/**
|
||||
* 多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用
|
||||
* 注意:
|
||||
* 1、使用分块模式,在读写性能底下的手机上,合并文件需要的时间会更加长;
|
||||
* 2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间;
|
||||
* 3、只对新的多线程下载任务有效 4、只对多线程的任务有效
|
||||
*/
|
||||
boolean useBlock = true;
|
||||
|
||||
/**
|
||||
* 设置http下载获取文件大小是否使用Head请求。true:使用head请求,false:使用默认的get请求
|
||||
*/
|
||||
boolean useHeadRequest = false;
|
||||
|
||||
public boolean isUseHeadRequest() {
|
||||
return useHeadRequest;
|
||||
}
|
||||
|
||||
public DownloadConfig setUseHeadRequest(boolean useHeadRequest) {
|
||||
this.useHeadRequest = useHeadRequest;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isUseBlock() {
|
||||
return useBlock;
|
||||
}
|
||||
|
||||
@Override public DownloadConfig setMaxSpeed(int maxSpeed) {
|
||||
super.setMaxSpeed(maxSpeed);
|
||||
EventMsgUtil.getDefault().post(new DSpeedEvent(maxSpeed));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadConfig setUseBlock(boolean useBlock) {
|
||||
this.useBlock = useBlock;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadConfig setMaxTaskNum(int maxTaskNum) {
|
||||
if (maxTaskNum <= 0) {
|
||||
ALog.e(TAG, "下载任务最大任务数不能小于0");
|
||||
return this;
|
||||
}
|
||||
super.setMaxTaskNum(maxTaskNum);
|
||||
EventMsgUtil.getDefault().post(new DMaxNumEvent(maxTaskNum));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadConfig setThreadNum(int threadNum) {
|
||||
this.threadNum = threadNum;
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getThreadNum() {
|
||||
return threadNum;
|
||||
}
|
||||
|
||||
DownloadConfig() {
|
||||
}
|
||||
|
||||
@Override int getType() {
|
||||
return TYPE_DOWNLOAD;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.arialyy.aria.core.config;
|
||||
|
||||
public class TTaskConfigAdapeter {
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.arialyy.aria.core.event.EventMsgUtil;
|
||||
import com.arialyy.aria.core.event.UMaxNumEvent;
|
||||
import com.arialyy.aria.core.event.USpeedEvent;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 上传配置
|
||||
*/
|
||||
public class UploadConfig extends BaseTaskConfig implements Serializable {
|
||||
|
||||
UploadConfig() {
|
||||
}
|
||||
|
||||
@Override public UploadConfig setMaxSpeed(int maxSpeed) {
|
||||
super.setMaxSpeed(maxSpeed);
|
||||
EventMsgUtil.getDefault().post(new USpeedEvent(maxSpeed));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UploadConfig setMaxTaskNum(int maxTaskNum) {
|
||||
if (maxTaskNum <= 0){
|
||||
ALog.e(TAG, "上传任务最大任务数不能小于0");
|
||||
return this;
|
||||
}
|
||||
super.setMaxTaskNum(maxTaskNum);
|
||||
EventMsgUtil.getDefault().post(new UMaxNumEvent(maxTaskNum));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override int getType() {
|
||||
return TYPE_UPLOAD;
|
||||
}
|
||||
}
|
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/5/22. 读取配置文件
|
||||
*/
|
||||
public class XMLReader extends DefaultHandler {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
private DownloadConfig mDownloadConfig = Configuration.getInstance().downloadCfg;
|
||||
private UploadConfig mUploadConfig = Configuration.getInstance().uploadCfg;
|
||||
private AppConfig mAppConfig = Configuration.getInstance().appCfg;
|
||||
private DGroupConfig mDGroupConfig = Configuration.getInstance().dGroupCfg;
|
||||
private int mType;
|
||||
|
||||
@Override public void startDocument() throws SAXException {
|
||||
super.startDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes)
|
||||
throws SAXException {
|
||||
super.startElement(uri, localName, qName, attributes);
|
||||
switch (qName) {
|
||||
case "download":
|
||||
mType = ConfigType.DOWNLOAD;
|
||||
break;
|
||||
case "upload":
|
||||
mType = ConfigType.UPLOAD;
|
||||
break;
|
||||
case "app":
|
||||
mType = ConfigType.APP;
|
||||
break;
|
||||
case "dGroup":
|
||||
mType = ConfigType.D_GROUP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mType == ConfigType.DOWNLOAD || mType == ConfigType.UPLOAD || mType == ConfigType.D_GROUP) {
|
||||
|
||||
String value = attributes.getValue("value");
|
||||
switch (qName) {
|
||||
case "threadNum": // 线程数
|
||||
int threadNum = checkInt(value) ? Integer.parseInt(value) : 3;
|
||||
if (threadNum < 1) {
|
||||
ALog.w(TAG, "下载线程数不能小于 1");
|
||||
threadNum = 1;
|
||||
}
|
||||
setField("threadNum", threadNum, ConfigType.DOWNLOAD);
|
||||
break;
|
||||
case "maxTaskNum": //最大任务书
|
||||
int maxTaskNum = checkInt(value) ? Integer.parseInt(value) : 2;
|
||||
if (maxTaskNum < 1) {
|
||||
ALog.w(TAG, "任务队列数不能小于 1");
|
||||
maxTaskNum = 2;
|
||||
}
|
||||
setField("maxTaskNum", maxTaskNum, mType);
|
||||
break;
|
||||
case "reTryNum": //任务重试次数
|
||||
setField("reTryNum", checkInt(value) ? Integer.parseInt(value) : 0, mType);
|
||||
break;
|
||||
case "connectTimeOut": // 连接超时时间
|
||||
setField("connectTimeOut", checkInt(value) ? Integer.parseInt(value) : 5 * 1000,
|
||||
mType);
|
||||
break;
|
||||
case "iOTimeOut": //io流超时时间
|
||||
int iOTimeOut = checkInt(value) ? Integer.parseInt(value) : 10 * 1000;
|
||||
if (iOTimeOut < 10 * 1000) {
|
||||
iOTimeOut = 10 * 1000;
|
||||
}
|
||||
setField("iOTimeOut", iOTimeOut, mType);
|
||||
break;
|
||||
case "reTryInterval": //失败重试间隔
|
||||
int reTryInterval = checkInt(value) ? Integer.parseInt(value) : 2 * 1000;
|
||||
|
||||
if (reTryInterval < 2 * 1000) {
|
||||
reTryInterval = 2 * 1000;
|
||||
}
|
||||
setField("reTryInterval", reTryInterval, mType);
|
||||
break;
|
||||
case "buffSize": //缓冲大小
|
||||
int buffSize = checkInt(value) ? Integer.parseInt(value) : 8192;
|
||||
|
||||
if (buffSize < 2048) {
|
||||
buffSize = 2048;
|
||||
}
|
||||
|
||||
setField("buffSize", buffSize, mType);
|
||||
break;
|
||||
case "ca": // ca证书
|
||||
String caName = attributes.getValue("name");
|
||||
String caPath = attributes.getValue("path");
|
||||
setField("caName", caName, mType);
|
||||
setField("caPath", caPath, mType);
|
||||
break;
|
||||
case "convertSpeed": // 是否转换速度
|
||||
setField("isConvertSpeed", !checkBoolean(value) || Boolean.parseBoolean(value),
|
||||
mType);
|
||||
break;
|
||||
case "maxSpeed": // 最大速度
|
||||
int maxSpeed = checkInt(value) ? Integer.parseInt(value) : 0;
|
||||
setField("maxSpeed", maxSpeed, mType);
|
||||
break;
|
||||
case "queueMod": // 队列类型
|
||||
String mod = "now";
|
||||
if (!TextUtils.isEmpty(value) && (value.equalsIgnoreCase("now") || value.equalsIgnoreCase(
|
||||
"wait"))) {
|
||||
mod = value;
|
||||
}
|
||||
setField("queueMod", mod, mType);
|
||||
break;
|
||||
case "updateInterval": // 进度更新时间
|
||||
setField("updateInterval", checkLong(value) ? Long.parseLong(value) : 1000,
|
||||
mType);
|
||||
break;
|
||||
|
||||
case "useBlock": // 是否使用分块任务
|
||||
setField("useBlock", checkBoolean(value) ? Boolean.valueOf(value) : false,
|
||||
ConfigType.DOWNLOAD);
|
||||
break;
|
||||
case "subMaxTaskNum": // 子任务最大任务数
|
||||
int subMaxTaskNum = checkInt(value) ? Integer.parseInt(value) : 3;
|
||||
setField("subMaxTaskNum", subMaxTaskNum, ConfigType.D_GROUP);
|
||||
break;
|
||||
case "subFailAsStop": // 子任务失败时回调stop
|
||||
setField("subFailAsStop", checkBoolean(value) ? Boolean.valueOf(value) : false,
|
||||
ConfigType.D_GROUP);
|
||||
break;
|
||||
case "subReTryNum": // 子任务重试次数
|
||||
int subReTryNum = checkInt(value) ? Integer.parseInt(value) : 5;
|
||||
setField("subReTryNum", subReTryNum, ConfigType.D_GROUP);
|
||||
break;
|
||||
case "subReTryInterval": // 子任务重试间隔
|
||||
int subReTryInterval = checkInt(value) ? Integer.parseInt(value) : 2000;
|
||||
setField("subReTryInterval", subReTryInterval, ConfigType.D_GROUP);
|
||||
break;
|
||||
case "useHeadRequest": // 是否使用head请求
|
||||
boolean useHeadRequest = checkBoolean(value) ? Boolean.valueOf(value) : false;
|
||||
setField("useHeadRequest", useHeadRequest, ConfigType.DOWNLOAD);
|
||||
break;
|
||||
}
|
||||
} else if (mType == ConfigType.APP) {
|
||||
String value = attributes.getValue("value");
|
||||
switch (qName) {
|
||||
case "useAriaCrashHandler": // 是否捕捉崩溃日志
|
||||
setField("useAriaCrashHandler", checkBoolean(value) ? Boolean.valueOf(value) : true,
|
||||
ConfigType.APP);
|
||||
break;
|
||||
case "logLevel": // 日记等级
|
||||
int level = checkInt(value) ? Integer.parseInt(value) : ALog.LOG_LEVEL_VERBOSE;
|
||||
if (level < ALog.LOG_LEVEL_VERBOSE || level > ALog.LOG_CLOSE) {
|
||||
ALog.w(TAG, "level【" + level + "】错误");
|
||||
level = ALog.LOG_LEVEL_VERBOSE;
|
||||
}
|
||||
setField("logLevel", level, ConfigType.APP);
|
||||
break;
|
||||
case "netCheck": // 是否检查网络
|
||||
setField("netCheck", checkBoolean(value) ? Boolean.valueOf(value) : false,
|
||||
ConfigType.APP);
|
||||
break;
|
||||
case "useBroadcast": // 是否使用广播
|
||||
setField("useBroadcast", checkBoolean(value) ? Boolean.valueOf(value) : false,
|
||||
ConfigType.APP);
|
||||
break;
|
||||
case "notNetRetry": // 没有网络也重试
|
||||
setField("notNetRetry", checkBoolean(value) ? Boolean.valueOf(value) : false,
|
||||
ConfigType.APP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setField(String key, Object value, int type) {
|
||||
if (type == ConfigType.DOWNLOAD) {
|
||||
setField(DownloadConfig.class, mDownloadConfig, key, value);
|
||||
} else if (type == ConfigType.UPLOAD) {
|
||||
setField(UploadConfig.class, mUploadConfig, key, value);
|
||||
} else if (type == ConfigType.APP) {
|
||||
setField(AppConfig.class, mAppConfig, key, value);
|
||||
} else if (type == ConfigType.D_GROUP) {
|
||||
setField(DGroupConfig.class, mDGroupConfig, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setField(Class clazz, Object target, String key, Object value) {
|
||||
Field field = CommonUtil.getField(clazz, key);
|
||||
try {
|
||||
field.set(target, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否int值是否合法
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
private boolean checkInt(String value) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int l = Integer.parseInt(value);
|
||||
return l >= 0;
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否long值是否合法
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
private boolean checkLong(String value) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Long l = Long.parseLong(value);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查boolean值是否合法
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
private boolean checkBoolean(String value) {
|
||||
return !TextUtils.isEmpty(value) && (value.equalsIgnoreCase("true") || value.equalsIgnoreCase(
|
||||
"false"));
|
||||
}
|
||||
|
||||
@Override public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
super.characters(ch, start, length);
|
||||
}
|
||||
|
||||
@Override public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
super.endElement(uri, localName, qName);
|
||||
}
|
||||
|
||||
@Override public void endDocument() throws SAXException {
|
||||
super.endDocument();
|
||||
mDownloadConfig.save();
|
||||
mUploadConfig.save();
|
||||
mAppConfig.save();
|
||||
mDGroupConfig.save();
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.common.AbsEntity;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组合任务实体包裹器,用于加载和任务相关的参数,如:组合任务实体{@link DownloadGroupEntity}
|
||||
*/
|
||||
public abstract class AbsGroupTaskWrapper<ENTITY extends AbsEntity, SUB extends AbsTaskWrapper>
|
||||
extends AbsTaskWrapper<ENTITY> {
|
||||
|
||||
public AbsGroupTaskWrapper(ENTITY entity) {
|
||||
super(entity);
|
||||
}
|
||||
|
||||
public abstract List<SUB> getSubTaskWrapper();
|
||||
|
||||
public abstract void setSubTaskWrapper(List<SUB> subTaskWrapper);
|
||||
|
||||
/**
|
||||
* {@code true} 忽略任务冲突,不考虑组任务hash冲突的情况
|
||||
*/
|
||||
private boolean ignoreTaskOccupy = false;
|
||||
|
||||
public boolean isIgnoreTaskOccupy() {
|
||||
return ignoreTaskOccupy;
|
||||
}
|
||||
|
||||
public void setIgnoreTaskOccupy(boolean ignoreTaskOccupy) {
|
||||
this.ignoreTaskOccupy = ignoreTaskOccupy;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
import com.arialyy.aria.orm.AbsDbWrapper;
|
||||
import com.arialyy.aria.orm.annotation.Many;
|
||||
import com.arialyy.aria.orm.annotation.One;
|
||||
import com.arialyy.aria.orm.annotation.Wrapper;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by laoyuyu on 2018/3/30.
|
||||
* 任务组实体和子任务实体的关系
|
||||
*/
|
||||
@Wrapper
|
||||
public class DGEntityWrapper extends AbsDbWrapper {
|
||||
|
||||
@One
|
||||
public DownloadGroupEntity groupEntity;
|
||||
|
||||
@Many(parentColumn = "groupHash", entityColumn = "groupHash")
|
||||
public List<DownloadEntity> subEntity;
|
||||
|
||||
@Override protected void handleConvert() {
|
||||
if (subEntity != null && !subEntity.isEmpty()) {
|
||||
groupEntity.setSubEntities(subEntity);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.config.Configuration;
|
||||
import com.arialyy.aria.core.config.DGroupConfig;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/7/1. 任务组的任务实体修饰器
|
||||
*/
|
||||
public class DGTaskWrapper extends AbsGroupTaskWrapper<DownloadGroupEntity, DTaskWrapper> {
|
||||
|
||||
private List<DTaskWrapper> subWrappers;
|
||||
|
||||
private boolean unknownSize = false;
|
||||
|
||||
/**
|
||||
* 保存临时设置的文件夹路径
|
||||
*/
|
||||
private String dirPathTemp;
|
||||
|
||||
/**
|
||||
* 子任务文件名
|
||||
*/
|
||||
private List<String> subNameTemp = new ArrayList<>();
|
||||
|
||||
public DGTaskWrapper(DownloadGroupEntity entity) {
|
||||
super(entity);
|
||||
}
|
||||
|
||||
public List<String> getSubNameTemp() {
|
||||
return subNameTemp;
|
||||
}
|
||||
|
||||
public void setSubNameTemp(List<String> subNameTemp) {
|
||||
this.subNameTemp = subNameTemp;
|
||||
}
|
||||
|
||||
public String getDirPathTemp() {
|
||||
return dirPathTemp;
|
||||
}
|
||||
|
||||
public void setDirPathTemp(String mDirPathTemp) {
|
||||
this.dirPathTemp = mDirPathTemp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubTaskWrapper(List<DTaskWrapper> subTaskEntities) {
|
||||
this.subWrappers = subTaskEntities;
|
||||
}
|
||||
|
||||
public boolean isUnknownSize() {
|
||||
return unknownSize;
|
||||
}
|
||||
|
||||
public void setUnknownSize(boolean unknownSize) {
|
||||
this.unknownSize = unknownSize;
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return getEntity().getKey();
|
||||
}
|
||||
|
||||
@Override public DGroupConfig getConfig() {
|
||||
return Configuration.getInstance().dGroupCfg;
|
||||
}
|
||||
|
||||
@Override public List<DTaskWrapper> getSubTaskWrapper() {
|
||||
if (subWrappers == null) {
|
||||
subWrappers = new ArrayList<>();
|
||||
}
|
||||
return subWrappers;
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.TaskOptionParams;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.config.DownloadConfig;
|
||||
import com.arialyy.aria.core.inf.ITaskOption;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ComponentUtil;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/1/23. 下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除
|
||||
*/
|
||||
public class DTaskWrapper extends AbsTaskWrapper<DownloadEntity> {
|
||||
|
||||
/**
|
||||
* 所属的任务组组名,如果不属于任务组,则为null
|
||||
*/
|
||||
private String groupHash;
|
||||
|
||||
/**
|
||||
* 该任务是否属于任务组
|
||||
*/
|
||||
private boolean isGroupTask = false;
|
||||
|
||||
/**
|
||||
* M3u8任务配置信息
|
||||
*/
|
||||
private ITaskOption m3u8Option;
|
||||
|
||||
private TaskOptionParams m3u8Params = new TaskOptionParams();
|
||||
|
||||
/**
|
||||
* 文件下载url的临时保存变量
|
||||
*/
|
||||
private String mTempUrl;
|
||||
|
||||
/**
|
||||
* 文件保存路径的临时变量
|
||||
*/
|
||||
private String mTempFilePath;
|
||||
|
||||
public DTaskWrapper(DownloadEntity entity) {
|
||||
super(entity);
|
||||
}
|
||||
|
||||
public ITaskOption getM3u8Option() {
|
||||
return m3u8Option;
|
||||
}
|
||||
|
||||
public void generateM3u8Option(Class<? extends ITaskOption> clazz) {
|
||||
m3u8Option = ComponentUtil.getInstance().buildTaskOption(clazz, m3u8Params);
|
||||
}
|
||||
|
||||
public TaskOptionParams getM3U8Params() {
|
||||
if (m3u8Params == null) {
|
||||
m3u8Params = new TaskOptionParams();
|
||||
}
|
||||
return m3u8Params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task实体对应的key,下载url
|
||||
*/
|
||||
@Override public String getKey() {
|
||||
return getEntity().getKey();
|
||||
}
|
||||
|
||||
@Override public DownloadConfig getConfig() {
|
||||
if (isGroupTask) {
|
||||
return Configuration.getInstance().dGroupCfg.getSubConfig();
|
||||
} else {
|
||||
return Configuration.getInstance().downloadCfg;
|
||||
}
|
||||
}
|
||||
|
||||
public String getGroupHash() {
|
||||
return groupHash;
|
||||
}
|
||||
|
||||
public boolean isGroupTask() {
|
||||
return isGroupTask;
|
||||
}
|
||||
|
||||
public void setGroupHash(String groupHash) {
|
||||
this.groupHash = groupHash;
|
||||
}
|
||||
|
||||
public void setGroupTask(boolean groupTask) {
|
||||
isGroupTask = groupTask;
|
||||
}
|
||||
|
||||
public String getTempUrl() {
|
||||
return mTempUrl;
|
||||
}
|
||||
|
||||
public void setTempUrl(String mTempUrl) {
|
||||
this.mTempUrl = mTempUrl;
|
||||
}
|
||||
|
||||
public String getTempFilePath() {
|
||||
return mTempFilePath;
|
||||
}
|
||||
|
||||
public void setTempFilePath(String mTempFilePath) {
|
||||
this.mTempFilePath = mTempFilePath;
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.orm.annotation.Ignore;
|
||||
import com.arialyy.aria.orm.annotation.Unique;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2015/12/25.
|
||||
* 下载实体
|
||||
*/
|
||||
public class DownloadEntity extends AbsNormalEntity implements Parcelable, Cloneable {
|
||||
@Unique private String downloadPath; //保存路径
|
||||
|
||||
/**
|
||||
* 所属任务组
|
||||
*/
|
||||
private String groupHash;
|
||||
|
||||
/**
|
||||
* 从服务器的返回信息中获取的文件md5信息,如果服务器没有返回,则不会设置该信息
|
||||
* 如果你已经设置了该任务的MD5信息,Aria也不会从服务器返回的信息中获取该信息
|
||||
*/
|
||||
private String md5Code;
|
||||
|
||||
/**
|
||||
* 从服务器的返回信息中获取的文件描述信息
|
||||
*/
|
||||
private String disposition;
|
||||
|
||||
/**
|
||||
* 从disposition获取到的文件名,如果可以获取到,则会赋值到这个字段
|
||||
*/
|
||||
private String serverFileName;
|
||||
|
||||
@Ignore
|
||||
private M3U8Entity m3U8Entity;
|
||||
|
||||
/**
|
||||
* 获取m3u8数据信息
|
||||
*
|
||||
* @return 如果m3u8信息为空,则返回null
|
||||
*/
|
||||
public M3U8Entity getM3U8Entity() {
|
||||
if (TextUtils.isEmpty(downloadPath)) {
|
||||
ALog.e("DownloadEntity", "文件保存路径为空,获取m3u8实体之前需要设置文件保存路径");
|
||||
return null;
|
||||
}
|
||||
if (m3U8Entity == null) {
|
||||
m3U8Entity = DbEntity.findFirst(M3U8Entity.class, "filePath=?", downloadPath);
|
||||
}
|
||||
return m3U8Entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进来的地址,如果需要获取真实的下载地址,请使用{@link #getRealUrl()}
|
||||
*/
|
||||
@Override public String getKey() {
|
||||
return getUrl();
|
||||
}
|
||||
|
||||
public String getRealUrl(){
|
||||
return isRedirect() ? getRedirectUrl() : getUrl();
|
||||
}
|
||||
|
||||
@Override public int getTaskType() {
|
||||
int type;
|
||||
if (getUrl() == null) {
|
||||
type = ITaskWrapper.ERROR;
|
||||
} else if (getUrl().startsWith("http")) {
|
||||
M3U8Entity temp = getM3U8Entity();
|
||||
if (temp == null) {
|
||||
type = ITaskWrapper.D_HTTP;
|
||||
} else {
|
||||
type = temp.isLive() ? ITaskWrapper.M3U8_LIVE : ITaskWrapper.M3U8_VOD;
|
||||
}
|
||||
} else if (getUrl().startsWith("ftp")) {
|
||||
type = ITaskWrapper.D_FTP;
|
||||
} else if (getUrl().startsWith("sftp")) {
|
||||
type = ITaskWrapper.D_SFTP;
|
||||
} else {
|
||||
type = ITaskWrapper.ERROR;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public DownloadEntity() {
|
||||
}
|
||||
|
||||
public String getMd5Code() {
|
||||
return md5Code;
|
||||
}
|
||||
|
||||
public void setMd5Code(String md5Code) {
|
||||
this.md5Code = md5Code;
|
||||
}
|
||||
|
||||
public String getDisposition() {
|
||||
return TextUtils.isEmpty(disposition) ? "" : CommonUtil.decryptBASE64(disposition);
|
||||
}
|
||||
|
||||
public void setDisposition(String disposition) {
|
||||
this.disposition = disposition;
|
||||
}
|
||||
|
||||
public String getServerFileName() {
|
||||
return serverFileName;
|
||||
}
|
||||
|
||||
public void setServerFileName(String serverFileName) {
|
||||
this.serverFileName = serverFileName;
|
||||
}
|
||||
|
||||
public String getGroupHash() {
|
||||
return groupHash;
|
||||
}
|
||||
|
||||
public void setGroupHash(String groupHash) {
|
||||
this.groupHash = groupHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilePath() {
|
||||
return downloadPath;
|
||||
}
|
||||
|
||||
public DownloadEntity setFilePath(String filePath) {
|
||||
this.downloadPath = filePath;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public DownloadEntity clone() throws CloneNotSupportedException {
|
||||
return (DownloadEntity) super.clone();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "DownloadEntity{"
|
||||
+ "downloadPath='"
|
||||
+ downloadPath
|
||||
+ '\''
|
||||
+ ", groupHash='"
|
||||
+ groupHash
|
||||
+ '\''
|
||||
+ ", fileName='"
|
||||
+ getFileName()
|
||||
+ '\''
|
||||
+ ", md5Code='"
|
||||
+ md5Code
|
||||
+ '\''
|
||||
+ ", disposition='"
|
||||
+ disposition
|
||||
+ '\''
|
||||
+ ", serverFileName='"
|
||||
+ serverFileName
|
||||
+ '\''
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(this.downloadPath);
|
||||
dest.writeString(this.groupHash);
|
||||
dest.writeString(this.md5Code);
|
||||
dest.writeString(this.disposition);
|
||||
dest.writeString(this.serverFileName);
|
||||
dest.writeParcelable(this.m3U8Entity, flags);
|
||||
}
|
||||
|
||||
protected DownloadEntity(Parcel in) {
|
||||
super(in);
|
||||
this.downloadPath = in.readString();
|
||||
this.groupHash = in.readString();
|
||||
this.md5Code = in.readString();
|
||||
this.disposition = in.readString();
|
||||
this.serverFileName = in.readString();
|
||||
this.m3U8Entity = in.readParcelable(M3U8Entity.class.getClassLoader());
|
||||
}
|
||||
|
||||
public static final Creator<DownloadEntity> CREATOR = new Creator<DownloadEntity>() {
|
||||
@Override public DownloadEntity createFromParcel(Parcel source) {
|
||||
return new DownloadEntity(source);
|
||||
}
|
||||
|
||||
@Override public DownloadEntity[] newArray(int size) {
|
||||
return new DownloadEntity[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.common.AbsGroupEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.annotation.Ignore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/29. 下载任务组实体
|
||||
*/
|
||||
public class DownloadGroupEntity extends AbsGroupEntity {
|
||||
|
||||
@Ignore private List<DownloadEntity> subEntities;
|
||||
|
||||
/**
|
||||
* 子任务实体列表
|
||||
*/
|
||||
public List<DownloadEntity> getSubEntities() {
|
||||
if (subEntities == null) {
|
||||
subEntities = new ArrayList<>();
|
||||
}
|
||||
return subEntities;
|
||||
}
|
||||
|
||||
public void setSubEntities(List<DownloadEntity> subTasks) {
|
||||
this.subEntities = subTasks;
|
||||
}
|
||||
|
||||
public void setGroupHash(String key) {
|
||||
this.groupHash = key;
|
||||
}
|
||||
|
||||
@Override public int getTaskType() {
|
||||
if (getSubEntities() == null || getSubEntities().isEmpty() || TextUtils.isEmpty(
|
||||
getSubEntities().get(0).getUrl())) {
|
||||
return ITaskWrapper.ERROR;
|
||||
}
|
||||
return (groupHash.startsWith("ftp") || groupHash.startsWith("sftp")) ? ITaskWrapper.D_FTP_DIR
|
||||
: ITaskWrapper.DG_HTTP;
|
||||
}
|
||||
|
||||
public DownloadGroupEntity() {
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeTypedList(this.subEntities);
|
||||
}
|
||||
|
||||
protected DownloadGroupEntity(Parcel in) {
|
||||
super(in);
|
||||
this.subEntities = in.createTypedArrayList(DownloadEntity.CREATOR);
|
||||
}
|
||||
|
||||
public static final Creator<DownloadGroupEntity> CREATOR = new Creator<DownloadGroupEntity>() {
|
||||
@Override public DownloadGroupEntity createFromParcel(Parcel source) {
|
||||
return new DownloadGroupEntity(source);
|
||||
}
|
||||
|
||||
@Override public DownloadGroupEntity[] newArray(int size) {
|
||||
return new DownloadGroupEntity[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.orm.annotation.Default;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.DbDataHelper;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* M3U8实体信息
|
||||
*/
|
||||
public class M3U8Entity extends DbEntity implements Parcelable {
|
||||
|
||||
/**
|
||||
* 文件保存路径
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 当前peer的位置
|
||||
*/
|
||||
private int peerIndex;
|
||||
|
||||
/**
|
||||
* peer总数
|
||||
*/
|
||||
private int peerNum;
|
||||
|
||||
/**
|
||||
* 是否是直播,true 直播
|
||||
*/
|
||||
private boolean isLive;
|
||||
|
||||
/**
|
||||
* 缓存目录
|
||||
*/
|
||||
private String cacheDir;
|
||||
|
||||
/**
|
||||
* 加密key保存地址
|
||||
*/
|
||||
public String keyPath;
|
||||
|
||||
/**
|
||||
* 加密key的下载地址
|
||||
*/
|
||||
public String keyUrl;
|
||||
|
||||
/**
|
||||
* 加密算法
|
||||
*/
|
||||
public String method;
|
||||
|
||||
/**
|
||||
* key的iv值
|
||||
*/
|
||||
public String iv;
|
||||
|
||||
/**
|
||||
* key的格式,可能为空
|
||||
*/
|
||||
public String keyFormat;
|
||||
|
||||
/**
|
||||
* key的格式版本,默认为1,如果是多个版本,使用"/"分隔,如:"1", "1/2", or "1/2/5"
|
||||
*/
|
||||
@Default("1")
|
||||
public String keyFormatVersion = "1";
|
||||
|
||||
public String getKeyFormat() {
|
||||
return keyFormat;
|
||||
}
|
||||
|
||||
public void setKeyFormat(String keyFormat) {
|
||||
this.keyFormat = keyFormat;
|
||||
}
|
||||
|
||||
public String getKeyFormatVersion() {
|
||||
return keyFormatVersion;
|
||||
}
|
||||
|
||||
public void setKeyFormatVersion(String keyFormatVersion) {
|
||||
this.keyFormatVersion = keyFormatVersion;
|
||||
}
|
||||
|
||||
public String getKeyPath() {
|
||||
return keyPath;
|
||||
}
|
||||
|
||||
public void setKeyPath(String keyPath) {
|
||||
this.keyPath = keyPath;
|
||||
}
|
||||
|
||||
public String getKeyUrl() {
|
||||
return keyUrl;
|
||||
}
|
||||
|
||||
public void setKeyUrl(String keyUrl) {
|
||||
this.keyUrl = keyUrl;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getIv() {
|
||||
return iv;
|
||||
}
|
||||
|
||||
public void setIv(String iv) {
|
||||
this.iv = iv;
|
||||
}
|
||||
|
||||
public boolean isLive() {
|
||||
return isLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取m3u8切片
|
||||
* 如果任务未完成,则返回所有已下载完成的切片;
|
||||
* 如果任务已完成,如果你设置了合并分块的请求,返回null;如果没有设置该请求,则返回所有已下载完成的切片
|
||||
*/
|
||||
public List<PeerInfo> getCompletedPeer() {
|
||||
if (TextUtils.isEmpty(getCacheDir())) {
|
||||
ALog.w("M3U8Entity", "任务未下载,获取切片失败");
|
||||
return null;
|
||||
}
|
||||
List<PeerInfo> peers = new ArrayList<>();
|
||||
TaskRecord taskRecord = DbDataHelper.getTaskRecord(filePath,
|
||||
isLive ? ITaskWrapper.M3U8_LIVE : ITaskWrapper.M3U8_VOD);
|
||||
File cacheDir = new File(getCacheDir());
|
||||
if ((taskRecord == null
|
||||
|| taskRecord.threadRecords == null
|
||||
|| taskRecord.threadRecords.isEmpty())
|
||||
&& !cacheDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 处理任务完成的情况
|
||||
if (taskRecord == null
|
||||
|| taskRecord.threadRecords == null
|
||||
|| taskRecord.threadRecords.isEmpty()
|
||||
&& cacheDir.exists()) {
|
||||
String[] files = cacheDir.list(new FilenameFilter() {
|
||||
@Override public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".ts");
|
||||
}
|
||||
});
|
||||
for (String fileName : files) {
|
||||
|
||||
PeerInfo peerInfo =
|
||||
new PeerInfo(Integer.parseInt(fileName.substring(0, fileName.lastIndexOf(".ts"))),
|
||||
getCacheDir().concat("/").concat(fileName));
|
||||
peers.add(peerInfo);
|
||||
}
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
// 任务未完成的情况
|
||||
if (taskRecord.threadRecords != null
|
||||
&& !taskRecord.threadRecords.isEmpty()
|
||||
&& cacheDir.exists()) {
|
||||
|
||||
for (ThreadRecord tr : taskRecord.threadRecords) {
|
||||
if (!tr.isComplete) {
|
||||
continue;
|
||||
}
|
||||
String peerPath = String.format("%s/%s.ts", cacheDir, tr.threadId);
|
||||
if (new File(peerPath).exists()) {
|
||||
PeerInfo peerInfo = new PeerInfo(tr.threadId, peerPath);
|
||||
peers.add(peerInfo);
|
||||
}
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCacheDir() {
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
public void setCacheDir(String cacheDir) {
|
||||
this.cacheDir = cacheDir;
|
||||
}
|
||||
|
||||
public void setLive(boolean live) {
|
||||
isLive = live;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public int getPeerIndex() {
|
||||
return peerIndex;
|
||||
}
|
||||
|
||||
public void setPeerIndex(int peerIndex) {
|
||||
this.peerIndex = peerIndex;
|
||||
}
|
||||
|
||||
public int getPeerNum() {
|
||||
return peerNum;
|
||||
}
|
||||
|
||||
public void setPeerNum(int peerNum) {
|
||||
this.peerNum = peerNum;
|
||||
}
|
||||
|
||||
public M3U8Entity() {
|
||||
}
|
||||
|
||||
public static class PeerInfo {
|
||||
public PeerInfo(int peerId, String peerPath) {
|
||||
this.peerId = peerId;
|
||||
this.peerPath = peerPath;
|
||||
}
|
||||
|
||||
public int peerId;
|
||||
public String peerPath;
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(this.filePath);
|
||||
dest.writeInt(this.peerIndex);
|
||||
dest.writeInt(this.peerNum);
|
||||
dest.writeByte(this.isLive ? (byte) 1 : (byte) 0);
|
||||
dest.writeString(this.cacheDir);
|
||||
dest.writeString(this.keyPath);
|
||||
dest.writeString(this.keyUrl);
|
||||
dest.writeString(this.method);
|
||||
dest.writeString(this.iv);
|
||||
}
|
||||
|
||||
protected M3U8Entity(Parcel in) {
|
||||
this.filePath = in.readString();
|
||||
this.peerIndex = in.readInt();
|
||||
this.peerNum = in.readInt();
|
||||
this.isLive = in.readByte() != 0;
|
||||
this.cacheDir = in.readString();
|
||||
this.keyPath = in.readString();
|
||||
this.keyUrl = in.readString();
|
||||
this.method = in.readString();
|
||||
this.iv = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<M3U8Entity> CREATOR = new Creator<M3U8Entity>() {
|
||||
@Override public M3U8Entity createFromParcel(Parcel source) {
|
||||
return new M3U8Entity(source);
|
||||
}
|
||||
|
||||
@Override public M3U8Entity[] newArray(int size) {
|
||||
return new M3U8Entity[size];
|
||||
}
|
||||
};
|
||||
}
|
@ -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.event;
|
||||
|
||||
/**
|
||||
* 组合任务最大下载任务数事件
|
||||
*/
|
||||
public class DGMaxNumEvent {
|
||||
|
||||
public int maxNum;
|
||||
|
||||
public DGMaxNumEvent(int maxNum) {
|
||||
this.maxNum = maxNum;
|
||||
}
|
||||
}
|
@ -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.event;
|
||||
|
||||
/**
|
||||
* 最大下载任务数事件
|
||||
*/
|
||||
public class DMaxNumEvent {
|
||||
|
||||
public int maxNum;
|
||||
|
||||
public DMaxNumEvent(int maxNum) {
|
||||
this.maxNum = maxNum;
|
||||
}
|
||||
}
|
@ -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.event;
|
||||
|
||||
public class DSpeedEvent {
|
||||
|
||||
public int speed;
|
||||
|
||||
public DSpeedEvent(int speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
}
|
@ -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.event;
|
||||
|
||||
public class ErrorEvent {
|
||||
public long taskId;
|
||||
public String errorMsg;
|
||||
|
||||
public ErrorEvent(long taskId, String errorMsg) {
|
||||
this.taskId = taskId;
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.event;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 注解需要接收消息的方法,方法中只能有一个参数
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Event {
|
||||
//Class value();
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.event;
|
||||
|
||||
/**
|
||||
* 事件消息信息
|
||||
*/
|
||||
class EventMethodInfo {
|
||||
|
||||
/**
|
||||
* 被{@link Event}注解的事件方法
|
||||
*/
|
||||
String methodName;
|
||||
|
||||
/**
|
||||
* 该方法对应的参数类型
|
||||
*/
|
||||
Class<?> param;
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.event;
|
||||
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 消息发送工具
|
||||
*/
|
||||
public class EventMsgUtil {
|
||||
private static final String TAG = "EventUtil";
|
||||
private static EventMsgUtil defaultInstance;
|
||||
private Map<Object, List<EventMethodInfo>> mEventMethods =
|
||||
new ConcurrentHashMap<>();
|
||||
private ArrayBlockingQueue<Object> mEventQueue = new ArrayBlockingQueue<>(10);
|
||||
private ExecutorService mPool = Executors.newFixedThreadPool(5);
|
||||
|
||||
private EventMsgUtil() {
|
||||
ExecutorService pool = Executors.newSingleThreadExecutor();
|
||||
pool.execute(new Runnable() {
|
||||
@Override public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Object info = mEventQueue.take();
|
||||
sendEvent(info);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendEvent(final Object param) {
|
||||
mPool.submit(new Runnable() {
|
||||
@Override public void run() {
|
||||
Set<Object> keys = mEventMethods.keySet();
|
||||
for (Object key : keys) {
|
||||
List<EventMethodInfo> list = mEventMethods.get(key);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
for (EventMethodInfo info : list) {
|
||||
try {
|
||||
if (info.param == param.getClass()) {
|
||||
Method method = key.getClass().getDeclaredMethod(info.methodName, info.param);
|
||||
method.setAccessible(true);
|
||||
method.invoke(key, param);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static EventMsgUtil getDefault() {
|
||||
if (defaultInstance == null) {
|
||||
synchronized (EventMsgUtil.class) {
|
||||
if (defaultInstance == null) {
|
||||
defaultInstance = new EventMsgUtil();
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册事件
|
||||
*/
|
||||
public void register(Object obj) {
|
||||
Method[] methods = obj.getClass().getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
method.setAccessible(true);
|
||||
if (method.getAnnotation(Event.class) == null) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] clazz = method.getParameterTypes();
|
||||
if (clazz.length == 0 || clazz.length > 1) {
|
||||
ALog.e(TAG,
|
||||
String.format("%s.%s参数数量为0或参数数量大于1", obj.getClass().getName(), method.getName()));
|
||||
continue;
|
||||
}
|
||||
int modifier = method.getModifiers();
|
||||
if (Modifier.isStatic(modifier) || Modifier.isAbstract(modifier) || Modifier.isFinal(
|
||||
modifier)) {
|
||||
ALog.e(TAG, "注册的方法不能使用final、static、abstract修饰");
|
||||
continue;
|
||||
}
|
||||
|
||||
EventMethodInfo methodInfo = new EventMethodInfo();
|
||||
methodInfo.methodName = method.getName();
|
||||
methodInfo.param = clazz[0];
|
||||
List<EventMethodInfo> list = mEventMethods.get(obj);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
mEventMethods.put(obj, list);
|
||||
}
|
||||
list.add(methodInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void unRegister(Object obj) {
|
||||
for (Iterator<Map.Entry<Object, List<EventMethodInfo>>> iter =
|
||||
mEventMethods.entrySet().iterator(); iter.hasNext(); ) {
|
||||
Map.Entry<Object, List<EventMethodInfo>> entry = iter.next();
|
||||
if (entry.getKey().equals(obj)) {
|
||||
entry.getValue().clear();
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送事件,接收消息的方法需要使用{@link Event}注解
|
||||
*/
|
||||
public void post(Object param) {
|
||||
|
||||
synchronized (EventMsgUtil.class) {
|
||||
try {
|
||||
mEventQueue.offer(param, 2, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.arialyy.aria.core.event;
|
||||
|
||||
public class PeerIndexEvent {
|
||||
|
||||
public int peerIndex;
|
||||
public long createTime;
|
||||
public String key;
|
||||
|
||||
public PeerIndexEvent(String key, int peerIndex) {
|
||||
this.peerIndex = peerIndex;
|
||||
this.key = key;
|
||||
createTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
@ -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.event;
|
||||
|
||||
/**
|
||||
* 最大上传任务数事件
|
||||
*/
|
||||
public class UMaxNumEvent {
|
||||
|
||||
public int maxNum;
|
||||
|
||||
public UMaxNumEvent(int maxNum) {
|
||||
this.maxNum = maxNum;
|
||||
}
|
||||
}
|
@ -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.event;
|
||||
|
||||
public class USpeedEvent {
|
||||
|
||||
public int speed;
|
||||
|
||||
public USpeedEvent(int speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
}
|
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.download.DGTaskWrapper;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IDGroupListener;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.loader.ILoader;
|
||||
import com.arialyy.aria.core.loader.ILoaderVisitor;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.loader.IThreadTaskBuilder;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 组合任务加载器
|
||||
*/
|
||||
public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
|
||||
private long mCurrentLocation = 0;
|
||||
private IDGroupListener mListener;
|
||||
private ScheduledThreadPoolExecutor mTimer;
|
||||
private long mUpdateInterval;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
private Handler mScheduler;
|
||||
private SimpleSubQueue mSubQueue = SimpleSubQueue.newInstance();
|
||||
private Map<String, AbsSubDLoadUtil> mExeLoader = new WeakHashMap<>();
|
||||
private Map<String, DTaskWrapper> mCache = new WeakHashMap<>();
|
||||
private DGTaskWrapper mGTWrapper;
|
||||
private GroupRunState mState;
|
||||
|
||||
protected IInfoTask mInfoTask;
|
||||
|
||||
protected AbsGroupLoader(AbsTaskWrapper groupWrapper, IEventListener listener) {
|
||||
mListener = (IDGroupListener) listener;
|
||||
mGTWrapper = (DGTaskWrapper) groupWrapper;
|
||||
mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务
|
||||
*/
|
||||
protected abstract void handlerTask(Looper looper);
|
||||
|
||||
/**
|
||||
* 创建子任务加载器工具
|
||||
*
|
||||
* @param needGetFileInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
|
||||
*/
|
||||
protected abstract AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo);
|
||||
|
||||
protected IDGroupListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
protected DGTaskWrapper getWrapper() {
|
||||
return mGTWrapper;
|
||||
}
|
||||
|
||||
protected GroupRunState getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
public Handler getScheduler() {
|
||||
return mScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化组合任务状态
|
||||
*/
|
||||
private void initState(Looper looper) {
|
||||
mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue);
|
||||
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
|
||||
long fileLen = checkFileExists(wrapper.getEntity().getFilePath());
|
||||
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE
|
||||
&& fileLen > 0
|
||||
&& fileLen == wrapper.getEntity().getFileSize()) {
|
||||
//mState.updateCompleteNum();
|
||||
mCurrentLocation += wrapper.getEntity().getFileSize();
|
||||
} else {
|
||||
if (fileLen <= 0) {
|
||||
wrapper.getEntity().setCurrentProgress(0);
|
||||
}
|
||||
wrapper.getEntity().setState(IEntity.STATE_POST_PRE);
|
||||
mCache.put(wrapper.getKey(), wrapper);
|
||||
mCurrentLocation += wrapper.getEntity().getCurrentProgress();
|
||||
}
|
||||
}
|
||||
if (getWrapper().getSubTaskWrapper().size() != mState.getCompleteNum()) {
|
||||
getWrapper().setState(IEntity.STATE_POST_PRE);
|
||||
}
|
||||
mState.updateProgress(mCurrentLocation);
|
||||
mScheduler = new Handler(looper, SimpleSchedulers.newInstance(mState, mGTWrapper.getKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在,需要检查普通任务和分块任务的
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @return 文件存在返回文件长度,不存在返回-1
|
||||
*/
|
||||
private long checkFileExists(String filePath) {
|
||||
File temp = new File(filePath);
|
||||
if (temp.exists()) {
|
||||
return temp.length();
|
||||
}
|
||||
File block = new File(String.format(IRecordHandler.SUB_PATH, filePath, 0));
|
||||
if (block.exists()) {
|
||||
return block.length();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return mGTWrapper.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动子任务下载
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
void startSubTask(String url) {
|
||||
if (!checkSubTask(url, "开始")) {
|
||||
return;
|
||||
}
|
||||
if (!mState.isRunning.get()) {
|
||||
startTimer();
|
||||
}
|
||||
AbsSubDLoadUtil d = getDownloader(url, false);
|
||||
if (d != null && !d.isRunning()) {
|
||||
mSubQueue.startTask(d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止子任务下载
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
void stopSubTask(String url) {
|
||||
if (!checkSubTask(url, "停止")) {
|
||||
return;
|
||||
}
|
||||
AbsSubDLoadUtil d = getDownloader(url, false);
|
||||
if (d != null && d.isRunning()) {
|
||||
mSubQueue.stopTask(d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查子任务
|
||||
*
|
||||
* @param url 子任务url
|
||||
* @param type 任务类型
|
||||
* @return {@code true} 任务可以下载
|
||||
*/
|
||||
private boolean checkSubTask(String url, String type) {
|
||||
DTaskWrapper wrapper = mCache.get(url);
|
||||
if (wrapper != null) {
|
||||
if (wrapper.getState() == IEntity.STATE_COMPLETE) {
|
||||
ALog.w(TAG, "任务【" + url + "】已完成," + type + "失败");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ALog.w(TAG, "任务组中没有该任务【" + url + "】," + type + "失败");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过地址获取下载器
|
||||
*
|
||||
* @param url 子任务下载地址
|
||||
*/
|
||||
private AbsSubDLoadUtil getDownloader(String url, boolean needGetFileInfo) {
|
||||
AbsSubDLoadUtil d = mExeLoader.get(url);
|
||||
if (d == null) {
|
||||
return createSubLoader(mCache.get(url), needGetFileInfo);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return mState != null && mState.isRunning.get();
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
isCancel = true;
|
||||
if (mInfoTask != null){
|
||||
mInfoTask.cancel();
|
||||
}
|
||||
closeTimer();
|
||||
mSubQueue.removeAllTask();
|
||||
mListener.onCancel();
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
if (mInfoTask != null){
|
||||
mInfoTask.stop();
|
||||
}
|
||||
isStop = true;
|
||||
if (mSubQueue.getExecSize() == 0) {
|
||||
mListener.onStop(mGTWrapper.getEntity().getCurrentProgress());
|
||||
} else {
|
||||
mSubQueue.stopAllTask();
|
||||
}
|
||||
closeTimer();
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
checkComponent();
|
||||
if (isStop || isCancel) {
|
||||
closeTimer();
|
||||
return;
|
||||
}
|
||||
startRunningFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始进度流程
|
||||
*/
|
||||
private void startRunningFlow() {
|
||||
closeTimer();
|
||||
Looper.prepare();
|
||||
Looper looper = Looper.myLooper();
|
||||
if (looper == Looper.getMainLooper()) {
|
||||
throw new IllegalThreadStateException("不能在主线程程序中调用Loader");
|
||||
}
|
||||
initState(looper);
|
||||
getState().setSubSize(getWrapper().getSubTaskWrapper().size());
|
||||
if (getState().getCompleteNum() != 0
|
||||
&& getState().getCompleteNum() == getState().getSubSize()) {
|
||||
mListener.onComplete();
|
||||
return;
|
||||
}
|
||||
startTimer();
|
||||
handlerTask(looper);
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 组合任务获取完成子任务的信息后调用
|
||||
*/
|
||||
protected void onPostStart() {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
getListener().onPostPre(getWrapper().getEntity().getFileSize());
|
||||
if (getWrapper().getEntity().getFileSize() > 0) {
|
||||
getListener().onResume(getWrapper().getEntity().getCurrentProgress());
|
||||
} else {
|
||||
getListener().onStart(getWrapper().getEntity().getCurrentProgress());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void startTimer() {
|
||||
mState.isRunning.set(true);
|
||||
mTimer = new ScheduledThreadPoolExecutor(1);
|
||||
mTimer.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (!mState.isRunning.get()) {
|
||||
closeTimer();
|
||||
} else if (mCurrentLocation >= 0) {
|
||||
long t = 0;
|
||||
for (DTaskWrapper te : mGTWrapper.getSubTaskWrapper()) {
|
||||
if (te.getState() == IEntity.STATE_COMPLETE) {
|
||||
t += te.getEntity().getFileSize();
|
||||
} else {
|
||||
t += te.getEntity().getCurrentProgress();
|
||||
}
|
||||
}
|
||||
mCurrentLocation = t;
|
||||
mState.updateProgress(mCurrentLocation);
|
||||
mListener.onProgress(t);
|
||||
}
|
||||
}
|
||||
}, 0, mUpdateInterval, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动子任务下载器
|
||||
*/
|
||||
protected void startSubLoader(AbsSubDLoadUtil loader) {
|
||||
mExeLoader.put(loader.getKey(), loader);
|
||||
mSubQueue.startTask(loader);
|
||||
}
|
||||
|
||||
@Override public boolean isBreak() {
|
||||
if (isCancel || isStop) {
|
||||
//ALog.d(TAG, "isCancel = " + isCancel + ", isStop = " + isStop);
|
||||
ALog.d(TAG, String.format("任务【%s】已停止或取消了", mGTWrapper.getKey()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private synchronized void closeTimer() {
|
||||
if (mTimer != null && !mTimer.isShutdown()) {
|
||||
mTimer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
protected void fail(AriaException e, boolean needRetry) {
|
||||
closeTimer();
|
||||
getListener().onFail(needRetry, e);
|
||||
}
|
||||
|
||||
@Override public long getCurrentProgress() {
|
||||
return mCurrentLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 组合任务不需要实现这个,记录交由其子任务处理
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public void addComponent(IRecordHandler recordHandler) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 组合任务不需要实现这个,线程创建交有子任务处理
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public void addComponent(IThreadTaskBuilder builder) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 组合任务不需要实现这个,其内部是一个子任务调度器,并不是线程状态管理器
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public void addComponent(IThreadStateManager threadState) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件: {@link #mInfoTask}
|
||||
*/
|
||||
private void checkComponent() {
|
||||
if (mInfoTask == null) {
|
||||
throw new NullPointerException(("文件信息组件为空"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.arialyy.aria.core.inf.IUtil;
|
||||
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.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/30.
|
||||
* 任务组核心逻辑
|
||||
*/
|
||||
public abstract class AbsGroupLoaderUtil implements IUtil {
|
||||
|
||||
protected String TAG = CommonUtil.getClassName(getClass());
|
||||
private IEventListener mListener;
|
||||
protected AbsGroupLoader mLoader;
|
||||
private AbsTaskWrapper mTaskWrapper;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
|
||||
@Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
|
||||
mTaskWrapper = taskWrapper;
|
||||
mListener = listener;
|
||||
mLoader = getLoader();
|
||||
return this;
|
||||
}
|
||||
|
||||
protected abstract AbsGroupLoader getLoader();
|
||||
|
||||
protected abstract LoaderStructure buildLoaderStructure();
|
||||
|
||||
public IEventListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
public AbsTaskWrapper getTaskWrapper() {
|
||||
return mTaskWrapper;
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return mTaskWrapper.getKey();
|
||||
}
|
||||
|
||||
@Override public long getFileSize() {
|
||||
return mTaskWrapper.getEntity().getFileSize();
|
||||
}
|
||||
|
||||
@Override public long getCurrentLocation() {
|
||||
return mLoader.getCurrentProgress();
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return mLoader.isRunning();
|
||||
}
|
||||
|
||||
public void startSubTask(String url) {
|
||||
getLoader().startSubTask(url);
|
||||
}
|
||||
|
||||
public void stopSubTask(String url) {
|
||||
getLoader().stopSubTask(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消下载
|
||||
*/
|
||||
@Override public void cancel() {
|
||||
isCancel = true;
|
||||
mLoader.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止下载
|
||||
*/
|
||||
@Override public void stop() {
|
||||
isStop = true;
|
||||
mLoader.stop();
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (isStop || isCancel) {
|
||||
ALog.w(TAG, "启动组合任务失败,任务已停止或已取消");
|
||||
return;
|
||||
}
|
||||
mListener.onPre();
|
||||
|
||||
buildLoaderStructure();
|
||||
new Thread(mLoader).start();
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import android.os.Handler;
|
||||
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.IUtil;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.listener.ISchedulers;
|
||||
import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.loader.SubLoader;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* 子任务下载器工具,需要在线程池中执行
|
||||
*/
|
||||
public abstract class AbsSubDLoadUtil implements IUtil, Runnable {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
|
||||
protected SubLoader mDLoader;
|
||||
private DTaskWrapper mWrapper;
|
||||
private Handler mSchedulers;
|
||||
private boolean needGetInfo;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
private String parentKey;
|
||||
|
||||
/**
|
||||
* @param schedulers 调度器
|
||||
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
|
||||
*/
|
||||
protected AbsSubDLoadUtil(Handler schedulers, boolean needGetInfo, String parentKey) {
|
||||
mSchedulers = schedulers;
|
||||
this.parentKey = parentKey;
|
||||
this.needGetInfo = needGetInfo;
|
||||
}
|
||||
|
||||
@Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
|
||||
mWrapper = (DTaskWrapper) taskWrapper;
|
||||
mDLoader = getLoader();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建加载器
|
||||
*/
|
||||
protected abstract SubLoader getLoader();
|
||||
|
||||
protected abstract LoaderStructure buildLoaderStructure();
|
||||
|
||||
public String getParentKey() {
|
||||
return parentKey;
|
||||
}
|
||||
|
||||
protected boolean isNeedGetInfo() {
|
||||
return needGetInfo;
|
||||
}
|
||||
|
||||
public Handler getSchedulers() {
|
||||
return mSchedulers;
|
||||
}
|
||||
|
||||
@Override public String getKey() {
|
||||
return mDLoader.getKey();
|
||||
}
|
||||
|
||||
public DTaskWrapper getWrapper() {
|
||||
return mWrapper;
|
||||
}
|
||||
|
||||
public DownloadEntity getEntity() {
|
||||
return mWrapper.getEntity();
|
||||
}
|
||||
|
||||
public TaskRecord getRecord(){
|
||||
return getLoader().getRecord();
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (isStop || isCancel) {
|
||||
return;
|
||||
}
|
||||
buildLoaderStructure();
|
||||
new Thread(mDLoader).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请在线程池中使用
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public void start() {
|
||||
throw new AssertionError("请在线程池中使用");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新开始任务
|
||||
*/
|
||||
void reStart() {
|
||||
if (mDLoader != null) {
|
||||
mDLoader.retryTask();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 子任务不实现这个
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public long getFileSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 子任务不实现这个
|
||||
*/
|
||||
@Deprecated
|
||||
@Override public long getCurrentLocation() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return mDLoader != null && mDLoader.isRunning();
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
if (isCancel) {
|
||||
ALog.w(TAG, "子任务已取消");
|
||||
return;
|
||||
}
|
||||
isCancel = true;
|
||||
if (mDLoader != null && isRunning()) {
|
||||
mDLoader.cancel();
|
||||
} else {
|
||||
mSchedulers.obtainMessage(ISchedulers.CANCEL, this).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
if (isStop) {
|
||||
ALog.w(TAG, "任务已停止");
|
||||
return;
|
||||
}
|
||||
isStop = true;
|
||||
if (mDLoader != null && isRunning()) {
|
||||
mDLoader.stop();
|
||||
} else {
|
||||
mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.arialyy.aria.core.listener.IDGroupListener;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 组合任务执行中的状态信息
|
||||
*/
|
||||
public final class GroupRunState {
|
||||
private String TAG = "GroupRunState";
|
||||
/**
|
||||
* 子任务数
|
||||
*/
|
||||
private int mSubSize;
|
||||
|
||||
/**
|
||||
* 已经完成的任务数
|
||||
*/
|
||||
private AtomicInteger mCompleteNum = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* 失败的任务数
|
||||
*/
|
||||
private AtomicInteger mFailNum = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* 停止的任务数
|
||||
*/
|
||||
private AtomicInteger mStopNum = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* 当前进度
|
||||
*/
|
||||
private long mProgress;
|
||||
|
||||
/**
|
||||
* 组合任务监听
|
||||
*/
|
||||
IDGroupListener listener;
|
||||
|
||||
/**
|
||||
* 子任务队列
|
||||
*/
|
||||
SimpleSubQueue queue;
|
||||
|
||||
/**
|
||||
* 是否在执行
|
||||
*/
|
||||
AtomicBoolean isRunning = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* 子任务失败、停止记录,用于当子任务失败重新被用户点击开始时,更新{@link #mStopNum}或{@link #mFailNum}
|
||||
* 保存的数据为:子任务key
|
||||
*/
|
||||
private Set<String> mFailTemp = new HashSet<>(), mStopTemp = new HashSet<>();
|
||||
|
||||
private String mGroupHash;
|
||||
|
||||
GroupRunState(String groupHash, IDGroupListener listener, SimpleSubQueue queue) {
|
||||
this.listener = listener;
|
||||
this.queue = queue;
|
||||
mGroupHash = groupHash;
|
||||
}
|
||||
|
||||
public void setSubSize(int subSize) {
|
||||
mSubSize = subSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组合任务是否正在自行
|
||||
*
|
||||
* @return {@code true}组合任务正在执行
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return isRunning.get();
|
||||
}
|
||||
|
||||
public void setRunning(boolean running) {
|
||||
isRunning.set(running);
|
||||
}
|
||||
|
||||
String getGroupHash() {
|
||||
return mGroupHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组合任务子任务数
|
||||
*/
|
||||
public int getSubSize() {
|
||||
return mSubSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取失败的数量
|
||||
*/
|
||||
public int getFailNum() {
|
||||
return mFailNum.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取停止的数量
|
||||
*/
|
||||
public int getStopNum() {
|
||||
return mStopNum.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完成的数量
|
||||
*/
|
||||
public int getCompleteNum() {
|
||||
return mCompleteNum.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前组合任务总进度
|
||||
*/
|
||||
public long getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新完成的数量,mCompleteNum + 1
|
||||
*/
|
||||
public void updateCompleteNum() {
|
||||
mCompleteNum.getAndIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
*/
|
||||
public void updateProgress(long newProgress) {
|
||||
this.mProgress = newProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当子任务开始时,更新停止\失败的任务数
|
||||
*
|
||||
* @param key {@link AbsTaskWrapper#getKey()}
|
||||
*/
|
||||
public void updateCount(String key) {
|
||||
if (mFailTemp.contains(key)) {
|
||||
mFailTemp.remove(key);
|
||||
mFailNum.getAndDecrement();
|
||||
} else if (mStopTemp.contains(key)) {
|
||||
mStopTemp.remove(key);
|
||||
mStopNum.getAndDecrement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计子任务停止的数量
|
||||
*
|
||||
* @param key {@link AbsTaskWrapper#getKey()}
|
||||
*/
|
||||
public void countStopNum(String key) {
|
||||
mStopTemp.add(key);
|
||||
mStopNum.getAndIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计子任务失败的数量
|
||||
*
|
||||
* @param key {@link AbsTaskWrapper#getKey()}
|
||||
*/
|
||||
public void countFailNum(String key) {
|
||||
mFailTemp.add(key);
|
||||
mFailNum.getAndIncrement();
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.task.AbsGroupTask;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/9/8.
|
||||
* 任务组参数传递
|
||||
*/
|
||||
public final class GroupSendParams<GROUP_TASK extends AbsGroupTask, ENTITY extends AbsNormalEntity> {
|
||||
|
||||
public GROUP_TASK groupTask;
|
||||
public ENTITY entity;
|
||||
|
||||
public GroupSendParams() {
|
||||
}
|
||||
|
||||
public GroupSendParams(GROUP_TASK groupTask, ENTITY entity) {
|
||||
this.groupTask = groupTask;
|
||||
this.entity = entity;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoader;
|
||||
import com.arialyy.aria.core.inf.IUtil;
|
||||
import com.arialyy.aria.core.config.DGroupConfig;
|
||||
|
||||
/**
|
||||
* 组合任务子任务队列
|
||||
*
|
||||
* @param <Fileer> {@link AbsNormalLoader}下载器
|
||||
*/
|
||||
interface ISubQueue<Fileer extends IUtil> {
|
||||
|
||||
/**
|
||||
* 添加任务
|
||||
*/
|
||||
|
||||
void addTask(Fileer fileer);
|
||||
|
||||
/**
|
||||
* 开始任务
|
||||
* 如果执行队列没有达到上限,则启动任务。
|
||||
* 如果执行队列已经到达上限,则将任务添加到等待队列总。
|
||||
* 队列上限配置{@link DGroupConfig#setSubMaxTaskNum(int)}
|
||||
*/
|
||||
void startTask(Fileer fileer);
|
||||
|
||||
/**
|
||||
* 停止单个任务,如果缓存队列中有等待中的任务,则启动等待中的任务
|
||||
*/
|
||||
void stopTask(Fileer fileer);
|
||||
|
||||
/**
|
||||
* 停止全部任务,停止所有正在执行的任务,并清空所有等待中的端服务
|
||||
*/
|
||||
void stopAllTask();
|
||||
|
||||
/**
|
||||
* 修改最大任务数
|
||||
*
|
||||
* @param num 任务数不能小于1
|
||||
*/
|
||||
void modifyMaxExecNum(int num);
|
||||
|
||||
/**
|
||||
* 从执行队列中移除任务,一般用于任务完成的情况
|
||||
*/
|
||||
void removeTaskFromExecQ(Fileer fileer);
|
||||
|
||||
/**
|
||||
* 删除任务,如果缓存队列中有等待中的任务,则启动等待中的任务
|
||||
*/
|
||||
void removeTask(Fileer fileer);
|
||||
|
||||
/**
|
||||
* 停止全部任务,停止所有正在执行的任务,并清空所有等待中的端服务
|
||||
*/
|
||||
void removeAllTask();
|
||||
|
||||
|
||||
/**
|
||||
* 获取下一个任务
|
||||
*/
|
||||
Fileer getNextTask();
|
||||
|
||||
/**
|
||||
* 清空缓存队列和执行队列
|
||||
*/
|
||||
void clear();
|
||||
}
|
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.exception.ExceptionFactory;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.NetUtils;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 组合任务子任务调度器,用于调度任务的开始、停止、失败、完成等情况
|
||||
* 该调度器生命周期和{@link AbsGroupLoaderUtil}生命周期一致
|
||||
*/
|
||||
final class SimpleSchedulers implements Handler.Callback {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
private SimpleSubQueue mQueue;
|
||||
private GroupRunState mGState;
|
||||
private String mKey; // 组合任务的key
|
||||
|
||||
private SimpleSchedulers(GroupRunState state, String key) {
|
||||
mQueue = state.queue;
|
||||
mGState = state;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
static SimpleSchedulers newInstance(GroupRunState state, String key) {
|
||||
return new SimpleSchedulers(state, key);
|
||||
}
|
||||
|
||||
@Override public boolean handleMessage(Message msg) {
|
||||
Bundle b = msg.getData();
|
||||
if (b == null) {
|
||||
ALog.w(TAG, "组合任务子任务调度数据为空");
|
||||
return true;
|
||||
}
|
||||
String threadName = b.getString(IThreadStateManager.DATA_THREAD_NAME);
|
||||
AbsSubDLoadUtil loaderUtil = mQueue.getLoaderUtil(threadName);
|
||||
if (loaderUtil == null) {
|
||||
ALog.e(TAG, String.format("子任务loader不存在,state:%s,key:%s", msg.what, threadName));
|
||||
return true;
|
||||
}
|
||||
long curLocation = b.getLong(IThreadStateManager.DATA_THREAD_LOCATION,
|
||||
loaderUtil.getLoader().getWrapper().getEntity().getCurrentProgress());
|
||||
// 处理状态
|
||||
switch (msg.what) {
|
||||
case IThreadStateManager.STATE_RUNNING:
|
||||
long range = (long) msg.obj;
|
||||
mGState.listener.onSubRunning(loaderUtil.getEntity(), range);
|
||||
break;
|
||||
case IThreadStateManager.STATE_PRE:
|
||||
mGState.listener.onSubPre(loaderUtil.getEntity());
|
||||
mGState.updateCount(loaderUtil.getKey());
|
||||
break;
|
||||
case IThreadStateManager.STATE_START:
|
||||
mGState.listener.onSubStart(loaderUtil.getEntity());
|
||||
break;
|
||||
case IThreadStateManager.STATE_STOP:
|
||||
handleStop(loaderUtil, curLocation);
|
||||
ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName);
|
||||
break;
|
||||
case IThreadStateManager.STATE_COMPLETE:
|
||||
handleComplete(loaderUtil);
|
||||
ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName);
|
||||
break;
|
||||
case IThreadStateManager.STATE_FAIL:
|
||||
boolean needRetry = b.getBoolean(IThreadStateManager.DATA_RETRY, false);
|
||||
handleFail(loaderUtil, needRetry);
|
||||
ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理子任务失败的情况
|
||||
* 1、子任务失败次数大于等于配置的重试次数,才能认为子任务停止
|
||||
* 2、stopNum + failNum + completeNum + cacheNum == subSize,则认为组合任务停止
|
||||
* 3、failNum == subSize,只有全部的子任务都失败了,才能任务组合任务失败
|
||||
*
|
||||
* @param needRetry true 需要重试,false 不需要重试
|
||||
*/
|
||||
private synchronized void handleFail(final AbsSubDLoadUtil loaderUtil, boolean needRetry) {
|
||||
Log.d(TAG, String.format("handleFail, size = %s, completeNum = %s, failNum = %s, stopNum = %s",
|
||||
mGState.getSubSize(), mGState.getCompleteNum(), mGState.getFailNum(),
|
||||
mGState.getSubSize()));
|
||||
|
||||
Configuration config = Configuration.getInstance();
|
||||
int num = config.dGroupCfg.getSubReTryNum();
|
||||
boolean isNotNetRetry = config.appCfg.isNotNetRetry();
|
||||
|
||||
if (!needRetry
|
||||
|| (!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry)
|
||||
|| loaderUtil.getLoader() == null // 如果获取不到文件信息,loader为空
|
||||
|| loaderUtil.getEntity().getFailNum() > num) {
|
||||
mQueue.removeTaskFromExecQ(loaderUtil);
|
||||
mGState.listener.onSubFail(loaderUtil.getEntity(),
|
||||
ExceptionFactory.getException(ExceptionFactory.TYPE_GROUP,
|
||||
String.format("任务组子任务【%s】下载失败,下载地址【%s】", loaderUtil.getEntity().getFileName(),
|
||||
loaderUtil.getEntity().getUrl()), null));
|
||||
mGState.countFailNum(loaderUtil.getKey());
|
||||
if (mGState.getFailNum() == mGState.getSubSize()
|
||||
|| mGState.getStopNum() + mGState.getFailNum() + mGState.getCompleteNum()
|
||||
== mGState.getSubSize()) {
|
||||
mGState.isRunning.set(false);
|
||||
if (mGState.getCompleteNum() > 0
|
||||
&& Configuration.getInstance().dGroupCfg.isSubFailAsStop()) {
|
||||
ALog.e(TAG, String.format("任务组【%s】停止", mGState.getGroupHash()));
|
||||
mGState.listener.onStop(mGState.getProgress());
|
||||
return;
|
||||
}
|
||||
mGState.listener.onFail(false,
|
||||
ExceptionFactory.getException(ExceptionFactory.TYPE_GROUP,
|
||||
String.format("任务组【%s】下载失败", mGState.getGroupHash()), null));
|
||||
return;
|
||||
}
|
||||
startNext();
|
||||
return;
|
||||
}
|
||||
SimpleSubRetryQueue.getInstance().offer(loaderUtil);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理子任务停止的情况
|
||||
* 1、所有的子任务已经停止,则认为组合任务停止
|
||||
* 2、completeNum + failNum + stopNum = subSize,则认为组合任务停止
|
||||
*/
|
||||
private synchronized void handleStop(AbsSubDLoadUtil loadUtil, long curLocation) {
|
||||
Log.d(TAG, String.format("handleStop, size = %s, completeNum = %s, failNum = %s, stopNum = %s",
|
||||
mGState.getSubSize(), mGState.getCompleteNum(), mGState.getFailNum(),
|
||||
mGState.getSubSize()));
|
||||
|
||||
mGState.listener.onSubStop(loadUtil.getEntity(), curLocation);
|
||||
mGState.countStopNum(loadUtil.getKey());
|
||||
if (mGState.getStopNum() == mGState.getSubSize()
|
||||
|| mGState.getStopNum()
|
||||
+ mGState.getCompleteNum()
|
||||
+ mGState.getFailNum()
|
||||
+ mQueue.getCacheSize()
|
||||
== mGState.getSubSize()) {
|
||||
mGState.isRunning.set(false);
|
||||
mGState.listener.onStop(mGState.getProgress());
|
||||
return;
|
||||
}
|
||||
startNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理子任务完成的情况,有以下三种情况
|
||||
* 1、已经没有缓存的子任务,并且停止的子任务是数{@link GroupRunState#getStopNum()} ()}为0,失败的子任数{@link
|
||||
* GroupRunState#getFailNum()}为0,则认为组合任务已经完成
|
||||
* 2、已经没有缓存的子任务,并且停止的子任务是数{@link GroupRunState#getCompleteNum()}不为0,或者失败的子任数{@link
|
||||
* GroupRunState#getFailNum()}不为0,则认为组合任务被停止
|
||||
* 3、只有有缓存的子任务,则任务组合任务没有完成
|
||||
*/
|
||||
private synchronized void handleComplete(AbsSubDLoadUtil loader) {
|
||||
ALog.d(TAG, String.format("子任务【%s】完成", loader.getEntity().getFileName()));
|
||||
Log.d(TAG,
|
||||
String.format("handleComplete, size = %s, completeNum = %s, failNum = %s, stopNum = %s",
|
||||
mGState.getSubSize(), mGState.getCompleteNum(), mGState.getFailNum(),
|
||||
mGState.getStopNum()));
|
||||
|
||||
TaskRecord record = loader.getRecord();
|
||||
if (record != null && record.isBlock) {
|
||||
File partFile =
|
||||
new File(String.format(IRecordHandler.SUB_PATH, record.filePath, 0));
|
||||
partFile.renameTo(new File(record.filePath));
|
||||
}
|
||||
ThreadTaskManager.getInstance().removeTaskThread(loader.getKey());
|
||||
mGState.listener.onSubComplete(loader.getEntity());
|
||||
mQueue.removeTaskFromExecQ(loader);
|
||||
mGState.updateCompleteNum();
|
||||
if (mGState.getCompleteNum() + mGState.getFailNum() + mGState.getStopNum()
|
||||
== mGState.getSubSize()) {
|
||||
if (mGState.getStopNum() == 0 && mGState.getFailNum() == 0) {
|
||||
mGState.listener.onComplete();
|
||||
} else if (mGState.getStopNum() == 0
|
||||
&& !Configuration.getInstance().dGroupCfg.isSubFailAsStop()) {
|
||||
mGState.listener.onFail(false,
|
||||
ExceptionFactory.getException(ExceptionFactory.TYPE_GROUP,
|
||||
String.format("任务组【%s】下载失败", mGState.getGroupHash()), null));
|
||||
} else {
|
||||
mGState.listener.onStop(mGState.getProgress());
|
||||
}
|
||||
mGState.isRunning.set(false);
|
||||
return;
|
||||
}
|
||||
startNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果有等待中的任务,则启动下一任务
|
||||
*/
|
||||
private void startNext() {
|
||||
if (mQueue.isStopAll()) {
|
||||
return;
|
||||
}
|
||||
AbsSubDLoadUtil next = mQueue.getNextTask();
|
||||
if (next != null) {
|
||||
ALog.d(TAG, String.format("启动任务:%s", next.getEntity().getFileName()));
|
||||
mQueue.startTask(next);
|
||||
return;
|
||||
}
|
||||
ALog.i(TAG, "没有下一子任务");
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 组合任务队列,该队列生命周期和{@link AbsGroupLoaderUtil}生命周期一致
|
||||
*/
|
||||
final class SimpleSubQueue implements ISubQueue<AbsSubDLoadUtil> {
|
||||
private final String TAG = CommonUtil.getClassName(getClass());
|
||||
/**
|
||||
* 缓存下载器
|
||||
*/
|
||||
private Map<String, AbsSubDLoadUtil> mCache = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 执行中的下载器
|
||||
*/
|
||||
private Map<String, AbsSubDLoadUtil> mExec = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 最大执行任务数
|
||||
*/
|
||||
private int mMaxExecSize;
|
||||
|
||||
/**
|
||||
* 是否停止任务任务
|
||||
*/
|
||||
private boolean isStopAll = false;
|
||||
|
||||
private SimpleSubQueue() {
|
||||
mMaxExecSize = Configuration.getInstance().dGroupCfg.getSubMaxTaskNum();
|
||||
}
|
||||
|
||||
static SimpleSubQueue newInstance() {
|
||||
return new SimpleSubQueue();
|
||||
}
|
||||
|
||||
synchronized AbsSubDLoadUtil getLoaderUtil(String key) {
|
||||
AbsSubDLoadUtil sub = mExec.get(key);
|
||||
if (sub != null) {
|
||||
return sub;
|
||||
}
|
||||
return mCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存队列大小
|
||||
*/
|
||||
int getCacheSize() {
|
||||
return mCache.size();
|
||||
}
|
||||
|
||||
public int getExecSize(){
|
||||
return mExec.size();
|
||||
}
|
||||
|
||||
boolean isStopAll() {
|
||||
return isStopAll;
|
||||
}
|
||||
|
||||
@Override public void addTask(AbsSubDLoadUtil fileer) {
|
||||
mCache.put(fileer.getKey(), fileer);
|
||||
}
|
||||
|
||||
@Override public void startTask(AbsSubDLoadUtil fileer) {
|
||||
if (mExec.size() < mMaxExecSize) {
|
||||
mCache.remove(fileer.getKey());
|
||||
mExec.put(fileer.getKey(), fileer);
|
||||
ALog.d(TAG,
|
||||
String.format("开始执行子任务:%s,key: %s", fileer.getEntity().getFileName(), fileer.getKey()));
|
||||
fileer.run();
|
||||
return;
|
||||
}
|
||||
ALog.d(TAG, String.format("执行队列已满,任务进入缓存器中,key: %s", fileer.getKey()));
|
||||
addTask(fileer);
|
||||
}
|
||||
|
||||
@Override public void stopTask(AbsSubDLoadUtil fileer) {
|
||||
fileer.stop();
|
||||
mExec.remove(fileer.getKey());
|
||||
}
|
||||
|
||||
@Override public void stopAllTask() {
|
||||
isStopAll = true;
|
||||
ALog.d(TAG, "停止组合任务");
|
||||
mCache.clear();
|
||||
Set<String> keys = mExec.keySet();
|
||||
for (String key : keys) {
|
||||
AbsSubDLoadUtil loader = mExec.get(key);
|
||||
if (loader != null) {
|
||||
ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName()));
|
||||
loader.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void modifyMaxExecNum(int num) {
|
||||
if (num < 1) {
|
||||
ALog.e(TAG, String.format("修改组合任务子任务队列数失败,num: %s", num));
|
||||
return;
|
||||
}
|
||||
if (num == mMaxExecSize) {
|
||||
ALog.i(TAG, String.format("忽略此次修改,oldSize: %s, num: %s", mMaxExecSize, num));
|
||||
return;
|
||||
}
|
||||
int oldSize = mMaxExecSize;
|
||||
mMaxExecSize = num;
|
||||
int diff = Math.abs(oldSize - num);
|
||||
|
||||
if (oldSize < num) { // 处理队列变小的情况,该情况下将停止队尾任务,并将这些任务添加到缓存队列中
|
||||
if (mExec.size() > num) {
|
||||
Set<String> keys = mExec.keySet();
|
||||
List<AbsSubDLoadUtil> caches = new ArrayList<>();
|
||||
int i = 0;
|
||||
for (String key : keys) {
|
||||
if (i > num) {
|
||||
caches.add(mExec.get(key));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
Collection<AbsSubDLoadUtil> temp = mCache.values();
|
||||
mCache.clear();
|
||||
for (AbsSubDLoadUtil cache : caches) {
|
||||
addTask(cache);
|
||||
}
|
||||
for (AbsSubDLoadUtil t : temp) {
|
||||
addTask(t);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 处理队列变大的情况,该情况下将增加任务
|
||||
if (mExec.size() < num) {
|
||||
for (int i = 0; i < diff; i++) {
|
||||
AbsSubDLoadUtil next = getNextTask();
|
||||
if (next != null) {
|
||||
startTask(next);
|
||||
} else {
|
||||
ALog.d(TAG, "子任务中没有缓存任务");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void removeTaskFromExecQ(AbsSubDLoadUtil fileer) {
|
||||
mExec.remove(fileer.getKey());
|
||||
}
|
||||
|
||||
@Override public void removeTask(AbsSubDLoadUtil fileer) {
|
||||
removeTaskFromExecQ(fileer);
|
||||
mCache.remove(fileer.getKey());
|
||||
}
|
||||
|
||||
@Override public void removeAllTask() {
|
||||
ALog.d(TAG, "删除组合任务");
|
||||
Set<String> keys = mExec.keySet();
|
||||
for (String key : keys) {
|
||||
AbsSubDLoadUtil loader = mExec.get(key);
|
||||
if (loader != null) {
|
||||
ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName()));
|
||||
loader.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public AbsSubDLoadUtil getNextTask() {
|
||||
Iterator<String> keys = mCache.keySet().iterator();
|
||||
if (keys.hasNext()) {
|
||||
return mCache.get(keys.next());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void clear() {
|
||||
mCache.clear();
|
||||
mExec.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 子任务重试队列
|
||||
*/
|
||||
final class SimpleSubRetryQueue {
|
||||
private volatile static SimpleSubRetryQueue INSTANCE = null;
|
||||
private ExecutorService pool = new ThreadPoolExecutor(5, 100,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
|
||||
public synchronized static SimpleSubRetryQueue getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (SimpleSubRetryQueue.class) {
|
||||
INSTANCE = new SimpleSubRetryQueue();
|
||||
}
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private SimpleSubRetryQueue() {
|
||||
|
||||
}
|
||||
|
||||
void offer(AbsSubDLoadUtil subDLoadUtil) {
|
||||
pool.submit(subDLoadUtil.getLoader());
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.RecordHandler;
|
||||
import com.arialyy.aria.core.common.RecordHelper;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.RecordUtil;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 子任务记录处理
|
||||
*/
|
||||
public class SubRecordHandler extends RecordHandler {
|
||||
public SubRecordHandler(AbsTaskWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@Override public void handlerTaskRecord(TaskRecord record) {
|
||||
RecordHelper helper = new RecordHelper(getWrapper(), record);
|
||||
if (getWrapper().isSupportBP() && record.threadNum > 1) {
|
||||
if (record.isBlock) {
|
||||
helper.handleBlockRecord();
|
||||
} else {
|
||||
helper.handleMultiRecord();
|
||||
}
|
||||
} else if (!getWrapper().isSupportBP()) {
|
||||
helper.handleNoSupportBPRecord();
|
||||
} else {
|
||||
helper.handleSingleThreadRecord();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) {
|
||||
ThreadRecord tr;
|
||||
tr = new ThreadRecord();
|
||||
tr.taskKey = record.filePath;
|
||||
tr.threadId = threadId;
|
||||
tr.startLocation = startL;
|
||||
tr.isComplete = false;
|
||||
|
||||
tr.threadType = record.taskType;
|
||||
//最后一个线程的结束位置即为文件的总长度
|
||||
if (threadId == (record.threadNum - 1)) {
|
||||
endL = getFileSize();
|
||||
}
|
||||
tr.endLocation = endL;
|
||||
tr.blockLen = RecordUtil.getBlockLen(getFileSize(), threadId, record.threadNum);
|
||||
return tr;
|
||||
}
|
||||
|
||||
@Override public TaskRecord createTaskRecord(int threadNum) {
|
||||
TaskRecord record = new TaskRecord();
|
||||
record.fileName = getEntity().getFileName();
|
||||
record.filePath = getEntity().getFilePath();
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadNum = threadNum;
|
||||
|
||||
int requestType = getWrapper().getRequestType();
|
||||
if (requestType == ITaskWrapper.D_HTTP || requestType == ITaskWrapper.DG_HTTP) {
|
||||
record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock();
|
||||
} else {
|
||||
record.isBlock = false;
|
||||
}
|
||||
record.taskType = requestType;
|
||||
record.isGroupRecord = getEntity().isGroupChild();
|
||||
if (record.isGroupRecord) {
|
||||
if (getEntity() instanceof DownloadEntity) {
|
||||
record.dGroupHash = ((DownloadEntity) getEntity()).getGroupHash();
|
||||
}
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override public int initTaskThreadNum() {
|
||||
int requestTpe = getWrapper().getRequestType();
|
||||
if (requestTpe == ITaskWrapper.U_HTTP
|
||||
|| (requestTpe == ITaskWrapper.D_HTTP && (!getWrapper().isSupportBP())
|
||||
)) {
|
||||
return 1;
|
||||
}
|
||||
int threadNum = Configuration.getInstance().downloadCfg.getThreadNum();
|
||||
return getFileSize() <= IRecordHandler.SUB_LEN
|
||||
? 1
|
||||
: threadNum;
|
||||
}
|
||||
}
|
@ -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.inf;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EventFlag {
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.inf;
|
||||
|
||||
import com.arialyy.aria.orm.annotation.Ignore;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/2/23.
|
||||
*/
|
||||
public interface IEntity {
|
||||
/**
|
||||
* 其它状态
|
||||
*/
|
||||
@Ignore int STATE_OTHER = -1;
|
||||
/**
|
||||
* 失败状态
|
||||
*/
|
||||
@Ignore int STATE_FAIL = 0;
|
||||
/**
|
||||
* 完成状态
|
||||
*/
|
||||
@Ignore int STATE_COMPLETE = 1;
|
||||
/**
|
||||
* 停止状态
|
||||
*/
|
||||
@Ignore int STATE_STOP = 2;
|
||||
/**
|
||||
* 等待状态
|
||||
*/
|
||||
@Ignore int STATE_WAIT = 3;
|
||||
/**
|
||||
* 正在执行
|
||||
*/
|
||||
@Ignore int STATE_RUNNING = 4;
|
||||
/**
|
||||
* 预处理
|
||||
*/
|
||||
@Ignore int STATE_PRE = 5;
|
||||
/**
|
||||
* 预处理完成
|
||||
*/
|
||||
@Ignore int STATE_POST_PRE = 6;
|
||||
/**
|
||||
* 删除任务
|
||||
*/
|
||||
@Ignore int STATE_CANCEL = 7;
|
||||
}
|
@ -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.inf;
|
||||
|
||||
/**
|
||||
* 事件处理对象
|
||||
*
|
||||
* @author lyy
|
||||
* Date: 2019-09-10
|
||||
*/
|
||||
public interface IEventHandler {
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.inf;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-10
|
||||
*/
|
||||
public interface IOptionConstant {
|
||||
// ftp 任务设置常量
|
||||
String ftpUrlEntity = "urlEntity";
|
||||
String charSet = "charSet";
|
||||
String clientConfig = "clientConfig";
|
||||
String uploadInterceptor = "uploadInterceptor";
|
||||
|
||||
// http
|
||||
String useServerFileName = "useServerFileName";
|
||||
String requestEnum = "requestEnum";
|
||||
String fileLenAdapter = "fileLenAdapter";
|
||||
String params = "params";
|
||||
String formFields = "formFields";
|
||||
String headers = "headers";
|
||||
String proxy = "proxy";
|
||||
|
||||
// m3u8 vod
|
||||
String bandWidth = "bandWidth";
|
||||
String cacheDir = "cacheDir";
|
||||
String generateIndexFileTemp = "generateIndexFileTemp";
|
||||
String bandWidthUrlConverter = "bandWidthUrlConverter";
|
||||
String mergeFile = "mergeFile";
|
||||
String mergeHandler = "mergeHandler";
|
||||
String vodUrlConverter = "vodUrlConverter";
|
||||
String maxTsQueueNum = "maxTsQueueNum";
|
||||
String jumpIndex = "jumpIndex";
|
||||
|
||||
// m3u8 live
|
||||
String liveTsUrlConverter = "liveTsUrlConverter";
|
||||
String liveUpdateInterval = "liveUpdateInterval";
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.inf;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/29.
|
||||
* 任务信息设置接口
|
||||
*/
|
||||
public interface ITaskOption {
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.inf;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.loader.ILoaderComponent;
|
||||
|
||||
/**
|
||||
* 线程任务状态
|
||||
*/
|
||||
public interface IThreadStateManager extends ILoaderComponent {
|
||||
int STATE_STOP = 0x01;
|
||||
int STATE_FAIL = 0x02;
|
||||
int STATE_CANCEL = 0x03;
|
||||
int STATE_COMPLETE = 0x04;
|
||||
int STATE_RUNNING = 0x05;
|
||||
int STATE_UPDATE_PROGRESS = 0x06;
|
||||
int STATE_PRE = 0x07;
|
||||
int STATE_START = 0x08;
|
||||
String DATA_RETRY = "DATA_RETRY";
|
||||
String DATA_ERROR_INFO = "DATA_ERROR_INFO";
|
||||
String DATA_THREAD_NAME = "DATA_THREAD_NAME";
|
||||
String DATA_THREAD_LOCATION = "DATA_THREAD_LOCATION";
|
||||
String DATA_ADD_LEN = "DATA_ADD_LEN"; // 增加的长度
|
||||
|
||||
/**
|
||||
* 任务是否已经失败
|
||||
*
|
||||
* @return true 任务已失败
|
||||
*/
|
||||
boolean isFail();
|
||||
|
||||
/**
|
||||
* 任务是否已经完成
|
||||
*
|
||||
* @return true 任务已完成
|
||||
*/
|
||||
boolean isComplete();
|
||||
|
||||
/**
|
||||
* 获取当前任务进度
|
||||
*
|
||||
* @return 任务当前进度
|
||||
*/
|
||||
long getCurrentProgress();
|
||||
|
||||
/**
|
||||
* 更新当前进度
|
||||
*
|
||||
* @param currentProgress 当前进度
|
||||
*/
|
||||
void updateCurrentProgress(long currentProgress);
|
||||
|
||||
/**
|
||||
* 设置消息循环体
|
||||
*/
|
||||
void setLooper(TaskRecord taskRecord, Looper looper);
|
||||
|
||||
/**
|
||||
* 创建handler 回调
|
||||
*/
|
||||
Handler.Callback getHandlerCallback();
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.inf;
|
||||
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/10/31.
|
||||
* 任务功能接口
|
||||
*/
|
||||
public interface IUtil {
|
||||
|
||||
IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener);
|
||||
|
||||
/**
|
||||
* 获取任务标志
|
||||
*
|
||||
* @return {@link DownloadEntity#getKey()}、{@link DownloadGroupEntity#getKey()}、{@link
|
||||
* UploadEntity#getKey()}
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
/**
|
||||
* 获取文件大小
|
||||
*/
|
||||
long getFileSize();
|
||||
|
||||
/**
|
||||
* 获取当前位置
|
||||
*/
|
||||
long getCurrentLocation();
|
||||
|
||||
/**
|
||||
* 任务是否正在执行
|
||||
*
|
||||
* @return {@code true} 任务正在执行
|
||||
*/
|
||||
boolean isRunning();
|
||||
|
||||
/**
|
||||
* 取消
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* 停止
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* 开始
|
||||
*/
|
||||
void start();
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.inf;
|
||||
|
||||
public interface Suggest {
|
||||
|
||||
String TASK_CONTROLLER = "after use #add()、#create()、#stop()、#cancel()、#resume()、#save()?";
|
||||
|
||||
String TO_CONTROLLER = "after use #controller()?";
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.arialyy.aria.core.inf;
|
||||
|
||||
public interface TaskSchedulerType {
|
||||
int TYPE_DEFAULT = 1;
|
||||
/**
|
||||
* 停止当前任务并且不自动启动下一任务
|
||||
*/
|
||||
int TYPE_STOP_NOT_NEXT = 2;
|
||||
/**
|
||||
* 停止任务并让当前任务处于等待状态
|
||||
*/
|
||||
int TYPE_STOP_AND_WAIT = 3;
|
||||
|
||||
/**
|
||||
* 删除任务并且不通知回调
|
||||
*/
|
||||
int TYPE_CANCEL_AND_NOT_NOTIFY = 4;
|
||||
|
||||
/**
|
||||
* 重置状态并启动任务
|
||||
*/
|
||||
int TYPE_START_AND_RESET_STATE = 5;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||
import com.arialyy.aria.core.task.DownloadTask;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.DeleteDRecord;
|
||||
|
||||
/**
|
||||
* 下载监听类
|
||||
*/
|
||||
public class BaseDListener extends BaseListener implements IDLoadListener {
|
||||
|
||||
@Override
|
||||
public void onPostPre(long fileSize) {
|
||||
mEntity.setFileSize(fileSize);
|
||||
mEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
|
||||
saveData(IEntity.STATE_POST_PRE, -1);
|
||||
sendInState2Target(ISchedulers.POST_PRE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void supportBreakpoint(boolean support) {
|
||||
if (!support) {
|
||||
sendInState2Target(ISchedulers.NO_SUPPORT_BREAK_POINT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void handleCancel() {
|
||||
int sType = getTask(DownloadTask.class).getSchedulerType();
|
||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||
mEntity.setComplete(false);
|
||||
mEntity.setState(IEntity.STATE_WAIT);
|
||||
|
||||
DeleteDRecord.getInstance().deleteRecord(mEntity, mTaskWrapper.isRemoveFile(), false);
|
||||
//RecordUtil.delTaskRecord(mEntity.getFilePath(), IRecordHandler.TYPE_DOWNLOAD,
|
||||
// mTaskWrapper.isRemoveFile(), false);
|
||||
} else {
|
||||
//RecordUtil.delTaskRecord(mEntity.getFilePath(), IRecordHandler.TYPE_DOWNLOAD,
|
||||
// mTaskWrapper.isRemoveFile(), true);
|
||||
|
||||
DeleteDRecord.getInstance().deleteRecord(mEntity, mTaskWrapper.isRemoveFile(), true);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.ErrorHelp;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class BaseListener implements IEventListener {
|
||||
protected String TAG = getClass().getSimpleName();
|
||||
static final int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度
|
||||
protected SoftReference<Handler> outHandler;
|
||||
private long mLastLen; //上一次发送长度
|
||||
private boolean isFirst = true;
|
||||
private AbsTask mTask;
|
||||
long mLastSaveTime;
|
||||
protected AbsEntity mEntity;
|
||||
protected AbsTaskWrapper mTaskWrapper;
|
||||
private boolean isConvertSpeed;
|
||||
private long mUpdateInterval;
|
||||
|
||||
@Override public IEventListener setParams(AbsTask task, Handler outHandler) {
|
||||
this.outHandler = new SoftReference<>(outHandler);
|
||||
mTask = new WeakReference<>(task).get();
|
||||
mEntity = mTask.getTaskWrapper().getEntity();
|
||||
mTaskWrapper = mTask.getTaskWrapper();
|
||||
isConvertSpeed = mTaskWrapper.getConfig().isConvertSpeed();
|
||||
mUpdateInterval = mTaskWrapper.getConfig().getUpdateInterval();
|
||||
mLastLen = mEntity.getCurrentProgress();
|
||||
mLastSaveTime = System.currentTimeMillis();
|
||||
TAG = CommonUtil.getClassName(getClass());
|
||||
return this;
|
||||
}
|
||||
|
||||
protected <TASK extends AbsTask> TASK getTask(Class<TASK> clazz) {
|
||||
return (TASK) mTask;
|
||||
}
|
||||
|
||||
@Override public void onPre() {
|
||||
saveData(IEntity.STATE_PRE, -1);
|
||||
sendInState2Target(ISchedulers.PRE);
|
||||
}
|
||||
|
||||
@Override public void onStart(long startLocation) {
|
||||
saveData(IEntity.STATE_RUNNING, startLocation);
|
||||
sendInState2Target(ISchedulers.START);
|
||||
}
|
||||
|
||||
@Override public void onResume(long resumeLocation) {
|
||||
saveData(IEntity.STATE_RUNNING, resumeLocation);
|
||||
sendInState2Target(ISchedulers.RESUME);
|
||||
}
|
||||
|
||||
@Override public void onProgress(long currentLocation) {
|
||||
mEntity.setCurrentProgress(currentLocation);
|
||||
long speed = currentLocation - mLastLen;
|
||||
if (isFirst) {
|
||||
speed = 0;
|
||||
isFirst = false;
|
||||
}
|
||||
handleSpeed(speed);
|
||||
sendInState2Target(ISchedulers.RUNNING);
|
||||
if (System.currentTimeMillis() - mLastSaveTime >= RUN_SAVE_INTERVAL) {
|
||||
saveData(IEntity.STATE_RUNNING, currentLocation);
|
||||
mLastSaveTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
mLastLen = currentLocation;
|
||||
}
|
||||
|
||||
@Override public void onStop(long stopLocation) {
|
||||
saveData(mTask.getSchedulerType() == TaskSchedulerType.TYPE_STOP_AND_WAIT ? IEntity.STATE_WAIT
|
||||
: IEntity.STATE_STOP, stopLocation);
|
||||
handleSpeed(0);
|
||||
sendInState2Target(ISchedulers.STOP);
|
||||
}
|
||||
|
||||
@Override public void onComplete() {
|
||||
saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize());
|
||||
handleSpeed(0);
|
||||
sendInState2Target(ISchedulers.COMPLETE);
|
||||
}
|
||||
|
||||
@Override public void onCancel() {
|
||||
saveData(IEntity.STATE_CANCEL, -1);
|
||||
handleSpeed(0);
|
||||
if (mTask.getSchedulerType() != TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||
ALog.d(TAG, "删除任务完成");
|
||||
sendInState2Target(ISchedulers.CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onFail(boolean needRetry, AriaException e) {
|
||||
mEntity.setFailNum(mEntity.getFailNum() + 1);
|
||||
saveData(IEntity.STATE_FAIL, mEntity.getCurrentProgress());
|
||||
handleSpeed(0);
|
||||
mTask.setNeedRetry(needRetry);
|
||||
mTask.putExpand(AbsTask.ERROR_INFO_KEY, e);
|
||||
sendInState2Target(ISchedulers.FAIL);
|
||||
if (e != null) {
|
||||
String error = ALog.getExceptionString(e);
|
||||
ALog.e(TAG, error);
|
||||
ErrorHelp.saveError(e.getMessage(), error);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSpeed(long speed) {
|
||||
if (mUpdateInterval != 1000) {
|
||||
speed = speed * 1000 / mUpdateInterval;
|
||||
}
|
||||
if (isConvertSpeed) {
|
||||
mEntity.setConvertSpeed(CommonUtil.formatFileSize(speed < 0 ? 0 : speed) + "/s");
|
||||
}
|
||||
mEntity.setSpeed(speed < 0 ? 0 : speed);
|
||||
int taskType = mTaskWrapper.getRequestType();
|
||||
if (taskType != ITaskWrapper.M3U8_VOD && taskType != ITaskWrapper.M3U8_LIVE) {
|
||||
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
|
||||
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
|
||||
}
|
||||
if (mEntity.getFileSize() != 0) {
|
||||
if (speed == 0) {
|
||||
mEntity.setTimeLeft(Integer.MAX_VALUE);
|
||||
} else {
|
||||
mEntity.setTimeLeft((int) ((mEntity.getFileSize() - mEntity.getCurrentProgress()) / speed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务完成后的情况
|
||||
*/
|
||||
private void handleComplete() {
|
||||
mEntity.setComplete(true);
|
||||
mEntity.setCompleteTime(System.currentTimeMillis());
|
||||
mEntity.setCurrentProgress(mEntity.getFileSize());
|
||||
mEntity.setPercent(100);
|
||||
handleSpeed(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务取消
|
||||
*/
|
||||
protected abstract void handleCancel();
|
||||
|
||||
/**
|
||||
* 将任务状态发送给下载器
|
||||
*
|
||||
* @param state {@link ISchedulers#START}
|
||||
*/
|
||||
protected void sendInState2Target(int state) {
|
||||
if (outHandler.get() != null) {
|
||||
outHandler.get().obtainMessage(state, mTask).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
protected void saveData(int state, long location) {
|
||||
mEntity.setState(state);
|
||||
|
||||
if (state == IEntity.STATE_CANCEL) {
|
||||
handleCancel();
|
||||
return;
|
||||
} else if (state == IEntity.STATE_STOP) {
|
||||
mEntity.setStopTime(System.currentTimeMillis());
|
||||
} else if (state == IEntity.STATE_COMPLETE) {
|
||||
handleComplete();
|
||||
}
|
||||
if (location > 0) {
|
||||
mEntity.setCurrentProgress(location);
|
||||
}
|
||||
mEntity.update();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||
import com.arialyy.aria.core.task.UploadTask;
|
||||
import com.arialyy.aria.util.DeleteURecord;
|
||||
|
||||
/**
|
||||
* 下载监听类
|
||||
*/
|
||||
public class BaseUListener extends BaseListener implements IUploadListener {
|
||||
|
||||
@Override protected void handleCancel() {
|
||||
int sType = getTask(UploadTask.class).getSchedulerType();
|
||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||
mEntity.setComplete(false);
|
||||
mEntity.setState(IEntity.STATE_WAIT);
|
||||
DeleteURecord.getInstance().deleteRecord(mEntity, mTaskWrapper.isRemoveFile(), false);
|
||||
} else {
|
||||
DeleteURecord.getInstance().deleteRecord(mEntity, mTaskWrapper.isRemoveFile(), true);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
import com.arialyy.aria.core.group.GroupSendParams;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.core.task.DownloadGroupTask;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.DeleteDGRecord;
|
||||
import com.arialyy.aria.util.ErrorHelp;
|
||||
|
||||
import static com.arialyy.aria.core.task.AbsTask.ERROR_INFO_KEY;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/20. 任务组下载事件
|
||||
*/
|
||||
public class DownloadGroupListener extends BaseListener implements IDGroupListener {
|
||||
private GroupSendParams<DownloadGroupTask, DownloadEntity> mSeedEntity;
|
||||
|
||||
@Override public IEventListener setParams(AbsTask task, Handler outHandler) {
|
||||
IEventListener listener = super.setParams(task, outHandler);
|
||||
mSeedEntity = new GroupSendParams<>();
|
||||
mSeedEntity.groupTask = (DownloadGroupTask) task;
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubPre(DownloadEntity subEntity) {
|
||||
handleSubSpeed(subEntity, 0);
|
||||
saveSubState(IEntity.STATE_PRE, subEntity);
|
||||
sendInState2Target(ISchedulers.SUB_PRE, subEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void supportBreakpoint(boolean support, DownloadEntity subEntity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubStart(DownloadEntity subEntity) {
|
||||
handleSubSpeed(subEntity, 0);
|
||||
saveSubState(IEntity.STATE_RUNNING, subEntity);
|
||||
sendInState2Target(ISchedulers.SUB_START, subEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubStop(DownloadEntity subEntity, long stopLocation) {
|
||||
subEntity.setCurrentProgress(stopLocation);
|
||||
handleSubSpeed(subEntity, 0);
|
||||
saveSubState(IEntity.STATE_STOP, subEntity);
|
||||
saveCurrentLocation();
|
||||
sendInState2Target(ISchedulers.SUB_STOP, subEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubComplete(DownloadEntity subEntity) {
|
||||
handleSubSpeed(subEntity, 0);
|
||||
saveSubState(IEntity.STATE_COMPLETE, subEntity);
|
||||
saveCurrentLocation();
|
||||
sendInState2Target(ISchedulers.SUB_COMPLETE, subEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubFail(DownloadEntity subEntity, AriaException e) {
|
||||
handleSubSpeed(subEntity, 0);
|
||||
saveSubState(IEntity.STATE_FAIL, subEntity);
|
||||
saveCurrentLocation();
|
||||
mSeedEntity.groupTask.putExpand(ERROR_INFO_KEY, e);
|
||||
sendInState2Target(ISchedulers.SUB_FAIL, subEntity);
|
||||
if (e != null) {
|
||||
e.printStackTrace();
|
||||
ErrorHelp.saveError("", ALog.getExceptionString(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubCancel(DownloadEntity subEntity) {
|
||||
handleSubSpeed(subEntity, 0);
|
||||
saveSubState(IEntity.STATE_CANCEL, subEntity);
|
||||
saveCurrentLocation();
|
||||
sendInState2Target(ISchedulers.SUB_CANCEL, subEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubRunning(DownloadEntity subEntity, long currentProgress) {
|
||||
|
||||
handleSubSpeed(subEntity, currentProgress);
|
||||
if (System.currentTimeMillis() - mLastSaveTime >= RUN_SAVE_INTERVAL) {
|
||||
saveSubState(IEntity.STATE_RUNNING, subEntity);
|
||||
mLastSaveTime = System.currentTimeMillis();
|
||||
}
|
||||
sendInState2Target(ISchedulers.SUB_RUNNING, subEntity);
|
||||
}
|
||||
|
||||
private void handleSubSpeed(DownloadEntity subEntity, long currentProgress) {
|
||||
if (currentProgress == 0) {
|
||||
subEntity.setSpeed(0);
|
||||
subEntity.setConvertSpeed("0kb/s");
|
||||
return;
|
||||
}
|
||||
long speed = currentProgress - subEntity.getCurrentProgress();
|
||||
subEntity.setSpeed(speed);
|
||||
subEntity.setConvertSpeed(
|
||||
speed <= 0 ? "" : String.format("%s/s", CommonUtil.formatFileSize(speed)));
|
||||
subEntity.setPercent((int) (subEntity.getFileSize() <= 0 ? 0
|
||||
: subEntity.getCurrentProgress() * 100 / subEntity.getFileSize()));
|
||||
subEntity.setCurrentProgress(currentProgress);
|
||||
|
||||
if (speed == 0) {
|
||||
subEntity.setTimeLeft(Integer.MAX_VALUE);
|
||||
} else {
|
||||
subEntity.setTimeLeft(
|
||||
(int) ((subEntity.getFileSize() - subEntity.getCurrentProgress()) / speed));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将任务状态发送给下载器
|
||||
*
|
||||
* @param state {@link ISchedulers#START}
|
||||
*/
|
||||
private void sendInState2Target(int state, DownloadEntity subEntity) {
|
||||
if (outHandler.get() != null) {
|
||||
mSeedEntity.entity = subEntity;
|
||||
outHandler.get().obtainMessage(state, ISchedulers.IS_SUB_TASK, 0, mSeedEntity).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSubState(int state, DownloadEntity subEntity) {
|
||||
subEntity.setState(state);
|
||||
if (state == IEntity.STATE_STOP) {
|
||||
subEntity.setStopTime(System.currentTimeMillis());
|
||||
} else if (state == IEntity.STATE_COMPLETE) {
|
||||
subEntity.setComplete(true);
|
||||
subEntity.setCompleteTime(System.currentTimeMillis());
|
||||
subEntity.setCurrentProgress(subEntity.getFileSize());
|
||||
subEntity.setPercent(100);
|
||||
subEntity.setConvertSpeed("0kb/s");
|
||||
subEntity.setSpeed(0);
|
||||
}
|
||||
subEntity.update();
|
||||
}
|
||||
|
||||
private void saveCurrentLocation() {
|
||||
DownloadGroupEntity dgEntity = (DownloadGroupEntity) mEntity;
|
||||
if (dgEntity.getSubEntities() == null || dgEntity.getSubEntities().isEmpty()) {
|
||||
ALog.w(TAG, "保存进度失败,子任务为null");
|
||||
return;
|
||||
}
|
||||
long location = 0;
|
||||
for (DownloadEntity e : dgEntity.getSubEntities()) {
|
||||
location += e.getCurrentProgress();
|
||||
}
|
||||
if (location > mEntity.getFileSize()) {
|
||||
location = mEntity.getFileSize();
|
||||
}
|
||||
mEntity.setCurrentProgress(location);
|
||||
mEntity.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostPre(long fileSize) {
|
||||
mEntity.setFileSize(fileSize);
|
||||
mEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize));
|
||||
saveData(IEntity.STATE_POST_PRE, -1);
|
||||
sendInState2Target(ISchedulers.POST_PRE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void supportBreakpoint(boolean support) {
|
||||
|
||||
}
|
||||
|
||||
@Override protected void handleCancel() {
|
||||
int sType = getTask(DownloadGroupTask.class).getSchedulerType();
|
||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||
mEntity.setComplete(false);
|
||||
mEntity.setState(IEntity.STATE_WAIT);
|
||||
DeleteDGRecord.getInstance().deleteRecord(mEntity, mTaskWrapper.isRemoveFile(), false);
|
||||
} else {
|
||||
DeleteDGRecord.getInstance().deleteRecord(mEntity, mTaskWrapper.isRemoveFile(), true);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/20.
|
||||
* 下载任务组事件
|
||||
*/
|
||||
public interface IDGroupListener extends IDLoadListener {
|
||||
|
||||
/**
|
||||
* 子任务预处理
|
||||
*/
|
||||
void onSubPre(DownloadEntity subEntity);
|
||||
|
||||
/**
|
||||
* 子任务支持断点回调
|
||||
*
|
||||
* @param support true,支持;false 不支持
|
||||
*/
|
||||
void supportBreakpoint(boolean support, DownloadEntity subEntity);
|
||||
|
||||
/**
|
||||
* 子任务开始下载\恢复下载
|
||||
*/
|
||||
void onSubStart(DownloadEntity subEntity);
|
||||
|
||||
/**
|
||||
* 子任务停止下载
|
||||
*/
|
||||
void onSubStop(DownloadEntity subEntity, long stopLocation);
|
||||
|
||||
/**
|
||||
* 子任务下载完成
|
||||
*/
|
||||
void onSubComplete(DownloadEntity subEntity);
|
||||
|
||||
/**
|
||||
* 子任务下载失败
|
||||
*/
|
||||
void onSubFail(DownloadEntity subEntity, AriaException e);
|
||||
|
||||
/**
|
||||
* 子任务取消下载
|
||||
*/
|
||||
void onSubCancel(DownloadEntity subEntity);
|
||||
|
||||
/**
|
||||
* 子任务执行中
|
||||
*
|
||||
* @param currentProgress 当前进度
|
||||
*/
|
||||
void onSubRunning(DownloadEntity subEntity, long currentProgress);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
/**
|
||||
* 下载监听
|
||||
*/
|
||||
public interface IDLoadListener extends IEventListener {
|
||||
|
||||
/**
|
||||
* 预处理完成,准备下载---开始下载之间
|
||||
*/
|
||||
void onPostPre(long fileSize);
|
||||
|
||||
/**
|
||||
* 支持断点回调
|
||||
*
|
||||
* @param support true,支持;false 不支持
|
||||
*/
|
||||
void supportBreakpoint(boolean support);
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/18.
|
||||
* 基础事件
|
||||
*/
|
||||
public interface IEventListener {
|
||||
|
||||
IEventListener setParams(AbsTask task, Handler outHandler);
|
||||
|
||||
/**
|
||||
* 预处理,有时有些地址链接比较慢,这时可以先在这个地方出来一些界面上的UI,如按钮的状态
|
||||
*/
|
||||
void onPre();
|
||||
|
||||
/**
|
||||
* 开始
|
||||
*/
|
||||
void onStart(long startLocation);
|
||||
|
||||
/**
|
||||
* 恢复位置
|
||||
*/
|
||||
void onResume(long resumeLocation);
|
||||
|
||||
/**
|
||||
* 下载监听
|
||||
*/
|
||||
void onProgress(long currentLocation);
|
||||
|
||||
/**
|
||||
* 停止
|
||||
*/
|
||||
void onStop(long stopLocation);
|
||||
|
||||
/**
|
||||
* 下载完成
|
||||
*/
|
||||
void onComplete();
|
||||
|
||||
/**
|
||||
* 取消下载
|
||||
*/
|
||||
void onCancel();
|
||||
|
||||
/**
|
||||
* 下载失败
|
||||
*
|
||||
* @param needRetry 是否需要重试{@code true} 需要
|
||||
* @param e 失败信息
|
||||
*/
|
||||
void onFail(boolean needRetry, AriaException e);
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
import com.arialyy.aria.core.task.ITask;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/11/2. 调度器功能接口
|
||||
*/
|
||||
public interface ISchedulers extends Handler.Callback {
|
||||
|
||||
String ARIA_TASK_INFO_ACTION = "ARIA_TASK_INFO_ACTION";
|
||||
/**
|
||||
* 广播接收器中通过TASK_TYPE字段获取任务类型 {@link ITask#DOWNLOAD}、{@link ITask#DOWNLOAD_GROUP}、{@link
|
||||
* ITask#UPLOAD}、{@link ITask#DOWNLOAD_GROUP_SUB}
|
||||
*/
|
||||
String TASK_TYPE = "ARIA_TASK_TYPE";
|
||||
|
||||
/**
|
||||
* 广播接收器中通过TASK_STATE字段获取任务状态
|
||||
*
|
||||
* 普通任务的有: {@link #NO_SUPPORT_BREAK_POINT}、{@link #PRE}、{@link
|
||||
* #POST_PRE}、{@link #START}、{@link #STOP}、{@link #FAIL}、{@link #CANCEL}、{@link #COMPLETE}、{@link
|
||||
* #RUNNING}、{@link #RESUME}、{@link #WAIT}
|
||||
*
|
||||
* 子任务的有:{@link #SUB_PRE}、{@link #SUB_START}、{@link
|
||||
* #SUB_STOP}、{@link #SUB_CANCEL}、{@link #SUB_FAIL}、{@link #SUB_RUNNING}、{@link #SUB_COMPLETE}
|
||||
*/
|
||||
String TASK_STATE = "ARIA_TASK_STATE";
|
||||
|
||||
/**
|
||||
* 广播接收器中通过TASK_ENTITY字段获取任务实体 {@link DownloadEntity}、{@link UploadEntity}、{@link
|
||||
* DownloadGroupEntity}
|
||||
*/
|
||||
String TASK_ENTITY = "ARIA_TASK_ENTITY";
|
||||
|
||||
/**
|
||||
* 任务速度,单位:byte/s
|
||||
*/
|
||||
String TASK_SPEED = "ARIA_TASK_SPEED";
|
||||
|
||||
/**
|
||||
* 任务进度
|
||||
*/
|
||||
String TASK_PERCENT = "ARIA_TASK_PERCENT";
|
||||
|
||||
/**
|
||||
* M3U8地址
|
||||
*/
|
||||
String DATA_M3U8_URL = "DATA_M3U8_URL";
|
||||
/**
|
||||
* 当前下载完成的切片地址
|
||||
*/
|
||||
String DATA_M3U8_PEER_PATH = "DATA_M3U8_PEER_PATH";
|
||||
/**
|
||||
* 当前下载完成的切片索引
|
||||
*/
|
||||
String DATA_M3U8_PEER_INDEX = "DATA_M3U8_PEER_INDEX";
|
||||
|
||||
/**
|
||||
* 组合任务任务标志
|
||||
*/
|
||||
int IS_SUB_TASK = 0xd1;
|
||||
|
||||
/**
|
||||
* m3u8切片任务标志
|
||||
*/
|
||||
int IS_M3U8_PEER = 0xd2;
|
||||
|
||||
/**
|
||||
* 任务不支持断点
|
||||
*/
|
||||
int NO_SUPPORT_BREAK_POINT = 9;
|
||||
/**
|
||||
* 任务预加载
|
||||
*/
|
||||
int PRE = 0;
|
||||
/**
|
||||
* 任务预加载完成
|
||||
*/
|
||||
int POST_PRE = 1;
|
||||
|
||||
/**
|
||||
* 任务开始
|
||||
*/
|
||||
int START = 2;
|
||||
/**
|
||||
* 任务停止
|
||||
*/
|
||||
int STOP = 3;
|
||||
/**
|
||||
* 任务失败
|
||||
*/
|
||||
int FAIL = 4;
|
||||
/**
|
||||
* 任务取消
|
||||
*/
|
||||
int CANCEL = 5;
|
||||
/**
|
||||
* 任务完成
|
||||
*/
|
||||
int COMPLETE = 6;
|
||||
/**
|
||||
* 任务处理中
|
||||
*/
|
||||
int RUNNING = 7;
|
||||
/**
|
||||
* 恢复任务
|
||||
*/
|
||||
int RESUME = 8;
|
||||
/**
|
||||
* 等待
|
||||
*/
|
||||
int WAIT = 10;
|
||||
/**
|
||||
* 检查信息失败
|
||||
*/
|
||||
int CHECK_FAIL = 11;
|
||||
|
||||
/**
|
||||
* 组合任务子任务预处理
|
||||
*/
|
||||
int SUB_PRE = 0xa1;
|
||||
|
||||
/**
|
||||
* 组合任务子任务开始
|
||||
*/
|
||||
int SUB_START = 0xa2;
|
||||
|
||||
/**
|
||||
* 组合任务子任务停止
|
||||
*/
|
||||
int SUB_STOP = 0xa3;
|
||||
|
||||
/**
|
||||
* 组合任务子任务取消
|
||||
*/
|
||||
int SUB_CANCEL = 0xa4;
|
||||
|
||||
/**
|
||||
* 组合任务子任务失败
|
||||
*/
|
||||
int SUB_FAIL = 0xa5;
|
||||
|
||||
/**
|
||||
* 组合任务子任务执行执行中
|
||||
*/
|
||||
int SUB_RUNNING = 0xa6;
|
||||
|
||||
/**
|
||||
* 组合任务子任务完成
|
||||
*/
|
||||
int SUB_COMPLETE = 0xa7;
|
||||
|
||||
/**
|
||||
* M3U8切片开始下载
|
||||
*/
|
||||
int M3U8_PEER_START = 0xb1;
|
||||
/**
|
||||
* M3U8切片下载完成
|
||||
*/
|
||||
int M3U8_PEER_COMPLETE = 0xb2;
|
||||
/**
|
||||
* M3U8切片下载失败
|
||||
*/
|
||||
int M3U8_PEER_FAIL = 0xb3;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/2/9.
|
||||
* 上传监听
|
||||
*/
|
||||
public interface IUploadListener extends IEventListener {
|
||||
|
||||
}
|
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/7/1.
|
||||
* 任务执行器,用于处理任务的开始,停止
|
||||
* 流程:
|
||||
* 1、获取任务记录
|
||||
* 2、创建任务状态管理器,用于管理任务的状态
|
||||
* 3、创建文件信息获取器,获取文件信息,根据文件信息执行任务
|
||||
* 4、创建线程任务执行下载、上传操作
|
||||
*/
|
||||
public abstract class AbsNormalLoader<T extends AbsTaskWrapper> implements ILoaderVisitor, ILoader {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
private IEventListener mListener;
|
||||
protected T mTaskWrapper;
|
||||
protected File mTempFile;
|
||||
|
||||
private List<IThreadTask> mTask = new ArrayList<>();
|
||||
private ScheduledThreadPoolExecutor mTimer;
|
||||
|
||||
/**
|
||||
* 进度刷新间隔
|
||||
*/
|
||||
private long mUpdateInterval = 1000;
|
||||
protected TaskRecord mRecord;
|
||||
protected boolean isCancel = false, isStop = false;
|
||||
private boolean isRuning = false;
|
||||
|
||||
protected IRecordHandler mRecordHandler;
|
||||
protected IThreadStateManager mStateManager;
|
||||
protected IInfoTask mInfoTask;
|
||||
protected IThreadTaskBuilder mTTBuilder;
|
||||
|
||||
protected AbsNormalLoader(T wrapper, IEventListener listener) {
|
||||
mListener = listener;
|
||||
mTaskWrapper = wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动线程任务
|
||||
*/
|
||||
protected abstract void handleTask(Looper looper);
|
||||
|
||||
/**
|
||||
* 获取文件长度
|
||||
*/
|
||||
public abstract long getFileSize();
|
||||
|
||||
protected IEventListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
protected IThreadStateManager getStateManager() {
|
||||
return mStateManager;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return mTaskWrapper.getKey();
|
||||
}
|
||||
|
||||
public List<IThreadTask> getTaskList() {
|
||||
return mTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置任务状态
|
||||
*/
|
||||
private void resetState() {
|
||||
closeTimer();
|
||||
if (mTask != null && mTask.size() != 0) {
|
||||
for (int i = 0; i < mTask.size(); i++) {
|
||||
mTask.get(i).breakTask();
|
||||
}
|
||||
mTask.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
checkComponent();
|
||||
if (isRunning()) {
|
||||
ALog.d(TAG, String.format("任务【%s】正在执行,启动任务失败", mTaskWrapper.getKey()));
|
||||
return;
|
||||
}
|
||||
startFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始流程
|
||||
*/
|
||||
private void startFlow() {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
Looper.prepare();
|
||||
Looper looper = Looper.myLooper();
|
||||
if (looper == Looper.getMainLooper()) {
|
||||
throw new IllegalThreadStateException("不能在主线程程序中调用Loader");
|
||||
}
|
||||
isRuning = true;
|
||||
resetState();
|
||||
onPostPre();
|
||||
handleTask(looper);
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 预处理完成
|
||||
*/
|
||||
protected void onPostPre() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟启动定时器
|
||||
*/
|
||||
protected long delayTimer() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动进度获取定时器
|
||||
*/
|
||||
protected synchronized void startTimer() {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
ALog.d(TAG, String.format("启动定时器,delayTimer = %s, updateInterval = %s", delayTimer(),
|
||||
mUpdateInterval));
|
||||
closeTimer();
|
||||
try {
|
||||
mTimer = new ScheduledThreadPoolExecutor(1);
|
||||
mTimer.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override public void run() {
|
||||
// 线程池中是不抛异常的,没有日志,很难定位问题,需要手动try-catch
|
||||
try {
|
||||
if (mStateManager == null) {
|
||||
ALog.e(TAG, "stateManager is null");
|
||||
} else if (mStateManager.isComplete()
|
||||
|| mStateManager.isFail()
|
||||
|| !isRunning()
|
||||
|| isBreak()) {
|
||||
//ALog.d(TAG, "isComplete = " + mStateManager.isComplete()
|
||||
// + "; isFail = " + mStateManager.isFail()
|
||||
// + "; isRunning = " + isRunning()
|
||||
// + "; isBreak = " + isBreak());
|
||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
||||
closeTimer();
|
||||
onDestroy();
|
||||
} else if (mStateManager.getCurrentProgress() >= 0) {
|
||||
Log.d(TAG, "running...");
|
||||
mListener.onProgress(mStateManager.getCurrentProgress());
|
||||
} else {
|
||||
Log.d(TAG, "未知状态");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, delayTimer(), mUpdateInterval, TimeUnit.MILLISECONDS);
|
||||
} catch (Exception e) {
|
||||
ALog.e(TAG, "启动定时器失败");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void closeTimer() {
|
||||
if (mTimer != null && !mTimer.isShutdown()) {
|
||||
mTimer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
isRuning = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置定时器更新间隔
|
||||
*
|
||||
* @param interval 单位毫秒,不能小于0
|
||||
*/
|
||||
protected void setUpdateInterval(long interval) {
|
||||
if (interval < 0) {
|
||||
ALog.w(TAG, "更新间隔不能小于0,默认为1000毫秒");
|
||||
return;
|
||||
}
|
||||
mUpdateInterval = interval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isRunning() {
|
||||
boolean b = ThreadTaskManager.getInstance().taskIsRunning(mTaskWrapper.getKey());
|
||||
//ALog.d(TAG, "isRunning = " + b);
|
||||
return b && isRuning;
|
||||
}
|
||||
|
||||
@Override final public synchronized void cancel() {
|
||||
if (isCancel) {
|
||||
ALog.d(TAG, String.format("任务【%s】正在删除,删除任务失败", mTaskWrapper.getKey()));
|
||||
return;
|
||||
}
|
||||
if (mInfoTask != null){
|
||||
mInfoTask.cancel();
|
||||
}
|
||||
closeTimer();
|
||||
isCancel = true;
|
||||
onCancel();
|
||||
for (int i = 0; i < mTask.size(); i++) {
|
||||
IThreadTask task = mTask.get(i);
|
||||
if (task != null && !task.isThreadComplete()) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
||||
onPostCancel();
|
||||
onDestroy();
|
||||
mListener.onCancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除线程任务前的操作
|
||||
*/
|
||||
protected void onCancel() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作处理完成
|
||||
*/
|
||||
protected void onPostCancel() {
|
||||
|
||||
}
|
||||
|
||||
final public synchronized void stop() {
|
||||
if (isStop) {
|
||||
return;
|
||||
}
|
||||
if (mInfoTask != null){
|
||||
mInfoTask.stop();
|
||||
}
|
||||
closeTimer();
|
||||
isStop = true;
|
||||
onStop();
|
||||
for (int i = 0; i < mTask.size(); i++) {
|
||||
IThreadTask task = mTask.get(i);
|
||||
if (task != null && !task.isThreadComplete()) {
|
||||
task.stop();
|
||||
}
|
||||
}
|
||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
||||
onPostStop();
|
||||
onDestroy();
|
||||
mListener.onStop(getCurrentProgress());
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止线程任务前的操作
|
||||
*/
|
||||
protected void onStop() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止操作完成
|
||||
*/
|
||||
protected void onPostStop() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试任务
|
||||
*/
|
||||
public void retryTask() {
|
||||
ALog.w(TAG, String.format("任务【%s】开始重试", mTaskWrapper.getKey()));
|
||||
startFlow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务是否已经中断
|
||||
*
|
||||
* @return {@code true}中断
|
||||
*/
|
||||
public boolean isBreak() {
|
||||
if (isCancel || isStop) {
|
||||
//closeTimer();
|
||||
ALog.d(TAG, "isCancel = " + isCancel + ", isStop = " + isStop);
|
||||
ALog.d(TAG, String.format("任务【%s】已停止或取消了", mTaskWrapper.getKey()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件: {@link #mRecordHandler}、{@link #mInfoTask}、{@link #mStateManager}、{@link #mTTBuilder}
|
||||
*/
|
||||
protected void checkComponent() {
|
||||
if (mRecordHandler == null) {
|
||||
throw new NullPointerException("任务记录组件为空");
|
||||
}
|
||||
if (mInfoTask == null) {
|
||||
throw new NullPointerException(("文件信息组件为空"));
|
||||
}
|
||||
if (mStateManager == null) {
|
||||
throw new NullPointerException("任务状态管理组件为空");
|
||||
}
|
||||
if (mTTBuilder == null) {
|
||||
throw new NullPointerException("线程任务组件为空");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import com.arialyy.aria.core.inf.IUtil;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2015/8/25.
|
||||
* HTTP\FTP单任务下载工具
|
||||
*/
|
||||
public abstract class AbsNormalLoaderUtil implements IUtil {
|
||||
protected String TAG = CommonUtil.getClassName(getClass());
|
||||
private IEventListener mListener;
|
||||
protected AbsNormalLoader mLoader;
|
||||
private AbsTaskWrapper mTaskWrapper;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
protected AbsNormalLoaderUtil() {
|
||||
}
|
||||
|
||||
@Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
|
||||
mTaskWrapper = taskWrapper;
|
||||
mListener = listener;
|
||||
mLoader = getLoader();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加载器
|
||||
*/
|
||||
public abstract AbsNormalLoader getLoader();
|
||||
|
||||
/**
|
||||
* 获取构造器
|
||||
*/
|
||||
public abstract LoaderStructure BuildLoaderStructure();
|
||||
|
||||
@Override public String getKey() {
|
||||
return mTaskWrapper.getKey();
|
||||
}
|
||||
|
||||
@Override public long getFileSize() {
|
||||
return mLoader.getFileSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前下载位置
|
||||
*/
|
||||
@Override public long getCurrentLocation() {
|
||||
return mLoader.getCurrentProgress();
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
return mLoader.isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消下载
|
||||
*/
|
||||
@Override public void cancel() {
|
||||
isCancel = true;
|
||||
mLoader.cancel();
|
||||
onCancel();
|
||||
}
|
||||
|
||||
protected void onCancel() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止下载
|
||||
*/
|
||||
@Override public void stop() {
|
||||
isStop = true;
|
||||
mLoader.stop();
|
||||
onStop();
|
||||
}
|
||||
|
||||
protected void onStop() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 多线程断点续传下载文件,开始下载
|
||||
*/
|
||||
@Override public void start() {
|
||||
if (isStop || isCancel) {
|
||||
ALog.w(TAG, "启动任务失败,任务已停止或已取消");
|
||||
return;
|
||||
}
|
||||
mListener.onPre();
|
||||
// 如果网址没有变,而服务器端端文件改变,以下代码就没有用了
|
||||
//if (mTaskWrapper.getEntity().getFileSize() <= 1
|
||||
// || mTaskWrapper.isRefreshInfo()
|
||||
// || mTaskWrapper.getRequestType() == AbsTaskWrapper.D_FTP
|
||||
// || mTaskWrapper.getState() == IEntity.STATE_FAIL) {
|
||||
// new Thread(createInfoThread()).create();
|
||||
//} else {
|
||||
// mDownloader.create();
|
||||
//}
|
||||
|
||||
BuildLoaderStructure();
|
||||
new Thread(mLoader).start();
|
||||
onStart();
|
||||
}
|
||||
|
||||
protected void onStart() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isStop() {
|
||||
return isStop;
|
||||
}
|
||||
|
||||
public boolean isCancel() {
|
||||
return isCancel;
|
||||
}
|
||||
|
||||
public IEventListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
protected void fail(AriaException e, boolean needRetry) {
|
||||
if (isStop || isCancel) {
|
||||
return;
|
||||
}
|
||||
mListener.onFail(needRetry, e);
|
||||
mLoader.onDestroy();
|
||||
}
|
||||
|
||||
public AbsTaskWrapper getTaskWrapper() {
|
||||
return mTaskWrapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.task.IThreadTaskAdapter;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.File;
|
||||
|
||||
public abstract class AbsNormalTTBuilderAdapter {
|
||||
protected String TAG = CommonUtil.getClassName(this);
|
||||
protected AbsTaskWrapper wrapper;
|
||||
private File tempFile;
|
||||
|
||||
public AbsNormalTTBuilderAdapter() {
|
||||
}
|
||||
|
||||
protected void setWrapper(AbsTaskWrapper wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
tempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建线程任务适配器
|
||||
*/
|
||||
public abstract IThreadTaskAdapter getAdapter(SubThreadConfig config);
|
||||
|
||||
/**
|
||||
* 处理新任务
|
||||
*
|
||||
* @param record 任务记录
|
||||
* @param totalThreadNum 任务的线程总数
|
||||
* @return {@code true}创建新任务成功
|
||||
*/
|
||||
public abstract boolean handleNewTask(TaskRecord record, int totalThreadNum);
|
||||
|
||||
/**
|
||||
* SubThreadConfig 模版,如果不使用该方法创建配置,则默认使用{@link #createNormalSubThreadConfig(Handler, ThreadRecord,
|
||||
* boolean, int)}创建配置
|
||||
*/
|
||||
protected SubThreadConfig getSubThreadConfig(Handler stateHandler, ThreadRecord threadRecord,
|
||||
boolean isBlock, int startNum) {
|
||||
return createNormalSubThreadConfig(stateHandler, threadRecord, isBlock, startNum);
|
||||
}
|
||||
|
||||
private SubThreadConfig createNormalSubThreadConfig(Handler stateHandler,
|
||||
ThreadRecord threadRecord,
|
||||
boolean isBlock, int startNum) {
|
||||
SubThreadConfig config = new SubThreadConfig();
|
||||
config.url = getEntity().isRedirect() ? getEntity().getRedirectUrl() : getEntity().getUrl();
|
||||
config.tempFile =
|
||||
isBlock ? new File(
|
||||
String.format(IRecordHandler.SUB_PATH, tempFile.getPath(), threadRecord.threadId))
|
||||
: tempFile;
|
||||
config.isBlock = isBlock;
|
||||
config.startThreadNum = startNum;
|
||||
config.taskWrapper = wrapper;
|
||||
config.record = threadRecord;
|
||||
config.stateHandler = stateHandler;
|
||||
config.threadType = SubThreadConfig.getThreadType(wrapper.getRequestType());
|
||||
config.updateInterval = SubThreadConfig.getUpdateInterval(wrapper.getRequestType());
|
||||
return config;
|
||||
}
|
||||
|
||||
protected AbsNormalEntity getEntity() {
|
||||
return (AbsNormalEntity) wrapper.getEntity();
|
||||
}
|
||||
|
||||
protected File getTempFile() {
|
||||
return tempFile;
|
||||
}
|
||||
}
|
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 线程任务管理器,用于处理多线程下载时任务的状态回调
|
||||
*/
|
||||
public class GroupSubThreadStateManager implements IThreadStateManager {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
/**
|
||||
* 任务状态回调
|
||||
*/
|
||||
private Handler mHandler;//SimpleSchedulers
|
||||
private int mThreadNum; // 启动的线程总数
|
||||
private AtomicInteger mCancelNum = new AtomicInteger(0); // 已经取消的线程的数
|
||||
private AtomicInteger mStopNum = new AtomicInteger(0); // 已经停止的线程数
|
||||
private AtomicInteger mFailNum = new AtomicInteger(0); // 失败的线程数
|
||||
private AtomicInteger mCompleteNum = new AtomicInteger(0); // 完成的线程数
|
||||
private long mProgress; //当前总进度
|
||||
private TaskRecord mTaskRecord; // 任务记录
|
||||
private Looper mLooper;
|
||||
private String mKey;
|
||||
/**
|
||||
* @param handler 任务事件
|
||||
*/
|
||||
public GroupSubThreadStateManager(Handler handler,String key) {
|
||||
mHandler = handler;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
@Override public void setLooper(TaskRecord taskRecord, Looper looper) {
|
||||
mTaskRecord = taskRecord;
|
||||
mThreadNum = mTaskRecord.threadNum;
|
||||
mLooper = looper;
|
||||
}
|
||||
|
||||
private void checkLooper() {
|
||||
if (mTaskRecord == null) {
|
||||
throw new NullPointerException("任务记录为空");
|
||||
}
|
||||
if (mLooper == null) {
|
||||
throw new NullPointerException("Looper为空");
|
||||
}
|
||||
}
|
||||
|
||||
private Handler.Callback callback = new Handler.Callback() {
|
||||
@Override public boolean handleMessage(Message msg) {
|
||||
checkLooper();
|
||||
switch (msg.what) {
|
||||
case STATE_STOP:
|
||||
mStopNum.getAndIncrement();
|
||||
if (isStop()) {
|
||||
quitLooper();
|
||||
}
|
||||
sendMessageFromMsg(msg);
|
||||
break;
|
||||
case STATE_CANCEL:
|
||||
mCancelNum.getAndIncrement();
|
||||
if (isCancel()) {
|
||||
quitLooper();
|
||||
}
|
||||
sendMessageFromMsg(msg);
|
||||
break;
|
||||
case STATE_FAIL:
|
||||
mFailNum.getAndIncrement();
|
||||
if (isFail()) {
|
||||
sendMessageFromMsg(msg);
|
||||
|
||||
/* Bundle b = msg.getData();
|
||||
mListener.onFail(b.getBoolean(DATA_RETRY, false),
|
||||
(AriaException) b.getSerializable(DATA_ERROR_INFO));*/
|
||||
quitLooper();
|
||||
}
|
||||
//sendMessageFromMsg(msg);
|
||||
break;
|
||||
case STATE_COMPLETE:
|
||||
mCompleteNum.getAndIncrement();
|
||||
if (isComplete()) {
|
||||
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
||||
//if (mTaskRecord.taskType == ITaskWrapper.D_SFTP) {
|
||||
// mergerSFtp();
|
||||
// mListener.onComplete();
|
||||
//} else
|
||||
|
||||
if (mTaskRecord.isBlock) {
|
||||
/*if (mergeFile()) {
|
||||
mListener.onComplete();
|
||||
} else {
|
||||
mListener.onFail(false, null);
|
||||
}*/
|
||||
if (!mergeFile()) {
|
||||
Bundle b=msg.getData();
|
||||
b.putBoolean(IThreadStateManager.DATA_RETRY, false);
|
||||
msg.setData(b);
|
||||
msg.what = STATE_FAIL;
|
||||
sendMessageFromMsg(msg);
|
||||
}
|
||||
sendMessageFromMsg(msg);
|
||||
} else {
|
||||
sendMessageFromMsg(msg);
|
||||
//mListener.onComplete();
|
||||
}
|
||||
quitLooper();
|
||||
}else if (isFail()) {
|
||||
sendMessageFromMsg(msg);
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
Bundle b = msg.getData();
|
||||
if (b != null) {
|
||||
long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0);
|
||||
mProgress += len;
|
||||
}
|
||||
msg.obj=mProgress;
|
||||
sendMessageFromMsg(msg);
|
||||
|
||||
break;
|
||||
case STATE_UPDATE_PROGRESS:
|
||||
|
||||
if (msg.obj == null) {
|
||||
mProgress = updateBlockProgress();
|
||||
} else if (msg.obj instanceof Long) {
|
||||
mProgress = (long) msg.obj;
|
||||
}
|
||||
msg.obj=mProgress;
|
||||
sendMessageFromMsg(msg);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void sendMessageFromMsg(Message msg){
|
||||
Message mMsg=mHandler.obtainMessage();
|
||||
Bundle b=mMsg.getData();
|
||||
b.putString(IThreadStateManager.DATA_THREAD_NAME,mKey);
|
||||
msg.setData(b);
|
||||
mMsg.copyFrom(msg);
|
||||
mHandler.sendMessage(mMsg);
|
||||
}
|
||||
};
|
||||
|
||||
@Override public void updateCurrentProgress(long currentProgress) {
|
||||
mProgress = currentProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出looper循环
|
||||
*/
|
||||
private void quitLooper() {
|
||||
mLooper.quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务下载进度
|
||||
*
|
||||
* @return 当前任务下载进度
|
||||
*/
|
||||
@Override
|
||||
public long getCurrentProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
@Override public Handler.Callback getHandlerCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经停止
|
||||
*/
|
||||
public boolean isStop() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isStop; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mStopNum.get() == mThreadNum || mStopNum.get() + mCompleteNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经失败
|
||||
*/
|
||||
@Override
|
||||
public boolean isFail() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isFail; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCompleteNum.get() != mThreadNum
|
||||
&& (mFailNum.get() == mThreadNum || mFailNum.get() + mCompleteNum.get() == mThreadNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经完成
|
||||
*/
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isComplete; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
||||
// mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCompleteNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经取消
|
||||
*/
|
||||
public boolean isCancel() {
|
||||
//ALog.d(TAG, String.format("isCancel; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
||||
// mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCancelNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新分块任务s的真实进度
|
||||
*/
|
||||
private long updateBlockProgress() {
|
||||
long size = 0;
|
||||
for (int i = 0, len = mTaskRecord.threadRecords.size(); i < len; i++) {
|
||||
File temp = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
if (temp.exists()) {
|
||||
size += temp.length();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并sftp的分块
|
||||
*/
|
||||
private boolean mergerSFtp() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(new File(mTaskRecord.filePath));
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength);
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
*
|
||||
* @return {@code true} 合并成功,{@code false}合并失败
|
||||
*/
|
||||
private boolean mergeFile() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(new File(mTaskRecord.filePath));
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
||||
if (isSuccess) {
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) {
|
||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s",
|
||||
targetFile.getName(), targetFile.length(), mTaskRecord.fileLength));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
ALog.e(TAG, "合并失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
|
||||
/**
|
||||
* 任务信息采集
|
||||
*/
|
||||
public interface IInfoTask extends ILoaderComponent {
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* 设置回调
|
||||
*/
|
||||
void setCallback(Callback callback);
|
||||
|
||||
/**
|
||||
* 任务停止
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* 任务取消
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
interface Callback {
|
||||
/**
|
||||
* 处理完成
|
||||
*
|
||||
* @param info 一些回调的信息
|
||||
*/
|
||||
void onSucceed(String key, CompleteInfo info);
|
||||
|
||||
/**
|
||||
* 请求失败
|
||||
*
|
||||
* @param e 错误信息
|
||||
*/
|
||||
void onFail(AbsEntity entity, AriaException e, boolean needRetry);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
public interface ILoader extends Runnable {
|
||||
|
||||
//void start();
|
||||
|
||||
/**
|
||||
* 任务是否在执行
|
||||
*
|
||||
* @return true 任务执行中
|
||||
*/
|
||||
boolean isRunning();
|
||||
|
||||
void cancel();
|
||||
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* 任务是否被中断(停止,取消)
|
||||
*
|
||||
* @return true 任务中断,false 任务没有中断
|
||||
*/
|
||||
boolean isBreak();
|
||||
|
||||
String getKey();
|
||||
|
||||
long getCurrentProgress();
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-18
|
||||
*/
|
||||
public interface ILoaderAdapter {
|
||||
|
||||
/**
|
||||
* 处理新任务
|
||||
*
|
||||
* @param record 任务记录
|
||||
* @param totalThreadNum 任务的线程总数
|
||||
* @return {@code true}创建新任务成功
|
||||
*/
|
||||
boolean handleNewTask(TaskRecord record, int totalThreadNum);
|
||||
|
||||
/**
|
||||
* 创建线程任务
|
||||
*/
|
||||
IThreadTask createThreadTask(SubThreadConfig config);
|
||||
|
||||
/**
|
||||
* 处理任务记录
|
||||
*/
|
||||
IRecordHandler recordHandler(AbsTaskWrapper wrapper);
|
||||
}
|
@ -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.loader;
|
||||
|
||||
/**
|
||||
* 加载器部件
|
||||
*/
|
||||
public interface ILoaderComponent {
|
||||
|
||||
void accept(ILoaderVisitor visitor);
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
|
||||
/**
|
||||
* 加载器访问者
|
||||
*/
|
||||
public interface ILoaderVisitor {
|
||||
|
||||
/**
|
||||
* 处理任务记录
|
||||
*/
|
||||
void addComponent(IRecordHandler recordHandler);
|
||||
|
||||
/**
|
||||
* 处理任务的文件信息
|
||||
*/
|
||||
void addComponent(IInfoTask infoTask);
|
||||
|
||||
/**
|
||||
* 线程状态
|
||||
*/
|
||||
void addComponent(IThreadStateManager threadState);
|
||||
|
||||
/**
|
||||
* 构造线程任务
|
||||
*/
|
||||
void addComponent(IThreadTaskBuilder builder);
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-18
|
||||
*/
|
||||
public interface IRecordHandler extends ILoaderComponent {
|
||||
|
||||
int TYPE_DOWNLOAD = 1;
|
||||
int TYPE_UPLOAD = 2;
|
||||
int TYPE_M3U8_VOD = 3;
|
||||
int TYPE_M3U8_LIVE = 4;
|
||||
|
||||
String STATE = "_state_";
|
||||
String RECORD = "_record_";
|
||||
/**
|
||||
* 小于1m的文件不启用多线程
|
||||
*/
|
||||
long SUB_LEN = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* 分块文件路径,文件路径.线程id.part
|
||||
*/
|
||||
String SUB_PATH = "%s.%s.part";
|
||||
|
||||
/**
|
||||
* 获取任务记录
|
||||
*/
|
||||
TaskRecord getRecord(long fileSize);
|
||||
|
||||
/**
|
||||
* 记录处理前的操作,可用来删除任务记录
|
||||
*/
|
||||
void onPre();
|
||||
|
||||
/**
|
||||
* 处理任务记录
|
||||
*/
|
||||
void handlerTaskRecord(TaskRecord record);
|
||||
|
||||
/**
|
||||
* 处理线程任务
|
||||
*
|
||||
* @param record 任务记录
|
||||
* @param threadId 线程id
|
||||
* @param startL 线程开始位置
|
||||
* @param endL 线程结束位置
|
||||
*/
|
||||
ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL);
|
||||
|
||||
/**
|
||||
* 新任务创建任务记录
|
||||
*/
|
||||
TaskRecord createTaskRecord(int threadNum);
|
||||
|
||||
/**
|
||||
* 配置新任务的线程数
|
||||
*
|
||||
* @return 新任务的线程数
|
||||
*/
|
||||
int initTaskThreadNum();
|
||||
|
||||
/**
|
||||
* 检查任务是否已完成
|
||||
*
|
||||
* @return true 任务已完成
|
||||
*/
|
||||
boolean checkTaskCompleted();
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 线程任务构造器
|
||||
*/
|
||||
public interface IThreadTaskBuilder extends ILoaderComponent {
|
||||
|
||||
/**
|
||||
* 构造线程任务
|
||||
*/
|
||||
List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler);
|
||||
|
||||
/**
|
||||
* 获取创建的线程任务数,需要先调用{@link #buildThreadTask(TaskRecord, Handler)}方法才能获取创建的线程任务数
|
||||
*/
|
||||
int getCreatedThreadNum();
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LoaderStructure {
|
||||
private List<ILoaderComponent> parts = new ArrayList<>();
|
||||
|
||||
public void accept(ILoaderVisitor visitor) {
|
||||
|
||||
for (ILoaderComponent part : parts) {
|
||||
part.accept(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将组件加入到集合,必须添加以下集合:
|
||||
* 1 {@link IRecordHandler}
|
||||
* 2 {@link IInfoTask}
|
||||
* 3 {@link IThreadStateManager}
|
||||
* 4 {@link IThreadTaskBuilder}
|
||||
*
|
||||
* @param component 待添加的组件
|
||||
*/
|
||||
public LoaderStructure addComponent(ILoaderComponent component) {
|
||||
parts.add(component);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.event.EventMsgUtil;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IDLoadListener;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单文件
|
||||
*/
|
||||
public class NormalLoader<T extends AbsTaskWrapper> extends AbsNormalLoader<T> {
|
||||
private int startThreadNum; //启动的线程数
|
||||
protected boolean isComplete = false;
|
||||
private Looper looper;
|
||||
|
||||
public NormalLoader(T wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
mTempFile = new File(getEntity().getFilePath());
|
||||
EventMsgUtil.getDefault().register(this);
|
||||
setUpdateInterval(wrapper.getConfig().getUpdateInterval());
|
||||
}
|
||||
|
||||
public AbsNormalEntity getEntity() {
|
||||
return (AbsNormalEntity) mTaskWrapper.getEntity();
|
||||
}
|
||||
|
||||
@Override public long getFileSize() {
|
||||
return getEntity().getFileSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大下载/上传速度AbsFtpInfoThread
|
||||
*
|
||||
* @param maxSpeed 单位为:kb
|
||||
*/
|
||||
protected void setMaxSpeed(int maxSpeed) {
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
if (threadTask != null && startThreadNum > 0) {
|
||||
threadTask.setMaxSpeed(maxSpeed / startThreadNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventMsgUtil.getDefault().unRegister(this);
|
||||
}
|
||||
|
||||
///**
|
||||
// * 如果使用"Content-Disposition"中的文件名,需要更新{@link #mTempFile}的路径
|
||||
// */
|
||||
//public void updateTempFile() {
|
||||
// if (!mTempFile.getPath().equals(getEntity().getFilePath())) {
|
||||
// boolean b = mTempFile.renameTo(new File(getEntity().getFilePath()));
|
||||
// ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败"));
|
||||
// }
|
||||
//}
|
||||
|
||||
protected Looper getLooper() {
|
||||
return looper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动单线程任务
|
||||
*/
|
||||
@Override
|
||||
public void handleTask(Looper looper) {
|
||||
if (isBreak() || isComplete) {
|
||||
return;
|
||||
}
|
||||
this.looper = looper;
|
||||
mInfoTask.run();
|
||||
}
|
||||
|
||||
protected void startThreadTask() {
|
||||
if (isBreak()){
|
||||
return;
|
||||
}
|
||||
|
||||
if (getListener() instanceof IDLoadListener) {
|
||||
((IDLoadListener) getListener()).onPostPre(getEntity().getFileSize());
|
||||
}
|
||||
File file = new File(getEntity().getFilePath());
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) {
|
||||
FileUtil.createDir(file.getPath());
|
||||
}
|
||||
// 处理记录、初始化状态管理器
|
||||
mRecord = mRecordHandler.getRecord(getFileSize());
|
||||
mStateManager.setLooper(mRecord, looper);
|
||||
getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord,
|
||||
new Handler(looper, mStateManager.getHandlerCallback())));
|
||||
startThreadNum = mTTBuilder.getCreatedThreadNum();
|
||||
|
||||
mStateManager.updateCurrentProgress(getEntity().getCurrentProgress());
|
||||
if (mStateManager.getCurrentProgress() > 0) {
|
||||
getListener().onResume(mStateManager.getCurrentProgress());
|
||||
} else {
|
||||
getListener().onStart(mStateManager.getCurrentProgress());
|
||||
}
|
||||
|
||||
// 启动线程任务
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask);
|
||||
}
|
||||
|
||||
// 启动定时器
|
||||
startTimer();
|
||||
}
|
||||
|
||||
@Override public long getCurrentProgress() {
|
||||
return isRunning() ? mStateManager.getCurrentProgress() : getEntity().getCurrentProgress();
|
||||
}
|
||||
|
||||
@Override public void addComponent(IRecordHandler recordHandler) {
|
||||
mRecordHandler = recordHandler;
|
||||
if (recordHandler.checkTaskCompleted()) {
|
||||
mRecord.deleteData();
|
||||
isComplete = true;
|
||||
getListener().onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void addComponent(IInfoTask infoTask) {
|
||||
mInfoTask = infoTask;
|
||||
infoTask.setCallback(new IInfoTask.Callback() {
|
||||
@Override public void onSucceed(String key, CompleteInfo info) {
|
||||
startThreadTask();
|
||||
}
|
||||
|
||||
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
|
||||
getListener().onFail(needRetry, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadStateManager threadState) {
|
||||
mStateManager = threadState;
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadTaskBuilder builder) {
|
||||
mTTBuilder = builder;
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package com.arialyy.aria.core.loader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.download.DGTaskWrapper;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.task.ThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class NormalTTBuilder implements IThreadTaskBuilder {
|
||||
protected String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
private Handler mStateHandler;
|
||||
private AbsTaskWrapper mWrapper;
|
||||
private TaskRecord mRecord;
|
||||
private int mTotalThreadNum;
|
||||
private int mStartThreadNum;
|
||||
private AbsNormalTTBuilderAdapter mAdapter;
|
||||
|
||||
public NormalTTBuilder(AbsTaskWrapper wrapper, AbsNormalTTBuilderAdapter adapter) {
|
||||
if (wrapper instanceof DGTaskWrapper) {
|
||||
throw new AssertionError("NormalTTBuilder 不适用于组合任务");
|
||||
}
|
||||
mWrapper = wrapper;
|
||||
mAdapter = adapter;
|
||||
mAdapter.setWrapper(wrapper);
|
||||
}
|
||||
|
||||
protected AbsNormalEntity getEntity() {
|
||||
return (AbsNormalEntity) mWrapper.getEntity();
|
||||
}
|
||||
|
||||
public AbsNormalTTBuilderAdapter getAdapter() {
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建线程任务
|
||||
*/
|
||||
private IThreadTask createThreadTask(SubThreadConfig config) {
|
||||
ThreadTask task = new ThreadTask(config);
|
||||
task.setAdapter(mAdapter.getAdapter(config));
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动断点任务时,创建单线程任务
|
||||
*
|
||||
* @param record 线程记录
|
||||
* @param startNum 启动的线程数
|
||||
*/
|
||||
private IThreadTask createSingThreadTask(ThreadRecord record, int startNum) {
|
||||
return createThreadTask(
|
||||
mAdapter.getSubThreadConfig(mStateHandler, record, mRecord.isBlock, startNum));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理不支持断点的任务
|
||||
*/
|
||||
private List<IThreadTask> handleNoSupportBP() {
|
||||
List<IThreadTask> list = new ArrayList<>();
|
||||
mStartThreadNum = 1;
|
||||
mRecord.isBlock = false;
|
||||
mRecord.update();
|
||||
IThreadTask task = createSingThreadTask(mRecord.threadRecords.get(0), 1);
|
||||
list.add(task);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支持断点的任务
|
||||
*/
|
||||
private List<IThreadTask> handleBreakpoint() {
|
||||
long fileLength = getEntity().getFileSize();
|
||||
long blockSize = fileLength / mTotalThreadNum;
|
||||
long currentProgress = 0;
|
||||
List<IThreadTask> threadTasks = new ArrayList<>(mTotalThreadNum);
|
||||
|
||||
mRecord.fileLength = fileLength;
|
||||
if (mWrapper.isNewTask() && !mAdapter.handleNewTask(mRecord, mTotalThreadNum)) {
|
||||
ALog.e(TAG, "初始化线程任务失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
for (ThreadRecord tr : mRecord.threadRecords) {
|
||||
if (!tr.isComplete) {
|
||||
mStartThreadNum++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mTotalThreadNum; i++) {
|
||||
long startL = i * blockSize, endL = (i + 1) * blockSize;
|
||||
ThreadRecord tr = mRecord.threadRecords.get(i);
|
||||
|
||||
if (tr.isComplete) {//该线程已经完成
|
||||
currentProgress += endL - startL;
|
||||
ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mWrapper.getKey(), i));
|
||||
Message msg = mStateHandler.obtainMessage();
|
||||
msg.what = IThreadStateManager.STATE_COMPLETE;
|
||||
Bundle b = msg.getData();
|
||||
if (b == null) {
|
||||
b = new Bundle();
|
||||
}
|
||||
b.putString(IThreadStateManager.DATA_THREAD_NAME,
|
||||
CommonUtil.getThreadName(getEntity().getKey(), tr.threadId));
|
||||
msg.setData(b);
|
||||
msg.sendToTarget();
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果有记录,则恢复任务
|
||||
long r = tr.startLocation;
|
||||
//记录的位置需要在线程区间中
|
||||
if (startL < r && r <= (i == (mTotalThreadNum - 1) ? fileLength : endL)) {
|
||||
currentProgress += r - startL;
|
||||
}
|
||||
ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", getEntity().getFileName(), i));
|
||||
|
||||
IThreadTask task = createSingThreadTask(tr, mStartThreadNum);
|
||||
if (task == null) {
|
||||
ALog.e(TAG, "创建线程任务失败");
|
||||
return null;
|
||||
}
|
||||
threadTasks.add(task);
|
||||
}
|
||||
if (currentProgress != getEntity().getCurrentProgress()) {
|
||||
ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress));
|
||||
getEntity().setCurrentProgress(currentProgress);
|
||||
}
|
||||
//mStateManager.updateProgress(currentProgress);
|
||||
return threadTasks;
|
||||
}
|
||||
|
||||
private List<IThreadTask> handleTask() {
|
||||
if (mWrapper.isSupportBP()) {
|
||||
return handleBreakpoint();
|
||||
} else {
|
||||
return handleNoSupportBP();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler) {
|
||||
mRecord = record;
|
||||
mStateHandler = stateHandler;
|
||||
mTotalThreadNum = mRecord.threadNum;
|
||||
return handleTask();
|
||||
}
|
||||
|
||||
@Override public int getCreatedThreadNum() {
|
||||
return mStartThreadNum;
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 线程任务管理器,用于处理多线程下载时任务的状态回调
|
||||
*/
|
||||
public class NormalThreadStateManager implements IThreadStateManager {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
/**
|
||||
* 任务状态回调
|
||||
*/
|
||||
private IEventListener mListener;
|
||||
private int mThreadNum; // 启动的线程总数
|
||||
private AtomicInteger mCancelNum = new AtomicInteger(0); // 已经取消的线程的数
|
||||
private AtomicInteger mStopNum = new AtomicInteger(0); // 已经停止的线程数
|
||||
private AtomicInteger mFailNum = new AtomicInteger(0); // 失败的线程数
|
||||
private AtomicInteger mCompleteNum = new AtomicInteger(0); // 完成的线程数
|
||||
private long mProgress; //当前总进度
|
||||
private TaskRecord mTaskRecord; // 任务记录
|
||||
private Looper mLooper;
|
||||
|
||||
/**
|
||||
* @param listener 任务事件
|
||||
*/
|
||||
public NormalThreadStateManager(IEventListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override public void setLooper(TaskRecord taskRecord, Looper looper) {
|
||||
mTaskRecord = taskRecord;
|
||||
mThreadNum = mTaskRecord.threadNum;
|
||||
mLooper = looper;
|
||||
}
|
||||
|
||||
private void checkLooper() {
|
||||
if (mTaskRecord == null) {
|
||||
throw new NullPointerException("任务记录为空");
|
||||
}
|
||||
if (mLooper == null) {
|
||||
throw new NullPointerException("Looper为空");
|
||||
}
|
||||
}
|
||||
|
||||
private Handler.Callback callback = new Handler.Callback() {
|
||||
@Override public boolean handleMessage(Message msg) {
|
||||
checkLooper();
|
||||
switch (msg.what) {
|
||||
case STATE_STOP:
|
||||
mStopNum.getAndIncrement();
|
||||
if (isStop()) {
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_CANCEL:
|
||||
mCancelNum.getAndIncrement();
|
||||
if (isCancel()) {
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_FAIL:
|
||||
mFailNum.getAndIncrement();
|
||||
if (isFail()) {
|
||||
Bundle b = msg.getData();
|
||||
mListener.onFail(b.getBoolean(DATA_RETRY, false),
|
||||
(AriaException) b.getSerializable(DATA_ERROR_INFO));
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_COMPLETE:
|
||||
mCompleteNum.getAndIncrement();
|
||||
if (isComplete()) {
|
||||
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
||||
//if (mTaskRecord.taskType == ITaskWrapper.D_SFTP) {
|
||||
// mergerSFtp();
|
||||
// mListener.onComplete();
|
||||
//} else
|
||||
if (mTaskRecord.isBlock || mTaskRecord.threadNum == 1) {
|
||||
if (mergeFile()) {
|
||||
mListener.onComplete();
|
||||
} else {
|
||||
mListener.onFail(false, null);
|
||||
}
|
||||
quitLooper();
|
||||
break;
|
||||
}
|
||||
mListener.onComplete();
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
Bundle b = msg.getData();
|
||||
if (b != null) {
|
||||
long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0);
|
||||
mProgress += len;
|
||||
}
|
||||
|
||||
break;
|
||||
case STATE_UPDATE_PROGRESS:
|
||||
if (msg.obj == null) {
|
||||
mProgress = updateBlockProgress();
|
||||
} else if (msg.obj instanceof Long) {
|
||||
mProgress = (long) msg.obj;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Override public void updateCurrentProgress(long currentProgress) {
|
||||
mProgress = currentProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出looper循环
|
||||
*/
|
||||
private void quitLooper() {
|
||||
mLooper.quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务下载进度
|
||||
*
|
||||
* @return 当前任务下载进度
|
||||
*/
|
||||
@Override
|
||||
public long getCurrentProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
@Override public Handler.Callback getHandlerCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经停止
|
||||
*/
|
||||
public boolean isStop() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isStop; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mStopNum.get() == mThreadNum || mStopNum.get() + mCompleteNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经失败
|
||||
*/
|
||||
@Override
|
||||
public boolean isFail() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isFail; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCompleteNum.get() != mThreadNum
|
||||
&& (mFailNum.get() == mThreadNum || mFailNum.get() + mCompleteNum.get() == mThreadNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经完成
|
||||
*/
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isComplete; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
||||
// mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCompleteNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经取消
|
||||
*/
|
||||
public boolean isCancel() {
|
||||
//ALog.d(TAG, String.format("isCancel; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
||||
// mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCancelNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新分块任务s的真实进度
|
||||
*/
|
||||
private long updateBlockProgress() {
|
||||
long size = 0;
|
||||
for (int i = 0, len = mTaskRecord.threadRecords.size(); i < len; i++) {
|
||||
File temp = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
if (temp.exists()) {
|
||||
size += temp.length();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并sftp的分块
|
||||
*/
|
||||
private boolean mergerSFtp() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(new File(mTaskRecord.filePath));
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength);
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
*
|
||||
* @return {@code true} 合并成功,{@code false}合并失败
|
||||
*/
|
||||
private boolean mergeFile() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
if (targetFile.exists()){
|
||||
//没有获得文件长度:不支持断点续传
|
||||
if (mTaskRecord.fileLength == 0 && targetFile.length() != 0) {
|
||||
return true;
|
||||
}
|
||||
if (targetFile.length() != 0 && targetFile.length() == mTaskRecord.fileLength) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FileUtil.deleteFile(targetFile);
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(targetFile);
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
||||
if (isSuccess) {
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) {
|
||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s",
|
||||
targetFile.getName(), targetFile.length(), mTaskRecord.fileLength));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
ALog.e(TAG, "合并失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.task.ThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 子任务加载器
|
||||
*/
|
||||
public final class SubLoader implements ILoader, ILoaderVisitor {
|
||||
private String TAG = CommonUtil.getClassName(this);
|
||||
// 是否需要获取信息
|
||||
private boolean needGetInfo = true;
|
||||
private Handler schedulers;
|
||||
private boolean isCancel = false, isStop = false;
|
||||
private AbsTaskWrapper wrapper;
|
||||
private IInfoTask infoTask;
|
||||
private IThreadTaskBuilder ttBuild;
|
||||
private IRecordHandler recordHandler;
|
||||
private List<IThreadTask> mTask = new ArrayList<>();
|
||||
private String parentKey;
|
||||
private TaskRecord record;
|
||||
protected IThreadStateManager mStateManager;
|
||||
|
||||
public SubLoader(AbsTaskWrapper wrapper, Handler schedulers) {
|
||||
this.wrapper = wrapper;
|
||||
this.schedulers = schedulers;
|
||||
}
|
||||
|
||||
public AbsTaskWrapper getWrapper() {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送状态到调度器
|
||||
*
|
||||
* @param state {@link IThreadStateManager}
|
||||
*/
|
||||
private void sendNormalState(int state) {
|
||||
Message msg = schedulers.obtainMessage();
|
||||
Bundle b = msg.getData();
|
||||
if (b == null) {
|
||||
b = new Bundle();
|
||||
}
|
||||
b.putString(IThreadStateManager.DATA_THREAD_NAME, getKey());
|
||||
msg.what = state;
|
||||
msg.setData(b);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送失败的状态
|
||||
*/
|
||||
private void sendFailState(boolean needRetry) {
|
||||
Message msg = schedulers.obtainMessage();
|
||||
Bundle b = msg.getData();
|
||||
if (b == null) {
|
||||
b = new Bundle();
|
||||
}
|
||||
b.putString(IThreadStateManager.DATA_THREAD_NAME, getKey());
|
||||
b.putBoolean(IThreadStateManager.DATA_RETRY, needRetry);
|
||||
msg.what = IThreadStateManager.STATE_FAIL;
|
||||
msg.setData(b);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
private void handlerTask() {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
Looper looper = Looper.myLooper();
|
||||
if (looper == null) {
|
||||
Looper.prepare();
|
||||
looper = Looper.myLooper();
|
||||
}
|
||||
|
||||
record = recordHandler.getRecord(wrapper.getEntity().getFileSize());
|
||||
if (record == null) {
|
||||
ALog.d(TAG, "子任务记录为空");
|
||||
sendFailState(false);
|
||||
return;
|
||||
}
|
||||
if (record.threadRecords != null
|
||||
&& !TextUtils.isEmpty(record.filePath)
|
||||
&& new File(record.filePath).exists()
|
||||
&& !record.threadRecords.isEmpty()
|
||||
&& record.threadRecords.get(0).isComplete) {
|
||||
ALog.d(TAG, "子任务已完成,key:" + wrapper.getKey());
|
||||
sendNormalState(IThreadStateManager.STATE_COMPLETE);
|
||||
return;
|
||||
}
|
||||
List<IThreadTask> task =
|
||||
ttBuild.buildThreadTask(record, new Handler(looper, mStateManager.getHandlerCallback()));
|
||||
mStateManager.setLooper(record, looper);
|
||||
if (task == null || task.isEmpty()) {
|
||||
ALog.e(TAG, "创建子任务的线程任务失败,key:" + wrapper.getKey());
|
||||
sendFailState(false);
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(parentKey)) {
|
||||
ALog.e(TAG, "parentKey为空");
|
||||
sendFailState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
sendNormalState(IThreadStateManager.STATE_PRE);
|
||||
mTask.addAll(task);
|
||||
try {
|
||||
for (IThreadTask iThreadTask : mTask) {
|
||||
ThreadTaskManager.getInstance().startThread(parentKey, iThreadTask);
|
||||
}
|
||||
|
||||
sendNormalState(IThreadStateManager.STATE_START);
|
||||
|
||||
mStateManager.updateCurrentProgress(getWrapper().getEntity().getCurrentProgress());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
public TaskRecord getRecord() {
|
||||
return record;
|
||||
}
|
||||
|
||||
public void setParentKey(String parentKey) {
|
||||
this.parentKey = parentKey;
|
||||
}
|
||||
|
||||
public void setNeedGetInfo(boolean needGetInfo) {
|
||||
this.needGetInfo = needGetInfo;
|
||||
}
|
||||
|
||||
public void retryTask() {
|
||||
try {
|
||||
if (!mTask.isEmpty()) {
|
||||
for (IThreadTask iThreadTask : mTask) {
|
||||
iThreadTask.call();
|
||||
}
|
||||
return;
|
||||
}
|
||||
ALog.e(TAG, "子任务的线程任务为空");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
if (isStop) {
|
||||
ALog.w(TAG, "子任务已停止");
|
||||
return;
|
||||
}
|
||||
isStop = true;
|
||||
if (infoTask != null){
|
||||
infoTask.stop();
|
||||
}
|
||||
for (IThreadTask iThreadTask : mTask) {
|
||||
iThreadTask.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean isRunning() {
|
||||
if (mTask.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (IThreadTask iThreadTask : mTask) {
|
||||
if (!iThreadTask.isBreak()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
if (isCancel) {
|
||||
ALog.w(TAG, "子任务已取消");
|
||||
return;
|
||||
}
|
||||
isCancel = true;
|
||||
if (infoTask != null){
|
||||
infoTask.cancel();
|
||||
}
|
||||
for (IThreadTask iThreadTask : mTask) {
|
||||
iThreadTask.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean isBreak() {
|
||||
if (isCancel || isStop) {
|
||||
ALog.d(TAG, "isCancel = " + isCancel + ", isStop = " + isStop);
|
||||
ALog.d(TAG, String.format("任务【%s】已停止或取消了", wrapper.getKey()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 线程名,一个子任务的loader只有一个线程,使用线程名标示key
|
||||
*
|
||||
* @return {@link ThreadTask#getThreadName()}
|
||||
*/
|
||||
@Override public String getKey() {
|
||||
return CommonUtil.getThreadName(wrapper.getKey(), 0);
|
||||
}
|
||||
|
||||
@Override public long getCurrentProgress() {
|
||||
return isRunning() ? mStateManager.getCurrentProgress()
|
||||
: getWrapper().getEntity().getCurrentProgress();
|
||||
}
|
||||
|
||||
@Override public void addComponent(IRecordHandler recordHandler) {
|
||||
this.recordHandler = recordHandler;
|
||||
}
|
||||
|
||||
@Override public void addComponent(IInfoTask infoTask) {
|
||||
this.infoTask = infoTask;
|
||||
infoTask.setCallback(new IInfoTask.Callback() {
|
||||
@Override public void onSucceed(String key, CompleteInfo info) {
|
||||
handlerTask();
|
||||
}
|
||||
|
||||
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
|
||||
sendFailState(needRetry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadStateManager threadState) {
|
||||
mStateManager = threadState;
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadTaskBuilder builder) {
|
||||
ttBuild = builder;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
checkComponent();
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
if (needGetInfo) {
|
||||
infoTask.run();
|
||||
return;
|
||||
}
|
||||
handlerTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件: {@link #recordHandler}、{@link #infoTask}、{@link #ttBuild}
|
||||
*/
|
||||
private void checkComponent() {
|
||||
if (recordHandler == null) {
|
||||
throw new NullPointerException("任务记录组件为空");
|
||||
}
|
||||
if (infoTask == null) {
|
||||
throw new NullPointerException(("文件信息组件为空"));
|
||||
}
|
||||
if (ttBuild == null) {
|
||||
throw new NullPointerException("线程任务组件为空");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import java.util.List;
|
||||
|
||||
public class SubTTBuilder implements IThreadTaskBuilder{
|
||||
|
||||
|
||||
|
||||
@Override public List<IThreadTask> buildThreadTask(TaskRecord record, Handler stateHandler) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public int getCreatedThreadNum() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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.loader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 线程任务管理器,用于处理多线程下载时任务的状态回调
|
||||
*/
|
||||
public class UploadThreadStateManager implements IThreadStateManager {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
/**
|
||||
* 任务状态回调
|
||||
*/
|
||||
private IEventListener mListener;
|
||||
private int mThreadNum; // 启动的线程总数
|
||||
private AtomicInteger mCancelNum = new AtomicInteger(0); // 已经取消的线程的数
|
||||
private AtomicInteger mStopNum = new AtomicInteger(0); // 已经停止的线程数
|
||||
private AtomicInteger mFailNum = new AtomicInteger(0); // 失败的线程数
|
||||
private AtomicInteger mCompleteNum = new AtomicInteger(0); // 完成的线程数
|
||||
private long mProgress; //当前总进度
|
||||
private TaskRecord mTaskRecord; // 任务记录
|
||||
private Looper mLooper;
|
||||
|
||||
/**
|
||||
* @param listener 任务事件
|
||||
*/
|
||||
public UploadThreadStateManager(IEventListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override public void setLooper(TaskRecord taskRecord, Looper looper) {
|
||||
mTaskRecord = taskRecord;
|
||||
mThreadNum = mTaskRecord.threadNum;
|
||||
mLooper = looper;
|
||||
}
|
||||
|
||||
private void checkLooper() {
|
||||
if (mTaskRecord == null) {
|
||||
throw new NullPointerException("任务记录为空");
|
||||
}
|
||||
if (mLooper == null) {
|
||||
throw new NullPointerException("Looper为空");
|
||||
}
|
||||
}
|
||||
|
||||
private Handler.Callback callback = new Handler.Callback() {
|
||||
@Override public boolean handleMessage(Message msg) {
|
||||
checkLooper();
|
||||
switch (msg.what) {
|
||||
case STATE_STOP:
|
||||
mStopNum.getAndIncrement();
|
||||
if (isStop()) {
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_CANCEL:
|
||||
mCancelNum.getAndIncrement();
|
||||
if (isCancel()) {
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_FAIL:
|
||||
mFailNum.getAndIncrement();
|
||||
if (isFail()) {
|
||||
Bundle b = msg.getData();
|
||||
mListener.onFail(b.getBoolean(DATA_RETRY, false),
|
||||
(AriaException) b.getSerializable(DATA_ERROR_INFO));
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_COMPLETE:
|
||||
mCompleteNum.getAndIncrement();
|
||||
if (isComplete()) {
|
||||
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
||||
//上传文件不需要合并文件
|
||||
mListener.onComplete();
|
||||
quitLooper();
|
||||
}
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
Bundle b = msg.getData();
|
||||
if (b != null) {
|
||||
long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0);
|
||||
mProgress += len;
|
||||
}
|
||||
|
||||
break;
|
||||
case STATE_UPDATE_PROGRESS:
|
||||
if (msg.obj == null) {
|
||||
mProgress = updateBlockProgress();
|
||||
} else if (msg.obj instanceof Long) {
|
||||
mProgress = (long) msg.obj;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Override public void updateCurrentProgress(long currentProgress) {
|
||||
mProgress = currentProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出looper循环
|
||||
*/
|
||||
private void quitLooper() {
|
||||
mLooper.quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务下载进度
|
||||
*
|
||||
* @return 当前任务下载进度
|
||||
*/
|
||||
@Override
|
||||
public long getCurrentProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
@Override public Handler.Callback getHandlerCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经停止
|
||||
*/
|
||||
public boolean isStop() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isStop; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mStopNum.get() == mThreadNum || mStopNum.get() + mCompleteNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经失败
|
||||
*/
|
||||
@Override
|
||||
public boolean isFail() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isFail; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCompleteNum.get() != mThreadNum
|
||||
&& (mFailNum.get() == mThreadNum || mFailNum.get() + mCompleteNum.get() == mThreadNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经完成
|
||||
*/
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
//ALog.d(TAG,
|
||||
// String.format("isComplete; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
||||
// mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCompleteNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有子线程是否都已经取消
|
||||
*/
|
||||
public boolean isCancel() {
|
||||
//ALog.d(TAG, String.format("isCancel; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
|
||||
// mStopNum,
|
||||
// mCancelNum, mFailNum, mCompleteNum));
|
||||
return mCancelNum.get() == mThreadNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新分块任务s的真实进度
|
||||
*/
|
||||
private long updateBlockProgress() {
|
||||
long size = 0;
|
||||
for (int i = 0, len = mTaskRecord.threadRecords.size(); i < len; i++) {
|
||||
File temp = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
if (temp.exists()) {
|
||||
size += temp.length();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并sftp的分块
|
||||
*/
|
||||
private boolean mergerSFtp() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(new File(mTaskRecord.filePath));
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength);
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
*
|
||||
* @return {@code true} 合并成功,{@code false}合并失败
|
||||
*/
|
||||
private boolean mergeFile() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
if (targetFile.exists() && targetFile.length() == mTaskRecord.fileLength){
|
||||
return true;
|
||||
}
|
||||
FileUtil.deleteFile(targetFile);
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(targetFile);
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
||||
if (isSuccess) {
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) {
|
||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s",
|
||||
targetFile.getName(), targetFile.length(), mTaskRecord.fileLength));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
ALog.e(TAG, "合并失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.arialyy.aria.core.manager;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* 线程任务管理器
|
||||
*/
|
||||
public class ThreadTaskManager {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
private static volatile ThreadTaskManager INSTANCE = null;
|
||||
private static final int CORE_POOL_NUM = 20;
|
||||
private static final ReentrantLock LOCK = new ReentrantLock();
|
||||
private ThreadPoolExecutor mExePool;
|
||||
private Map<String, Set<FutureContainer>> mThreadTasks = new ConcurrentHashMap<>();
|
||||
|
||||
public static synchronized ThreadTaskManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new ThreadTaskManager();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private ThreadTaskManager() {
|
||||
mExePool = new ThreadPoolExecutor(CORE_POOL_NUM, Integer.MAX_VALUE,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
mExePool.allowsCoreThreadTimeOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有线程任务
|
||||
*/
|
||||
public void removeAllThreadTask() {
|
||||
if (mThreadTasks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LOCK.tryLock(2, TimeUnit.SECONDS);
|
||||
for (Set<FutureContainer> threads : mThreadTasks.values()) {
|
||||
for (FutureContainer container : threads) {
|
||||
if (container.future.isDone() || container.future.isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
container.threadTask.destroy();
|
||||
}
|
||||
threads.clear();
|
||||
}
|
||||
mThreadTasks.clear();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动线程任务
|
||||
*
|
||||
* @param key 任务对应的key{@link AbsTaskWrapper#getKey()}
|
||||
* @param threadTask 线程任务{@link IThreadTask}
|
||||
*/
|
||||
public void startThread(String key, IThreadTask threadTask) {
|
||||
try {
|
||||
LOCK.tryLock(2, TimeUnit.SECONDS);
|
||||
if (mExePool.isShutdown()) {
|
||||
ALog.e(TAG, "线程池已经关闭");
|
||||
return;
|
||||
}
|
||||
key = getKey(key);
|
||||
Set<FutureContainer> temp = mThreadTasks.get(key);
|
||||
if (temp == null) {
|
||||
temp = new HashSet<>();
|
||||
mThreadTasks.put(key, temp);
|
||||
}
|
||||
FutureContainer container = new FutureContainer();
|
||||
container.threadTask = threadTask;
|
||||
container.future = mExePool.submit(threadTask);
|
||||
temp.add(container);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务是否在执行
|
||||
*
|
||||
* @param key 任务的key
|
||||
* @return {@code true} 任务正在运行
|
||||
*/
|
||||
public boolean taskIsRunning(String key) {
|
||||
return mThreadTasks.get(getKey(key)) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止任务的所有线程
|
||||
*
|
||||
* @param key 任务对应的key{@link AbsTaskWrapper#getKey()}
|
||||
*/
|
||||
public void removeTaskThread(String key) {
|
||||
try {
|
||||
LOCK.tryLock(2, TimeUnit.SECONDS);
|
||||
if (mExePool.isShutdown()) {
|
||||
ALog.e(TAG, "线程池已经关闭");
|
||||
return;
|
||||
}
|
||||
key = getKey(key);
|
||||
Set<FutureContainer> temp = mThreadTasks.get(key);
|
||||
if (temp != null && temp.size() > 0) {
|
||||
for (FutureContainer container : temp) {
|
||||
if (container.future.isDone() || container.future.isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
container.threadTask.destroy();
|
||||
}
|
||||
temp.clear();
|
||||
mThreadTasks.remove(key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据线程名删除任务的中的线程
|
||||
*
|
||||
* @param key 任务的key,如果是组合任务,则为组合任务的key
|
||||
* @param threadName 线程名
|
||||
* @return true 删除线程成功;false 删除线程失败
|
||||
*/
|
||||
public boolean removeSingleTaskThread(String key, String threadName) {
|
||||
try {
|
||||
LOCK.tryLock(2, TimeUnit.SECONDS);
|
||||
if (mExePool.isShutdown()) {
|
||||
ALog.e(TAG, "线程池已经关闭");
|
||||
return false;
|
||||
}
|
||||
if (TextUtils.isEmpty(threadName)) {
|
||||
ALog.e(TAG, "线程名为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
key = getKey(key);
|
||||
Set<FutureContainer> temp = mThreadTasks.get(key);
|
||||
if (temp != null && temp.size() > 0) {
|
||||
FutureContainer tempC = null;
|
||||
for (FutureContainer container : temp) {
|
||||
if (container.threadTask.getThreadName().equals(threadName)) {
|
||||
tempC = container;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tempC != null) {
|
||||
tempC.threadTask.destroy();
|
||||
temp.remove(tempC);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个线程任务
|
||||
*
|
||||
* @param key 任务的key
|
||||
* @param task 线程任务
|
||||
*/
|
||||
public boolean removeSingleTaskThread(String key, IThreadTask task) {
|
||||
try {
|
||||
LOCK.tryLock(2, TimeUnit.SECONDS);
|
||||
if (mExePool.isShutdown()) {
|
||||
ALog.e(TAG, "线程池已经关闭");
|
||||
return false;
|
||||
}
|
||||
if (task == null) {
|
||||
ALog.e(TAG, "线程任务为空");
|
||||
return false;
|
||||
}
|
||||
key = getKey(key);
|
||||
Set<FutureContainer> temp = mThreadTasks.get(key);
|
||||
if (temp != null && temp.size() > 0) {
|
||||
FutureContainer tempC = null;
|
||||
for (FutureContainer container : temp) {
|
||||
if (container.threadTask == task) {
|
||||
tempC = container;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tempC != null) {
|
||||
task.destroy();
|
||||
temp.remove(tempC);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试线程任务
|
||||
*
|
||||
* @param task 线程任务
|
||||
*/
|
||||
public void retryThread(IThreadTask task) {
|
||||
try {
|
||||
LOCK.tryLock(2, TimeUnit.SECONDS);
|
||||
if (mExePool.isShutdown()) {
|
||||
ALog.e(TAG, "线程池已经关闭");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (task == null || task.isDestroy()) {
|
||||
ALog.e(TAG, "线程为空或线程已经中断");
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ALog.e(TAG, "", e);
|
||||
return;
|
||||
}
|
||||
mExePool.submit(task);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* map中的key
|
||||
*
|
||||
* @param key 任务的key{@link AbsTaskWrapper#getKey()}
|
||||
* @return 转换后的map中的key
|
||||
*/
|
||||
private String getKey(String key) {
|
||||
return CommonUtil.getStrMd5(key);
|
||||
}
|
||||
|
||||
private class FutureContainer {
|
||||
Future future;
|
||||
IThreadTask threadTask;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Ftp上传拦截器处理,只针对新任务有效
|
||||
*
|
||||
* 如果使用者同时实现{@link Builder#resetFileName(String)}和{@link Builder#coverServerFile},
|
||||
* 将默认使用{@link Builder#coverServerFile}
|
||||
*/
|
||||
public class FtpInterceptHandler {
|
||||
|
||||
private boolean coverServerFile;
|
||||
|
||||
private String newFileName;
|
||||
|
||||
private FtpInterceptHandler() {
|
||||
}
|
||||
|
||||
public boolean isCoverServerFile() {
|
||||
return coverServerFile;
|
||||
}
|
||||
|
||||
public String getNewFileName() {
|
||||
return newFileName;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private boolean coverServerFile = false;
|
||||
|
||||
private String newFileName;
|
||||
|
||||
private boolean stopUpload = false;
|
||||
|
||||
/**
|
||||
* 如果ftp服务器端已经有同名文件,控制是否覆盖远端的同名文件;
|
||||
* 如果你不希望覆盖远端文件,可以使用{@link #resetFileName(String)}
|
||||
*
|
||||
* @return {@code true} 如果ftp服务器端已经有同名文件,覆盖服务器端的同名文件
|
||||
*/
|
||||
public Builder coverServerFile() {
|
||||
coverServerFile = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果ftp服务器端已经有同名文件,修改该文件上传到远端的文件名,该操作不会修改本地文件名
|
||||
* 如果你希望覆盖远端的同名文件,可以使用{@link #coverServerFile()}
|
||||
*/
|
||||
public Builder resetFileName(String newFileName) {
|
||||
this.newFileName = newFileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果你希望停止上传任务,可以调用该方法
|
||||
*/
|
||||
public Builder stopUpload() {
|
||||
stopUpload = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果使用者同时实现{@link Builder#resetFileName(String)}和{@link Builder#coverServerFile},
|
||||
* 将默认使用{@link Builder#coverServerFile}
|
||||
*/
|
||||
public FtpInterceptHandler build() {
|
||||
FtpInterceptHandler handler = new FtpInterceptHandler();
|
||||
if (coverServerFile) {
|
||||
handler.coverServerFile = true;
|
||||
} else if (!TextUtils.isEmpty(newFileName)) {
|
||||
handler.newFileName = newFileName;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import com.arialyy.aria.core.inf.IEventHandler;
|
||||
|
||||
/**
|
||||
* M3U8 bandWidth 码率url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的,
|
||||
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||
*/
|
||||
public interface IBandWidthUrlConverter extends IEventHandler {
|
||||
|
||||
/**
|
||||
* 转换码率地址为可用的http地址,对于某些服务器,返回的切片信息有可能是相对地址,也可能是处理过的,
|
||||
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||
*
|
||||
* @param m3u8Url m3u8url地址
|
||||
* @param bandWidthUrl 原始码率地址
|
||||
* @return 可正常访问的http地址
|
||||
*/
|
||||
String convert(String m3u8Url, String bandWidthUrl);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user