修复一个匿名内部类中的内存溢出的问题 https://github.com/AriaLyy/Aria/issues/705
m3u8密钥下载地址转换器增加ts列表的url地址https://github.com/AriaLyy/Aria/issues/718
This commit is contained in:
@@ -27,6 +27,7 @@ import android.widget.PopupWindow;
|
||||
import com.arialyy.aria.core.download.DownloadReceiver;
|
||||
import com.arialyy.aria.core.upload.UploadReceiver;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/12/1.
|
||||
@@ -134,8 +135,8 @@ import com.arialyy.aria.util.ALog;
|
||||
return (Service) obj;
|
||||
} else if (obj instanceof Activity) {
|
||||
return (Activity) obj;
|
||||
} else if (AriaManager.isFragment(obj.getClass())) {
|
||||
return AriaManager.getFragmentActivity(obj);
|
||||
} else if (CommonUtil.isFragment(obj.getClass())) {
|
||||
return CommonUtil.getFragmentActivity(obj);
|
||||
} else if (obj instanceof Dialog) {
|
||||
return ((Dialog) obj).getContext();
|
||||
} else if (obj instanceof PopupWindow) {
|
||||
|
||||
@@ -19,11 +19,10 @@ import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.widget.PopupWindow;
|
||||
import android.util.Log;
|
||||
import com.arialyy.aria.core.command.CommandManager;
|
||||
import com.arialyy.aria.core.common.QueueMod;
|
||||
import com.arialyy.aria.core.config.AppConfig;
|
||||
@@ -35,23 +34,19 @@ import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
import com.arialyy.aria.core.download.DownloadReceiver;
|
||||
import com.arialyy.aria.core.inf.AbsReceiver;
|
||||
import com.arialyy.aria.core.inf.IReceiver;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.inf.ReceiverType;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.core.upload.UploadReceiver;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.orm.DelegateWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.AriaCrashHandler;
|
||||
import com.arialyy.aria.util.DeleteDGRecord;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.DeleteURecord;
|
||||
import com.arialyy.aria.util.RecordUtil;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -63,35 +58,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
private static final String TAG = "AriaManager";
|
||||
private static final Object LOCK = new Object();
|
||||
|
||||
/**
|
||||
* android、androidx、support的fragment、dialogFragment类名
|
||||
*/
|
||||
private static List<String> mFragmentClassName = new ArrayList<>();
|
||||
private static List<String> mDialogFragmentClassName = new ArrayList<>();
|
||||
|
||||
@SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null;
|
||||
private Map<String, AbsReceiver> mReceivers = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* activity 和其Dialog、Fragment的映射表
|
||||
*/
|
||||
private Map<String, List<String>> mSubClass = new ConcurrentHashMap<>();
|
||||
private static Context APP;
|
||||
private DelegateWrapper mDbWrapper;
|
||||
private AriaConfig mConfig;
|
||||
|
||||
static {
|
||||
mFragmentClassName.add("androidx.fragment.app.Fragment");
|
||||
mFragmentClassName.add("androidx.fragment.app.DialogFragment");
|
||||
mFragmentClassName.add("android.app.Fragment");
|
||||
mFragmentClassName.add("android.app.DialogFragment");
|
||||
mFragmentClassName.add("android.support.v4.app.Fragment");
|
||||
mFragmentClassName.add("android.support.v4.app.DialogFragment");
|
||||
|
||||
mDialogFragmentClassName.add("androidx.fragment.app.DialogFragment");
|
||||
mDialogFragmentClassName.add("android.app.DialogFragment");
|
||||
mDialogFragmentClassName.add("android.support.v4.app.DialogFragment");
|
||||
}
|
||||
|
||||
private AriaManager(Context context) {
|
||||
APP = context.getApplicationContext();
|
||||
}
|
||||
@@ -253,6 +225,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* 处理下载操作
|
||||
*/
|
||||
DownloadReceiver download(Object obj) {
|
||||
|
||||
IReceiver receiver = mReceivers.get(getKey(ReceiverType.DOWNLOAD, obj));
|
||||
if (receiver == null) {
|
||||
receiver = putReceiver(ReceiverType.DOWNLOAD, obj);
|
||||
@@ -292,138 +265,19 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
}
|
||||
}
|
||||
|
||||
private IReceiver putReceiver(String type, Object obj) {
|
||||
private IReceiver putReceiver(ReceiverType type, Object obj) {
|
||||
final String key = getKey(type, obj);
|
||||
IReceiver receiver = mReceivers.get(key);
|
||||
boolean needRmReceiver = false;
|
||||
// 监控Dialog、fragment、popupWindow的生命周期
|
||||
final WidgetLiftManager widgetLiftManager = new WidgetLiftManager();
|
||||
Context subParenActivity = null;
|
||||
|
||||
if (obj instanceof Dialog) {
|
||||
needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj);
|
||||
subParenActivity = ((Dialog) obj).getOwnerActivity();
|
||||
} else if (obj instanceof PopupWindow) {
|
||||
needRmReceiver = widgetLiftManager.handlePopupWindowLift((PopupWindow) obj);
|
||||
subParenActivity = ((PopupWindow) obj).getContentView().getContext();
|
||||
} else if (isDialogFragment(obj.getClass())) {
|
||||
needRmReceiver = widgetLiftManager.handleDialogFragmentLift(getDialog(obj));
|
||||
subParenActivity = getFragmentActivity(obj);
|
||||
} else if (isFragment(obj.getClass())) {
|
||||
subParenActivity = getFragmentActivity(obj);
|
||||
}
|
||||
|
||||
if (subParenActivity instanceof Activity) {
|
||||
relateSubClass(type, obj, (Activity) subParenActivity);
|
||||
}
|
||||
|
||||
if (receiver == null) {
|
||||
AbsReceiver absReceiver =
|
||||
type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver() : new UploadReceiver();
|
||||
absReceiver.targetName = obj.getClass().getName();
|
||||
AbsReceiver.OBJ_MAP.put(absReceiver.getKey(), obj);
|
||||
absReceiver.needRmListener = needRmReceiver;
|
||||
type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver(obj) : new UploadReceiver(obj);
|
||||
mReceivers.put(key, absReceiver);
|
||||
receiver = absReceiver;
|
||||
}
|
||||
return receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取fragment的activity
|
||||
*
|
||||
* @return 获取失败,返回null
|
||||
*/
|
||||
static Activity getFragmentActivity(Object obj) {
|
||||
try {
|
||||
Method method = obj.getClass().getMethod("getActivity");
|
||||
return (Activity) method.invoke(obj);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断注解对象是否是fragment
|
||||
*
|
||||
* @return true 对象是fragment
|
||||
*/
|
||||
static boolean isFragment(Class subClazz) {
|
||||
Class parentClass = subClazz.getSuperclass();
|
||||
if (parentClass == null) {
|
||||
return false;
|
||||
} else {
|
||||
String parentName = parentClass.getName();
|
||||
if (mFragmentClassName.contains(parentName)) {
|
||||
return true;
|
||||
} else {
|
||||
return isFragment(parentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否是DialogFragment
|
||||
*
|
||||
* @return true 对象是DialogFragment
|
||||
*/
|
||||
private boolean isDialogFragment(Class subClazz) {
|
||||
Class parentClass = subClazz.getSuperclass();
|
||||
if (parentClass == null) {
|
||||
return false;
|
||||
} else {
|
||||
String parentName = parentClass.getName();
|
||||
if (mDialogFragmentClassName.contains(parentName)) {
|
||||
return true;
|
||||
} else {
|
||||
return isDialogFragment(parentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取DialogFragment的dialog
|
||||
*
|
||||
* @return 获取失败,返回null
|
||||
*/
|
||||
private Dialog getDialog(Object obj) {
|
||||
try {
|
||||
Method method = obj.getClass().getMethod("getDialog");
|
||||
return (Dialog) method.invoke(obj);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联Activity类和Fragment间的关系
|
||||
*
|
||||
* @param sub Fragment或dialog类
|
||||
* @param activity activity寄主类
|
||||
*/
|
||||
private void relateSubClass(String type, Object sub, Activity activity) {
|
||||
String key = getKey(type, activity);
|
||||
List<String> subClass = mSubClass.get(key);
|
||||
if (subClass == null) {
|
||||
subClass = new ArrayList<>();
|
||||
mSubClass.put(key, subClass);
|
||||
}
|
||||
subClass.add(getKey(type, sub));
|
||||
if (mReceivers.get(key) == null) { // 将activity填充进去
|
||||
mReceivers.put(key, new DownloadReceiver());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据功能类型和控件类型获取对应的key
|
||||
*
|
||||
@@ -432,8 +286,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* @return key的格式为:{@code String.format("%s_%s_%s", obj.getClass().getName(), type,
|
||||
* obj.hashCode());}
|
||||
*/
|
||||
private String getKey(String type, Object obj) {
|
||||
return String.format("%s_%s_%s", obj.getClass().getName(), type, obj.hashCode());
|
||||
private String getKey(ReceiverType type, Object obj) {
|
||||
return String.format("%s_%s_%s", CommonUtil.getTargetName(obj), type.name(), obj.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,40 +309,27 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
ALog.e(TAG, "target obj is null");
|
||||
return;
|
||||
}
|
||||
List<String> temp = new ArrayList<>();
|
||||
// 移除寄主的receiver
|
||||
for (Iterator<Map.Entry<String, AbsReceiver>> iter = mReceivers.entrySet().iterator();
|
||||
iter.hasNext(); ) {
|
||||
Map.Entry<String, AbsReceiver> entry = iter.next();
|
||||
String key = entry.getKey();
|
||||
if (key.equals(getKey(ReceiverType.DOWNLOAD, obj)) || key.equals(
|
||||
getKey(ReceiverType.UPLOAD, obj))) {
|
||||
AbsReceiver receiver = mReceivers.get(key);
|
||||
List<String> subNames = mSubClass.get(key);
|
||||
if (subNames != null && !subNames.isEmpty()) {
|
||||
temp.addAll(subNames);
|
||||
}
|
||||
if (receiver != null) {
|
||||
receiver.destroy();
|
||||
}
|
||||
AbsReceiver receiver = entry.getValue();
|
||||
if ((receiver.isLocalOrAnonymousClass || receiver.isFragment())
|
||||
&& key.startsWith(obj.getClass().getName())) {
|
||||
receiver.destroy();
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.equals(getKey(ReceiverType.DOWNLOAD, obj))
|
||||
|| key.equals(getKey(ReceiverType.UPLOAD, obj))
|
||||
) {
|
||||
receiver.destroy();
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 移除寄生的receiver
|
||||
if (!temp.isEmpty()) {
|
||||
for (Iterator<Map.Entry<String, AbsReceiver>> iter = mReceivers.entrySet().iterator();
|
||||
iter.hasNext(); ) {
|
||||
Map.Entry<String, AbsReceiver> entry = iter.next();
|
||||
if (temp.contains(entry.getKey())) {
|
||||
AbsReceiver receiver = mReceivers.get(entry.getKey());
|
||||
if (receiver != null) {
|
||||
receiver.destroy();
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "debug");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,20 +29,20 @@ import java.lang.reflect.Field;
|
||||
* Created by lyy on 2017/2/7.
|
||||
* 为组件添加生命周期
|
||||
*/
|
||||
final class WidgetLiftManager {
|
||||
public final class WidgetLiftManager {
|
||||
private final String TAG = "WidgetLiftManager";
|
||||
|
||||
/**
|
||||
* 处理DialogFragment事件
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB) boolean handleDialogFragmentLift(Dialog dialog) {
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public boolean handleDialogFragmentLift(Dialog dialog) {
|
||||
return handleDialogLift(dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理悬浮框取消或dismiss事件
|
||||
*/
|
||||
boolean handlePopupWindowLift(PopupWindow popupWindow) {
|
||||
public boolean handlePopupWindowLift(PopupWindow popupWindow) {
|
||||
try {
|
||||
Field dismissField = CommonUtil.getField(popupWindow.getClass(), "mOnDismissListener");
|
||||
PopupWindow.OnDismissListener listener =
|
||||
@@ -76,7 +76,7 @@ final class WidgetLiftManager {
|
||||
*
|
||||
* @return true 设置了dialog的销毁事件。false 没有设置dialog的销毁事件
|
||||
*/
|
||||
boolean handleDialogLift(Dialog dialog) {
|
||||
public boolean handleDialogLift(Dialog dialog) {
|
||||
if (dialog == null) {
|
||||
ALog.w(TAG,
|
||||
"dialog 为空,没有设置自动销毁事件,为了防止内存泄露,请在dismiss方法中调用Aria.download(this).unRegister();来注销事件\n"
|
||||
|
||||
@@ -55,7 +55,10 @@ import java.util.Set;
|
||||
* 下载功能接收器
|
||||
*/
|
||||
public class DownloadReceiver extends AbsReceiver {
|
||||
private final String TAG = "DownloadReceiver";
|
||||
|
||||
public DownloadReceiver(Object obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大下载速度,单位:kb
|
||||
@@ -164,7 +167,6 @@ public class DownloadReceiver extends AbsReceiver {
|
||||
* 将当前类注册到Aria
|
||||
*/
|
||||
public void register() {
|
||||
Object obj = OBJ_MAP.get(getKey());
|
||||
if (obj == null) {
|
||||
ALog.e(TAG, String.format("register【%s】观察者为空", getTargetName()));
|
||||
return;
|
||||
@@ -199,18 +201,17 @@ public class DownloadReceiver extends AbsReceiver {
|
||||
* @see <a href="https://aria.laoyuyu.me/aria_doc/start/any_java.html#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9">module类中销毁</a>
|
||||
*/
|
||||
@Override public void unRegister() {
|
||||
if (needRmListener) {
|
||||
if (isNeedRmListener()) {
|
||||
unRegisterListener();
|
||||
}
|
||||
AriaManager.getInstance().removeReceiver(OBJ_MAP.get(getKey()));
|
||||
AriaManager.getInstance().removeReceiver(obj);
|
||||
}
|
||||
|
||||
@Override public String getType() {
|
||||
@Override public ReceiverType getType() {
|
||||
return ReceiverType.DOWNLOAD;
|
||||
}
|
||||
|
||||
@Override protected void unRegisterListener() {
|
||||
Object obj = OBJ_MAP.get(getKey());
|
||||
if (obj == null) {
|
||||
ALog.e(TAG, String.format("unRegister【%s】观察者为空", getTargetName()));
|
||||
return;
|
||||
@@ -220,7 +221,9 @@ public class DownloadReceiver extends AbsReceiver {
|
||||
for (Integer integer : set) {
|
||||
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
|
||||
TaskSchedulers.getInstance().unRegister(obj);
|
||||
} else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
|
||||
continue;
|
||||
}
|
||||
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
|
||||
TaskSchedulers.getInstance().unRegister(obj);
|
||||
}
|
||||
}
|
||||
@@ -417,7 +420,7 @@ public class DownloadReceiver extends AbsReceiver {
|
||||
* @return 如果没有任务组列表,则返回null
|
||||
*/
|
||||
public List<DownloadGroupEntity> getGroupTaskList() {
|
||||
return getGroupTaskList(1,10);
|
||||
return getGroupTaskList(1, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,31 +16,119 @@
|
||||
|
||||
package com.arialyy.aria.core.inf;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.util.Log;
|
||||
import android.widget.PopupWindow;
|
||||
import com.arialyy.aria.core.WidgetLiftManager;
|
||||
import com.arialyy.aria.core.queue.DGroupTaskQueue;
|
||||
import com.arialyy.aria.core.queue.DTaskQueue;
|
||||
import com.arialyy.aria.core.queue.UTaskQueue;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/27.
|
||||
* 接收器
|
||||
*/
|
||||
public abstract class AbsReceiver implements IReceiver {
|
||||
protected String TAG = getClass().getSimpleName();
|
||||
|
||||
/**
|
||||
* 观察者对象map
|
||||
* key 由 {@link #getKey(IReceiver)}指定
|
||||
* 观察者对象
|
||||
*/
|
||||
public static final Map<String, Object> OBJ_MAP = new ConcurrentHashMap<>();
|
||||
protected Object obj;
|
||||
|
||||
/**
|
||||
* 观察者对象类的完整名称
|
||||
*/
|
||||
public String targetName;
|
||||
private String targetName;
|
||||
|
||||
/**
|
||||
* 当dialog、dialogFragment、popupwindow已经被用户使用了Dismiss事件或Cancel事件,需要手动移除receiver
|
||||
*/
|
||||
public boolean needRmListener = false;
|
||||
private boolean needRmReceiver = false;
|
||||
|
||||
private boolean isFragment = false;
|
||||
|
||||
public boolean isLocalOrAnonymousClass = false;
|
||||
|
||||
public AbsReceiver(Object obj) {
|
||||
this.obj = obj;
|
||||
initParams();
|
||||
}
|
||||
|
||||
private void initParams() {
|
||||
try {
|
||||
targetName = CommonUtil.getTargetName(obj);
|
||||
Class clazz = obj.getClass();
|
||||
if (CommonUtil.isLocalOrAnonymousClass(clazz)) {
|
||||
isLocalOrAnonymousClass = true;
|
||||
String parentName = CommonUtil.getTargetName(obj);
|
||||
Class parentClazz = Class.forName(parentName);
|
||||
handleFragmentOrDialogParam(parentClazz, true);
|
||||
return;
|
||||
}
|
||||
handleFragmentOrDialogParam(clazz, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFragmentOrDialogParam(Class clazz, boolean isLocalOrAnonymousClass) {
|
||||
final WidgetLiftManager widgetLiftManager = new WidgetLiftManager();
|
||||
if (obj instanceof Dialog) {
|
||||
needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj);
|
||||
return;
|
||||
}
|
||||
if (obj instanceof PopupWindow) {
|
||||
needRmReceiver = widgetLiftManager.handlePopupWindowLift((PopupWindow) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CommonUtil.isFragment(clazz)){
|
||||
isFragment = true;
|
||||
}
|
||||
|
||||
if (CommonUtil.isDialogFragment(clazz)) {
|
||||
isFragment = true;
|
||||
if (isLocalOrAnonymousClass) {
|
||||
Log.e(TAG, String.format(
|
||||
"%s 是匿名内部类,无法获取到dialog对象,为了防止内存泄漏,请在dismiss方法中调用Aria.download(this).unRegister();来注销事件",
|
||||
obj.getClass().getName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
needRmReceiver = widgetLiftManager.handleDialogFragmentLift(getDialog(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取DialogFragment的dialog
|
||||
*
|
||||
* @return 获取失败,返回null
|
||||
*/
|
||||
private Dialog getDialog(Object obj) {
|
||||
try {
|
||||
Method method = obj.getClass().getMethod("getDialog");
|
||||
return (Dialog) method.invoke(obj);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isNeedRmListener() {
|
||||
return needRmReceiver;
|
||||
}
|
||||
|
||||
@Override public boolean isFragment() {
|
||||
return isFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建观察者对象map的key,生成规则:
|
||||
@@ -69,14 +157,7 @@ public abstract class AbsReceiver implements IReceiver {
|
||||
* 移除观察者对象
|
||||
*/
|
||||
private void removeObj() {
|
||||
for (Iterator<Map.Entry<String, Object>> iter = OBJ_MAP.entrySet().iterator();
|
||||
iter.hasNext(); ) {
|
||||
Map.Entry<String, Object> entry = iter.next();
|
||||
String key = entry.getKey();
|
||||
if (key.equals(getKey())) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
obj = null;
|
||||
}
|
||||
|
||||
@Override public void destroy() {
|
||||
|
||||
@@ -49,5 +49,11 @@ public interface IReceiver {
|
||||
*
|
||||
* @return {@link ReceiverType}
|
||||
*/
|
||||
String getType();
|
||||
ReceiverType getType();
|
||||
|
||||
/**
|
||||
* 判断是否是fragment,如果是fragment,在activity销毁时,需要将其从receiver中移除
|
||||
*/
|
||||
boolean isFragment();
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,15 @@ package com.arialyy.aria.core.inf;
|
||||
/**
|
||||
* {@link AbsReceiver}类型
|
||||
*/
|
||||
public interface ReceiverType {
|
||||
String DOWNLOAD = "download";
|
||||
String UPLOAD = "upload";
|
||||
public enum ReceiverType {
|
||||
DOWNLOAD(1, "download"),
|
||||
UPLOAD(2, "upload");
|
||||
|
||||
String name;
|
||||
int type;
|
||||
|
||||
ReceiverType(int type, String name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,9 @@ import java.util.Set;
|
||||
* 上传功能接收器
|
||||
*/
|
||||
public class UploadReceiver extends AbsReceiver {
|
||||
private static final String TAG = "UploadReceiver";
|
||||
public UploadReceiver(Object obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大上传速度,单位:kb
|
||||
@@ -264,7 +266,6 @@ public class UploadReceiver extends AbsReceiver {
|
||||
* 将当前类注册到Aria
|
||||
*/
|
||||
public void register() {
|
||||
Object obj = OBJ_MAP.get(getKey());
|
||||
if (obj == null) {
|
||||
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
|
||||
return;
|
||||
@@ -286,18 +287,17 @@ public class UploadReceiver extends AbsReceiver {
|
||||
* 如果是Dialog或popupwindow,需要你在撤销界面时调用该方法
|
||||
*/
|
||||
@Override public void unRegister() {
|
||||
if (needRmListener) {
|
||||
if (isNeedRmListener()) {
|
||||
unRegisterListener();
|
||||
}
|
||||
AriaManager.getInstance().removeReceiver(OBJ_MAP.get(getKey()));
|
||||
AriaManager.getInstance().removeReceiver(obj);
|
||||
}
|
||||
|
||||
@Override public String getType() {
|
||||
@Override public ReceiverType getType() {
|
||||
return ReceiverType.UPLOAD;
|
||||
}
|
||||
|
||||
@Override protected void unRegisterListener() {
|
||||
Object obj = OBJ_MAP.get(getKey());
|
||||
if (obj == null) {
|
||||
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
## 开发日志
|
||||
+ v_3.8.11
|
||||
- 修复一个正则表达式导致的文件名保存号异常问题 https://github.com/AriaLyy/Aria/issues/715
|
||||
- 修复一个匿名内部类中的内存溢出的问题 https://github.com/AriaLyy/Aria/issues/705
|
||||
- m3u8密钥下载地址转换器增加ts列表的url地址 https://github.com/AriaLyy/Aria/issues/718
|
||||
+ v_3.8.10 (2020/6/26)
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/703
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/702
|
||||
|
||||
@@ -101,7 +101,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
ConnectionHelp.setConnectParam(mHttpOption, conn);
|
||||
conn.setConnectTimeout(mConnectTimeOut);
|
||||
conn.connect();
|
||||
handleConnect(conn);
|
||||
handleConnect(mEntity.getUrl(), conn);
|
||||
} catch (IOException e) {
|
||||
failDownload(e.getMessage(), false);
|
||||
} finally {
|
||||
@@ -115,7 +115,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
private void handleConnect(HttpURLConnection conn) throws IOException {
|
||||
private void handleConnect(String tsListUrl, HttpURLConnection conn) throws IOException {
|
||||
int code = conn.getResponseCode();
|
||||
if (code == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
@@ -151,7 +151,9 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
// 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志
|
||||
addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
|
||||
break;
|
||||
} else if (line.startsWith("#EXTINF")) {
|
||||
}
|
||||
|
||||
if (line.startsWith("#EXTINF")) {
|
||||
String url = reader.readLine();
|
||||
if (isLive) {
|
||||
if (onGetPeerCallback != null) {
|
||||
@@ -163,7 +165,10 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
ALog.d(TAG, url);
|
||||
addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
|
||||
addIndexInfo(isGenerateIndexFile && !isLive, fos, url);
|
||||
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith("#EXT-X-STREAM-INF")) {
|
||||
addIndexInfo(isGenerateIndexFile, fos, line);
|
||||
int setBand = mM3U8Option.getBandWidth();
|
||||
int bandWidth = getBandWidth(line);
|
||||
@@ -179,12 +184,14 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
failDownload(String.format("【%s】码率不存在", setBand), false);
|
||||
}
|
||||
return;
|
||||
} else if (line.startsWith("#EXT-X-KEY")) {
|
||||
addIndexInfo(isGenerateIndexFile, fos, line);
|
||||
getKeyInfo(line);
|
||||
} else {
|
||||
addIndexInfo(isGenerateIndexFile, fos, line);
|
||||
}
|
||||
|
||||
if (line.startsWith("#EXT-X-KEY")) {
|
||||
addIndexInfo(isGenerateIndexFile, fos, line);
|
||||
getKeyInfo(tsListUrl, line);
|
||||
continue;
|
||||
}
|
||||
addIndexInfo(isGenerateIndexFile, fos, line);
|
||||
}
|
||||
|
||||
if (!isLive && extInf.isEmpty()) {
|
||||
@@ -248,7 +255,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
/**
|
||||
* 获取加密的密钥信息
|
||||
*/
|
||||
private void getKeyInfo(String line) {
|
||||
private void getKeyInfo(String tsListUrl, String line) {
|
||||
String temp = line.substring(line.indexOf(":") + 1);
|
||||
String[] params = temp.split(",");
|
||||
M3U8Entity m3U8Entity = mEntity.getM3U8Entity();
|
||||
@@ -273,7 +280,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
m3U8Entity.keyFormatVersion = param.split("=")[1];
|
||||
}
|
||||
}
|
||||
downloadKey(m3U8Entity);
|
||||
downloadKey(tsListUrl, m3U8Entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +327,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
conn.setRequestProperty("Cookie", cookies);
|
||||
conn.setConnectTimeout(mConnectTimeOut);
|
||||
conn.connect();
|
||||
handleConnect(conn);
|
||||
handleConnect(newUrl, conn);
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
@@ -349,7 +356,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
conn.setRequestProperty("Cookie", cookies);
|
||||
conn.setConnectTimeout(mConnectTimeOut);
|
||||
conn.connect();
|
||||
handleConnect(conn);
|
||||
handleConnect(bandWidthM3u8Url, conn);
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
@@ -360,7 +367,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
/**
|
||||
* 密钥不存在,下载密钥
|
||||
*/
|
||||
private void downloadKey(M3U8Entity info) {
|
||||
private void downloadKey(String tsListUr, M3U8Entity info) {
|
||||
HttpURLConnection conn = null;
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
@@ -375,7 +382,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter();
|
||||
String keyUrl = info.keyUrl;
|
||||
if (keyUrlConverter != null) {
|
||||
keyUrl = keyUrlConverter.convert(mEntity.getUrl(), keyUrl);
|
||||
keyUrl = keyUrlConverter.convert(mEntity.getUrl(), tsListUr, keyUrl);
|
||||
}
|
||||
if (TextUtils.isEmpty(keyUrl)) {
|
||||
ALog.e(TAG, "m3u8密钥key url 为空");
|
||||
|
||||
@@ -25,9 +25,10 @@ public interface IKeyUrlConverter extends IEventHandler {
|
||||
/**
|
||||
* 将被加密的密钥下载地址转换为可使用的http下载地址
|
||||
*
|
||||
* @param m3u8Url m3u8文件的下载地址
|
||||
* @param m3u8Url 主m3u8的url地址
|
||||
* @param tsListUrl m3u8切片列表url地址
|
||||
* @param keyUrl 加密的url地址
|
||||
* @return 可正常访问的http地址
|
||||
*/
|
||||
String convert(String m3u8Url, String keyUrl);
|
||||
String convert(String m3u8Url, String tsListUrl, String keyUrl);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class AriaServiceLoader<S> {
|
||||
private Class<S> service;
|
||||
private ClassLoader loader;
|
||||
private Enumeration<URL> configs = null;
|
||||
private Iterator<String> pending = null;
|
||||
private List<String> pending = null;
|
||||
|
||||
private LazyLoader(Class<S> service, ClassLoader loader) {
|
||||
this.service = service;
|
||||
@@ -103,7 +103,7 @@ public class AriaServiceLoader<S> {
|
||||
// If an I/O error occurs while reading from the given URL, or
|
||||
// if a configuration-file format error is detected
|
||||
//
|
||||
private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
|
||||
private List<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
|
||||
InputStream in = null;
|
||||
BufferedReader r = null;
|
||||
ArrayList<String> names = new ArrayList<>();
|
||||
@@ -122,7 +122,7 @@ public class AriaServiceLoader<S> {
|
||||
fail(service, "Error closing configuration file", y);
|
||||
}
|
||||
}
|
||||
return names.iterator();
|
||||
return names;
|
||||
}
|
||||
|
||||
private void fail(Class<?> service, String msg, Throwable cause)
|
||||
@@ -177,8 +177,8 @@ public class AriaServiceLoader<S> {
|
||||
}
|
||||
|
||||
private S loadService(String serviceName) {
|
||||
while (pending.hasNext()) {
|
||||
if (pending.next().equals(serviceName)) {
|
||||
for (String s : pending) {
|
||||
if (s.equals(serviceName)) {
|
||||
Class<?> c = null;
|
||||
try {
|
||||
c = Class.forName(serviceName, false, loader);
|
||||
@@ -224,7 +224,7 @@ public class AriaServiceLoader<S> {
|
||||
fail(service, "Error locating configuration files", x);
|
||||
}
|
||||
}
|
||||
while ((pending == null) || !pending.hasNext()) {
|
||||
while ((pending == null) || pending.isEmpty()) {
|
||||
if (!configs.hasMoreElements()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.arialyy.aria.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -23,6 +24,7 @@ import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import dalvik.system.DexFile;
|
||||
@@ -34,6 +36,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
@@ -60,6 +64,101 @@ public class CommonUtil {
|
||||
private static final String TAG = "CommonUtil";
|
||||
public static final String SERVER_CHARSET = "ISO-8859-1";
|
||||
private static long lastClickTime;
|
||||
/**
|
||||
* android、androidx、support的fragment、dialogFragment类名
|
||||
*/
|
||||
private static List<String> mFragmentClassName = new ArrayList<>();
|
||||
private static List<String> mDialogFragmentClassName = new ArrayList<>();
|
||||
|
||||
static {
|
||||
mFragmentClassName.add("androidx.fragment.app.Fragment");
|
||||
mFragmentClassName.add("androidx.fragment.app.DialogFragment");
|
||||
mFragmentClassName.add("android.app.Fragment");
|
||||
mFragmentClassName.add("android.app.DialogFragment");
|
||||
mFragmentClassName.add("android.support.v4.app.Fragment");
|
||||
mFragmentClassName.add("android.support.v4.app.DialogFragment");
|
||||
|
||||
mDialogFragmentClassName.add("androidx.fragment.app.DialogFragment");
|
||||
mDialogFragmentClassName.add("android.app.DialogFragment");
|
||||
mDialogFragmentClassName.add("android.support.v4.app.DialogFragment");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取fragment的activityz
|
||||
*
|
||||
* @return 获取失败,返回null
|
||||
*/
|
||||
public static Activity getFragmentActivity(Object obj) {
|
||||
try {
|
||||
Method method = obj.getClass().getMethod("getActivity");
|
||||
return (Activity) method.invoke(obj);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断注解对象是否是fragment
|
||||
*
|
||||
* @return true 对象是fragment
|
||||
*/
|
||||
public static boolean isFragment(Class subClazz) {
|
||||
Class parentClass = subClazz.getSuperclass();
|
||||
if (parentClass == null) {
|
||||
return false;
|
||||
} else {
|
||||
String parentName = parentClass.getName();
|
||||
if (mFragmentClassName.contains(parentName)) {
|
||||
return true;
|
||||
} else {
|
||||
return isFragment(parentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否是DialogFragment
|
||||
*
|
||||
* @return true 对象是DialogFragment
|
||||
*/
|
||||
public static boolean isDialogFragment(Class subClazz) {
|
||||
Class parentClass = subClazz.getSuperclass();
|
||||
if (parentClass == null) {
|
||||
return false;
|
||||
} else {
|
||||
String parentName = parentClass.getName();
|
||||
if (mDialogFragmentClassName.contains(parentName)) {
|
||||
return true;
|
||||
} else {
|
||||
return isDialogFragment(parentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLocalOrAnonymousClass(Class clazz) {
|
||||
// JVM Spec 4.8.6: A class must have an EnclosingMethod
|
||||
// attribute if and only if it is a local class or an
|
||||
// anonymous class.
|
||||
return clazz.isLocalClass() || clazz.isAnonymousClass();
|
||||
}
|
||||
|
||||
public static String getTargetName(Object obj) {
|
||||
String targetName;
|
||||
if (isLocalOrAnonymousClass(obj.getClass())) {
|
||||
Log.w(TAG, String.format("%s 是匿名内部类或局部类,将使用其主类的对象", obj.getClass().getName()));
|
||||
String clsName = obj.getClass().getName();
|
||||
int $Index = clsName.lastIndexOf("$");
|
||||
targetName = clsName.substring(0, $Index);
|
||||
} else {
|
||||
targetName = obj.getClass().getName();
|
||||
}
|
||||
return targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取线程名称,命名规则:md5(任务地址 + 线程id)
|
||||
|
||||
@@ -106,7 +106,6 @@ public class ComponentUtil {
|
||||
* @return 返回任务工具
|
||||
*/
|
||||
public synchronized IUtil buildUtil(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
utilLoader.reload();
|
||||
int requestType = wrapper.getRequestType();
|
||||
String className = null;
|
||||
switch (requestType) {
|
||||
|
||||
@@ -4,11 +4,23 @@ import androidx.test.runner.AndroidJUnit4
|
||||
import com.arialyy.aria.util.CommonUtil
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import kotlin.math.pow
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ApiTest {
|
||||
|
||||
@Test
|
||||
fun testAddChar(){
|
||||
var str = "\\\\+道+歉+信\u0026感 谢 信"
|
||||
str = str.replace("\\+".toRegex(), "%2B")
|
||||
println("========")
|
||||
println(str)
|
||||
println(URLEncoder.encode(str))
|
||||
println(URLDecoder.decode(str))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSpeed() {
|
||||
val speed = 1024.0.pow(4.0) * 2
|
||||
|
||||
@@ -311,8 +311,9 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
|
||||
//.setMergeHandler(new TsMergeHandler());
|
||||
option.setUseDefConvert(false);
|
||||
option.setKeyUrlConverter(new KeyUrlConverter());
|
||||
option.setVodTsUrlConvert(new VodTsUrlConverter());
|
||||
option.setBandWidthUrlConverter(new BandWidthUrlConverter());
|
||||
option.setUseDefConvert(true);
|
||||
//option.setUseDefConvert(true);
|
||||
return option;
|
||||
}
|
||||
|
||||
@@ -329,18 +330,19 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
|
||||
@Override public List<String> convert(String m3u8Url, List<String> tsUrls) {
|
||||
Uri uri = Uri.parse(m3u8Url);
|
||||
//String parentUrl = "http://devimages.apple.com/iphone/samples/bipbop/gear1/";
|
||||
String parentUrl = "http://youku.cdn7-okzy.com/20200123/16815_fbe419ed/1000k/hls/";
|
||||
//String parentUrl = "http://youku.cdn7-okzy.com/20200123/16815_fbe419ed/1000k/hls/";
|
||||
//String parentUrl = "http://" + uri.getHost() + "/gear1/";
|
||||
//int index = m3u8Url.lastIndexOf("/");
|
||||
//String parentUrl = m3u8Url.substring(0, index + 1);
|
||||
//String parentUrl = "https://v1.szjal.cn/20190819/Ql6UD1od/";
|
||||
//String parentUrl = "http://" + uri.getHost() + "/";
|
||||
List<String> newUrls = new ArrayList<>();
|
||||
for (String url : tsUrls) {
|
||||
newUrls.add(parentUrl + url);
|
||||
}
|
||||
//List<String> newUrls = new ArrayList<>();
|
||||
//for (String url : tsUrls) {
|
||||
// newUrls.add(parentUrl + url);
|
||||
//}
|
||||
|
||||
return newUrls;
|
||||
//return newUrls;
|
||||
return tsUrls;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ public class M3U8VodModule extends BaseViewModule {
|
||||
// m3u8测试集合:http://www.voidcn.com/article/p-snaliarm-ct.html
|
||||
//private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8";
|
||||
// 多码率地址:
|
||||
private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8";
|
||||
//private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8";
|
||||
private final String defUrl = "http://mgjx.awu1k.cn:8000/parse/player.m3u8?target=eb2d3ca5198516269cf356463be86573";
|
||||
//private final String defUrl = "http://youku.cdn7-okzy.com/20200123/16815_fbe419ed/index.m3u8";
|
||||
|
||||
//private final String defUrl = "https://cn7.kankia.com/hls/20200108/e1eaec074274c64fe46a3bdb5d2ba487/1578488360/index.m3u8";
|
||||
|
||||
Reference in New Issue
Block a user