修复一个匿名内部类中的内存溢出的问题 https://github.com/AriaLyy/Aria/issues/705

m3u8密钥下载地址转换器增加ts列表的url地址https://github.com/AriaLyy/Aria/issues/718
pull/789/head
laoyuyu 4 years ago
parent bebc9b926d
commit 9d8bf579b6
  1. 5
      Aria/src/main/java/com/arialyy/aria/core/Aria.java
  2. 191
      Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
  3. 8
      Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java
  4. 17
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java
  5. 113
      Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java
  6. 8
      Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java
  7. 14
      Aria/src/main/java/com/arialyy/aria/core/inf/ReceiverType.java
  8. 12
      Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java
  9. 4
      DEV_LOG.md
  10. 35
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoTask.java
  11. 5
      PublicComponent/src/main/java/com/arialyy/aria/core/processor/IKeyUrlConverter.java
  12. 12
      PublicComponent/src/main/java/com/arialyy/aria/util/AriaServiceLoader.java
  13. 99
      PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java
  14. 1
      PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java
  15. 12
      app/src/androidTest/java/com/example/arial/downloaddemo/ApiTest.kt
  16. 16
      app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java
  17. 3
      app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java

@ -27,6 +27,7 @@ import android.widget.PopupWindow;
import com.arialyy.aria.core.download.DownloadReceiver; import com.arialyy.aria.core.download.DownloadReceiver;
import com.arialyy.aria.core.upload.UploadReceiver; import com.arialyy.aria.core.upload.UploadReceiver;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
/** /**
* Created by lyy on 2016/12/1. * Created by lyy on 2016/12/1.
@ -134,8 +135,8 @@ import com.arialyy.aria.util.ALog;
return (Service) obj; return (Service) obj;
} else if (obj instanceof Activity) { } else if (obj instanceof Activity) {
return (Activity) obj; return (Activity) obj;
} else if (AriaManager.isFragment(obj.getClass())) { } else if (CommonUtil.isFragment(obj.getClass())) {
return AriaManager.getFragmentActivity(obj); return CommonUtil.getFragmentActivity(obj);
} else if (obj instanceof Dialog) { } else if (obj instanceof Dialog) {
return ((Dialog) obj).getContext(); return ((Dialog) obj).getContext();
} else if (obj instanceof PopupWindow) { } else if (obj instanceof PopupWindow) {

@ -19,11 +19,10 @@ import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.widget.PopupWindow; import android.util.Log;
import com.arialyy.aria.core.command.CommandManager; import com.arialyy.aria.core.command.CommandManager;
import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.core.common.QueueMod;
import com.arialyy.aria.core.config.AppConfig; 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.download.DownloadReceiver;
import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.AbsReceiver;
import com.arialyy.aria.core.inf.IReceiver; 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.inf.ReceiverType;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.upload.UploadReceiver; import com.arialyy.aria.core.upload.UploadReceiver;
import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.orm.DelegateWrapper; import com.arialyy.aria.orm.DelegateWrapper;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.AriaCrashHandler; 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.DeleteURecord;
import com.arialyy.aria.util.RecordUtil; import com.arialyy.aria.util.RecordUtil;
import java.io.File; 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.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -63,35 +58,12 @@ import java.util.concurrent.ConcurrentHashMap;
private static final String TAG = "AriaManager"; private static final String TAG = "AriaManager";
private static final Object LOCK = new Object(); private static final Object LOCK = new Object();
/**
* androidandroidxsupport的fragmentdialogFragment类名
*/
private static List<String> mFragmentClassName = new ArrayList<>();
private static List<String> mDialogFragmentClassName = new ArrayList<>();
@SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null; @SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null;
private Map<String, AbsReceiver> mReceivers = new ConcurrentHashMap<>(); private Map<String, AbsReceiver> mReceivers = new ConcurrentHashMap<>();
/**
* activity 和其DialogFragment的映射表
*/
private Map<String, List<String>> mSubClass = new ConcurrentHashMap<>();
private static Context APP; private static Context APP;
private DelegateWrapper mDbWrapper; private DelegateWrapper mDbWrapper;
private AriaConfig mConfig; 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) { private AriaManager(Context context) {
APP = context.getApplicationContext(); APP = context.getApplicationContext();
} }
@ -253,6 +225,7 @@ import java.util.concurrent.ConcurrentHashMap;
* 处理下载操作 * 处理下载操作
*/ */
DownloadReceiver download(Object obj) { DownloadReceiver download(Object obj) {
IReceiver receiver = mReceivers.get(getKey(ReceiverType.DOWNLOAD, obj)); IReceiver receiver = mReceivers.get(getKey(ReceiverType.DOWNLOAD, obj));
if (receiver == null) { if (receiver == null) {
receiver = putReceiver(ReceiverType.DOWNLOAD, obj); 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); final String key = getKey(type, obj);
IReceiver receiver = mReceivers.get(key); 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) { if (receiver == null) {
AbsReceiver absReceiver = AbsReceiver absReceiver =
type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver() : new UploadReceiver(); type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver(obj) : new UploadReceiver(obj);
absReceiver.targetName = obj.getClass().getName();
AbsReceiver.OBJ_MAP.put(absReceiver.getKey(), obj);
absReceiver.needRmListener = needRmReceiver;
mReceivers.put(key, absReceiver); mReceivers.put(key, absReceiver);
receiver = absReceiver; receiver = absReceiver;
} }
return receiver; 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 * 根据功能类型和控件类型获取对应的key
* *
@ -432,8 +286,8 @@ import java.util.concurrent.ConcurrentHashMap;
* @return key的格式为{@code String.format("%s_%s_%s", obj.getClass().getName(), type, * @return key的格式为{@code String.format("%s_%s_%s", obj.getClass().getName(), type,
* obj.hashCode());} * obj.hashCode());}
*/ */
private String getKey(String type, Object obj) { private String getKey(ReceiverType type, Object obj) {
return String.format("%s_%s_%s", obj.getClass().getName(), type, obj.hashCode()); 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"); ALog.e(TAG, "target obj is null");
return; return;
} }
List<String> temp = new ArrayList<>();
// 移除寄主的receiver // 移除寄主的receiver
for (Iterator<Map.Entry<String, AbsReceiver>> iter = mReceivers.entrySet().iterator(); for (Iterator<Map.Entry<String, AbsReceiver>> iter = mReceivers.entrySet().iterator();
iter.hasNext(); ) { iter.hasNext(); ) {
Map.Entry<String, AbsReceiver> entry = iter.next(); Map.Entry<String, AbsReceiver> entry = iter.next();
String key = entry.getKey(); String key = entry.getKey();
if (key.equals(getKey(ReceiverType.DOWNLOAD, obj)) || key.equals( AbsReceiver receiver = entry.getValue();
getKey(ReceiverType.UPLOAD, obj))) { if ((receiver.isLocalOrAnonymousClass || receiver.isFragment())
AbsReceiver receiver = mReceivers.get(key); && key.startsWith(obj.getClass().getName())) {
List<String> subNames = mSubClass.get(key);
if (subNames != null && !subNames.isEmpty()) {
temp.addAll(subNames);
}
if (receiver != null) {
receiver.destroy(); receiver.destroy();
}
iter.remove(); iter.remove();
} continue;
} }
// 移除寄生的receiver if (key.equals(getKey(ReceiverType.DOWNLOAD, obj))
if (!temp.isEmpty()) { || key.equals(getKey(ReceiverType.UPLOAD, obj))
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(); receiver.destroy();
}
iter.remove(); iter.remove();
} }
} }
} Log.d(TAG, "debug");
} }
/** /**

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

@ -55,7 +55,10 @@ import java.util.Set;
* 下载功能接收器 * 下载功能接收器
*/ */
public class DownloadReceiver extends AbsReceiver { public class DownloadReceiver extends AbsReceiver {
private final String TAG = "DownloadReceiver";
public DownloadReceiver(Object obj) {
super(obj);
}
/** /**
* 设置最大下载速度单位kb * 设置最大下载速度单位kb
@ -164,7 +167,6 @@ public class DownloadReceiver extends AbsReceiver {
* 将当前类注册到Aria * 将当前类注册到Aria
*/ */
public void register() { public void register() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) { if (obj == null) {
ALog.e(TAG, String.format("register【%s】观察者为空", getTargetName())); ALog.e(TAG, String.format("register【%s】观察者为空", getTargetName()));
return; 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> * @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() { @Override public void unRegister() {
if (needRmListener) { if (isNeedRmListener()) {
unRegisterListener(); unRegisterListener();
} }
AriaManager.getInstance().removeReceiver(OBJ_MAP.get(getKey())); AriaManager.getInstance().removeReceiver(obj);
} }
@Override public String getType() { @Override public ReceiverType getType() {
return ReceiverType.DOWNLOAD; return ReceiverType.DOWNLOAD;
} }
@Override protected void unRegisterListener() { @Override protected void unRegisterListener() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) { if (obj == null) {
ALog.e(TAG, String.format("unRegister【%s】观察者为空", getTargetName())); ALog.e(TAG, String.format("unRegister【%s】观察者为空", getTargetName()));
return; return;
@ -220,7 +221,9 @@ public class DownloadReceiver extends AbsReceiver {
for (Integer integer : set) { for (Integer integer : set) {
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) { if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) {
TaskSchedulers.getInstance().unRegister(obj); TaskSchedulers.getInstance().unRegister(obj);
} else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { continue;
}
if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) {
TaskSchedulers.getInstance().unRegister(obj); TaskSchedulers.getInstance().unRegister(obj);
} }
} }

@ -16,31 +16,119 @@
package com.arialyy.aria.core.inf; 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.DGroupTaskQueue;
import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.core.queue.DTaskQueue;
import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.queue.UTaskQueue;
import java.util.Iterator; import com.arialyy.aria.util.CommonUtil;
import java.util.Map; import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentHashMap; import java.lang.reflect.Method;
/** /**
* Created by AriaL on 2017/6/27. * Created by AriaL on 2017/6/27.
* 接收器 * 接收器
*/ */
public abstract class AbsReceiver implements IReceiver { 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;
/** /**
* 当dialogdialogFragmentpopupwindow已经被用户使用了Dismiss事件或Cancel事件需要手动移除receiver * 当dialogdialogFragmentpopupwindow已经被用户使用了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生成规则 * 创建观察者对象map的key生成规则
@ -69,14 +157,7 @@ public abstract class AbsReceiver implements IReceiver {
* 移除观察者对象 * 移除观察者对象
*/ */
private void removeObj() { private void removeObj() {
for (Iterator<Map.Entry<String, Object>> iter = OBJ_MAP.entrySet().iterator(); obj = null;
iter.hasNext(); ) {
Map.Entry<String, Object> entry = iter.next();
String key = entry.getKey();
if (key.equals(getKey())) {
iter.remove();
}
}
} }
@Override public void destroy() { @Override public void destroy() {

@ -49,5 +49,11 @@ public interface IReceiver {
* *
* @return {@link ReceiverType} * @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}类型 * {@link AbsReceiver}类型
*/ */
public interface ReceiverType { public enum ReceiverType {
String DOWNLOAD = "download"; DOWNLOAD(1, "download"),
String UPLOAD = "upload"; 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 { public class UploadReceiver extends AbsReceiver {
private static final String TAG = "UploadReceiver"; public UploadReceiver(Object obj) {
super(obj);
}
/** /**
* 设置最大上传速度单位kb * 设置最大上传速度单位kb
@ -264,7 +266,6 @@ public class UploadReceiver extends AbsReceiver {
* 将当前类注册到Aria * 将当前类注册到Aria
*/ */
public void register() { public void register() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) { if (obj == null) {
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName())); ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
return; return;
@ -286,18 +287,17 @@ public class UploadReceiver extends AbsReceiver {
* 如果是Dialog或popupwindow需要你在撤销界面时调用该方法 * 如果是Dialog或popupwindow需要你在撤销界面时调用该方法
*/ */
@Override public void unRegister() { @Override public void unRegister() {
if (needRmListener) { if (isNeedRmListener()) {
unRegisterListener(); unRegisterListener();
} }
AriaManager.getInstance().removeReceiver(OBJ_MAP.get(getKey())); AriaManager.getInstance().removeReceiver(obj);
} }
@Override public String getType() { @Override public ReceiverType getType() {
return ReceiverType.UPLOAD; return ReceiverType.UPLOAD;
} }
@Override protected void unRegisterListener() { @Override protected void unRegisterListener() {
Object obj = OBJ_MAP.get(getKey());
if (obj == null) { if (obj == null) {
ALog.e(TAG, String.format("【%s】观察者为空", getTargetName())); ALog.e(TAG, String.format("【%s】观察者为空", getTargetName()));
return; 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) + v_3.8.10 (2020/6/26)
- fix bug https://github.com/AriaLyy/Aria/issues/703 - fix bug https://github.com/AriaLyy/Aria/issues/703
- fix bug https://github.com/AriaLyy/Aria/issues/702 - fix bug https://github.com/AriaLyy/Aria/issues/702

@ -101,7 +101,7 @@ final public class M3U8InfoTask implements IInfoTask {
ConnectionHelp.setConnectParam(mHttpOption, conn); ConnectionHelp.setConnectParam(mHttpOption, conn);
conn.setConnectTimeout(mConnectTimeOut); conn.setConnectTimeout(mConnectTimeOut);
conn.connect(); conn.connect();
handleConnect(conn); handleConnect(mEntity.getUrl(), conn);
} catch (IOException e) { } catch (IOException e) {
failDownload(e.getMessage(), false); failDownload(e.getMessage(), false);
} finally { } finally {
@ -115,7 +115,7 @@ final public class M3U8InfoTask implements IInfoTask {
mCallback = callback; mCallback = callback;
} }
private void handleConnect(HttpURLConnection conn) throws IOException { private void handleConnect(String tsListUrl, HttpURLConnection conn) throws IOException {
int code = conn.getResponseCode(); int code = conn.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) { if (code == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
@ -151,7 +151,9 @@ final public class M3U8InfoTask implements IInfoTask {
// 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志 // 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志
addIndexInfo(isGenerateIndexFile && !isLive, fos, line); addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
break; break;
} else if (line.startsWith("#EXTINF")) { }
if (line.startsWith("#EXTINF")) {
String url = reader.readLine(); String url = reader.readLine();
if (isLive) { if (isLive) {
if (onGetPeerCallback != null) { if (onGetPeerCallback != null) {
@ -163,7 +165,10 @@ final public class M3U8InfoTask implements IInfoTask {
ALog.d(TAG, url); ALog.d(TAG, url);
addIndexInfo(isGenerateIndexFile && !isLive, fos, line); addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
addIndexInfo(isGenerateIndexFile && !isLive, fos, url); 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); addIndexInfo(isGenerateIndexFile, fos, line);
int setBand = mM3U8Option.getBandWidth(); int setBand = mM3U8Option.getBandWidth();
int bandWidth = getBandWidth(line); int bandWidth = getBandWidth(line);
@ -179,12 +184,14 @@ final public class M3U8InfoTask implements IInfoTask {
failDownload(String.format("【%s】码率不存在", setBand), false); failDownload(String.format("【%s】码率不存在", setBand), false);
} }
return; return;
} else if (line.startsWith("#EXT-X-KEY")) { }
addIndexInfo(isGenerateIndexFile, fos, line);
getKeyInfo(line); if (line.startsWith("#EXT-X-KEY")) {
} else {
addIndexInfo(isGenerateIndexFile, fos, line); addIndexInfo(isGenerateIndexFile, fos, line);
getKeyInfo(tsListUrl, line);
continue;
} }
addIndexInfo(isGenerateIndexFile, fos, line);
} }
if (!isLive && extInf.isEmpty()) { 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 temp = line.substring(line.indexOf(":") + 1);
String[] params = temp.split(","); String[] params = temp.split(",");
M3U8Entity m3U8Entity = mEntity.getM3U8Entity(); M3U8Entity m3U8Entity = mEntity.getM3U8Entity();
@ -273,7 +280,7 @@ final public class M3U8InfoTask implements IInfoTask {
m3U8Entity.keyFormatVersion = param.split("=")[1]; 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.setRequestProperty("Cookie", cookies);
conn.setConnectTimeout(mConnectTimeOut); conn.setConnectTimeout(mConnectTimeOut);
conn.connect(); conn.connect();
handleConnect(conn); handleConnect(newUrl, conn);
conn.disconnect(); conn.disconnect();
} }
@ -349,7 +356,7 @@ final public class M3U8InfoTask implements IInfoTask {
conn.setRequestProperty("Cookie", cookies); conn.setRequestProperty("Cookie", cookies);
conn.setConnectTimeout(mConnectTimeOut); conn.setConnectTimeout(mConnectTimeOut);
conn.connect(); conn.connect();
handleConnect(conn); handleConnect(bandWidthM3u8Url, conn);
conn.disconnect(); 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; HttpURLConnection conn = null;
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
@ -375,7 +382,7 @@ final public class M3U8InfoTask implements IInfoTask {
IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter(); IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter();
String keyUrl = info.keyUrl; String keyUrl = info.keyUrl;
if (keyUrlConverter != null) { if (keyUrlConverter != null) {
keyUrl = keyUrlConverter.convert(mEntity.getUrl(), keyUrl); keyUrl = keyUrlConverter.convert(mEntity.getUrl(), tsListUr, keyUrl);
} }
if (TextUtils.isEmpty(keyUrl)) { if (TextUtils.isEmpty(keyUrl)) {
ALog.e(TAG, "m3u8密钥key url 为空"); ALog.e(TAG, "m3u8密钥key url 为空");

@ -25,9 +25,10 @@ public interface IKeyUrlConverter extends IEventHandler {
/** /**
* 将被加密的密钥下载地址转换为可使用的http下载地址 * 将被加密的密钥下载地址转换为可使用的http下载地址
* *
* @param m3u8Url m3u8文件的下载地址 * @param m3u8Url 主m3u8的url地址
* @param tsListUrl m3u8切片列表url地址
* @param keyUrl 加密的url地址 * @param keyUrl 加密的url地址
* @return 可正常访问的http地址 * @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 Class<S> service;
private ClassLoader loader; private ClassLoader loader;
private Enumeration<URL> configs = null; private Enumeration<URL> configs = null;
private Iterator<String> pending = null; private List<String> pending = null;
private LazyLoader(Class<S> service, ClassLoader loader) { private LazyLoader(Class<S> service, ClassLoader loader) {
this.service = service; 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 an I/O error occurs while reading from the given URL, or
// if a configuration-file format error is detected // 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; InputStream in = null;
BufferedReader r = null; BufferedReader r = null;
ArrayList<String> names = new ArrayList<>(); ArrayList<String> names = new ArrayList<>();
@ -122,7 +122,7 @@ public class AriaServiceLoader<S> {
fail(service, "Error closing configuration file", y); fail(service, "Error closing configuration file", y);
} }
} }
return names.iterator(); return names;
} }
private void fail(Class<?> service, String msg, Throwable cause) private void fail(Class<?> service, String msg, Throwable cause)
@ -177,8 +177,8 @@ public class AriaServiceLoader<S> {
} }
private S loadService(String serviceName) { private S loadService(String serviceName) {
while (pending.hasNext()) { for (String s : pending) {
if (pending.next().equals(serviceName)) { if (s.equals(serviceName)) {
Class<?> c = null; Class<?> c = null;
try { try {
c = Class.forName(serviceName, false, loader); c = Class.forName(serviceName, false, loader);
@ -224,7 +224,7 @@ public class AriaServiceLoader<S> {
fail(service, "Error locating configuration files", x); fail(service, "Error locating configuration files", x);
} }
} }
while ((pending == null) || !pending.hasNext()) { while ((pending == null) || pending.isEmpty()) {
if (!configs.hasMoreElements()) { if (!configs.hasMoreElements()) {
return; return;
} }

@ -16,6 +16,7 @@
package com.arialyy.aria.util; package com.arialyy.aria.util;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -23,6 +24,7 @@ import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.FtpUrlEntity;
import dalvik.system.DexFile; import dalvik.system.DexFile;
@ -34,6 +36,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -60,6 +64,101 @@ public class CommonUtil {
private static final String TAG = "CommonUtil"; private static final String TAG = "CommonUtil";
public static final String SERVER_CHARSET = "ISO-8859-1"; public static final String SERVER_CHARSET = "ISO-8859-1";
private static long lastClickTime; private static long lastClickTime;
/**
* androidandroidxsupport的fragmentdialogFragment类名
*/
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) * 获取线程名称命名规则md5(任务地址 + 线程id)

@ -106,7 +106,6 @@ public class ComponentUtil {
* @return 返回任务工具 * @return 返回任务工具
*/ */
public synchronized IUtil buildUtil(AbsTaskWrapper wrapper, IEventListener listener) { public synchronized IUtil buildUtil(AbsTaskWrapper wrapper, IEventListener listener) {
utilLoader.reload();
int requestType = wrapper.getRequestType(); int requestType = wrapper.getRequestType();
String className = null; String className = null;
switch (requestType) { switch (requestType) {

@ -4,11 +4,23 @@ import androidx.test.runner.AndroidJUnit4
import com.arialyy.aria.util.CommonUtil import com.arialyy.aria.util.CommonUtil
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.net.URLDecoder
import java.net.URLEncoder
import kotlin.math.pow import kotlin.math.pow
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class ApiTest { 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 @Test
fun testSpeed() { fun testSpeed() {
val speed = 1024.0.pow(4.0) * 2 val speed = 1024.0.pow(4.0) * 2

@ -311,8 +311,9 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
//.setMergeHandler(new TsMergeHandler()); //.setMergeHandler(new TsMergeHandler());
option.setUseDefConvert(false); option.setUseDefConvert(false);
option.setKeyUrlConverter(new KeyUrlConverter()); option.setKeyUrlConverter(new KeyUrlConverter());
option.setVodTsUrlConvert(new VodTsUrlConverter());
option.setBandWidthUrlConverter(new BandWidthUrlConverter()); option.setBandWidthUrlConverter(new BandWidthUrlConverter());
option.setUseDefConvert(true); //option.setUseDefConvert(true);
return option; return option;
} }
@ -329,18 +330,19 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
@Override public List<String> convert(String m3u8Url, List<String> tsUrls) { @Override public List<String> convert(String m3u8Url, List<String> tsUrls) {
Uri uri = Uri.parse(m3u8Url); Uri uri = Uri.parse(m3u8Url);
//String parentUrl = "http://devimages.apple.com/iphone/samples/bipbop/gear1/"; //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/"; //String parentUrl = "http://" + uri.getHost() + "/gear1/";
//int index = m3u8Url.lastIndexOf("/"); //int index = m3u8Url.lastIndexOf("/");
//String parentUrl = m3u8Url.substring(0, index + 1); //String parentUrl = m3u8Url.substring(0, index + 1);
//String parentUrl = "https://v1.szjal.cn/20190819/Ql6UD1od/"; //String parentUrl = "https://v1.szjal.cn/20190819/Ql6UD1od/";
//String parentUrl = "http://" + uri.getHost() + "/"; //String parentUrl = "http://" + uri.getHost() + "/";
List<String> newUrls = new ArrayList<>(); //List<String> newUrls = new ArrayList<>();
for (String url : tsUrls) { //for (String url : tsUrls) {
newUrls.add(parentUrl + url); // 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 // 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 = "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 = "http://youku.cdn7-okzy.com/20200123/16815_fbe419ed/index.m3u8";
//private final String defUrl = "https://cn7.kankia.com/hls/20200108/e1eaec074274c64fe46a3bdb5d2ba487/1578488360/index.m3u8"; //private final String defUrl = "https://cn7.kankia.com/hls/20200108/e1eaec074274c64fe46a3bdb5d2ba487/1578488360/index.m3u8";

Loading…
Cancel
Save