修复一个匿名内部类中的内存溢出的问题 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. 197
      Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
  3. 8
      Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java
  4. 19
      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.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();
/**
* androidandroidxsupport的fragmentdialogFragment类名
*/
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 和其DialogFragment的映射表
*/
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;
}
}
// 移除寄生的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();
}
if (key.equals(getKey(ReceiverType.DOWNLOAD, obj))
|| key.equals(getKey(ReceiverType.UPLOAD, obj))
) {
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;
/**
* 当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生成规则
@ -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 {
}
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;
/**
* 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)

@ -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";

Loading…
Cancel
Save