添加了清除缓存,存在BUG

This commit is contained in:
2025-08-20 21:23:22 +08:00
parent 96e3c86cd4
commit ae088a1a7a
93 changed files with 283918 additions and 281546 deletions

View File

@@ -205,7 +205,8 @@ dependencies {
api 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
api 'io.github.mayzs:paho.mqtt.android:1.2.1'
// api project(':LocalAar')
/// 内存泄露的方法
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
//2. 云normal SDK
//aar的名称例如WbCloudNormal-v5.1.10-123456789.aar填入 'WbCloudNormal-v5.1.10-123456789.aar'
}

View File

@@ -8,20 +8,12 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application android:allowBackup="true">
<service
android:name=".service.MyRoomService"
android:enabled="true"
android:exported="true"></service>
<activity
android:name=".activity.QxActivity"
android:exported="false" />
<service
android:name=".http.FloatingWindowService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="specialUse" />
<!-- <service-->
<!-- android:name=".http.FloatingWindowService"-->
<!-- android:enabled="true"-->
<!-- android:exported="true"-->
<!-- android:foregroundServiceType="specialUse" />-->
<activity
android:name=".activity.WebViewActivity"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,8 +2,10 @@ package com.qxcm.moduleutil.activity;
import static com.liulishuo.okdownload.OkDownloadProvider.context;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -22,16 +24,21 @@ import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import com.alibaba.android.arouter.launcher.ARouter;
import com.blankj.utilcode.util.ActivityUtils;
import com.blankj.utilcode.util.BarUtils;
import com.petterp.floatingx.FloatingX;
import com.petterp.floatingx.assist.helper.FxAppHelper;
import com.qxcm.moduleutil.R;
import com.qxcm.moduleutil.base.CommonAppContext;
import com.qxcm.moduleutil.event.MqttBean;
import com.qxcm.moduleutil.utils.ARouteConstants;
import com.qxcm.moduleutil.utils.BackgroundManager;
import com.qxcm.moduleutil.utils.ColorManager;
import com.qxcm.moduleutil.utils.DisplayUtil;
import com.qxcm.moduleutil.utils.LanguageUtil;
import com.qxcm.moduleutil.utils.SpUtil;
import com.qxcm.moduleutil.widget.PiaoPingManager;
import com.tencent.qcloud.tuikit.tuichat.bean.ChatInfo;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -54,7 +61,26 @@ public abstract class BaseAppCompatActivity<VDB extends ViewDataBinding> extends
// private LoadingDialog mLoadingDialog;
// 添加广播接收器成员变量
private BroadcastReceiver mLogoutReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if ("com.qxcm.moduleutil.ACTION_USER_LOGOUT".equals(intent.getAction())) {
// 在这里处理用户登出后的UI更新
// 例如:隐藏需要登录才能显示的控件
// 或者跳转到登录状态的页面
handleUserLogout();
}
}
};
// 处理用户登出的方法
protected void handleUserLogout() {
// 子类可以重写此方法处理具体的登出逻辑
// 例如更新UI状态、清除本地数据等
ActivityUtils.finishAllActivities();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -82,6 +108,9 @@ public abstract class BaseAppCompatActivity<VDB extends ViewDataBinding> extends
// 应用当前颜色
applyCurrentColors();
// 注册登出广播接收器
IntentFilter filter = new IntentFilter("com.qxcm.moduleutil.ACTION_USER_LOGOUT");
registerReceiver(mLogoutReceiver, filter);
// 动态判断是否包含 @Subscribe 注解的方法
boolean hasSubscribeMethods = false;
@@ -232,6 +261,13 @@ public abstract class BaseAppCompatActivity<VDB extends ViewDataBinding> extends
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
try {
unregisterReceiver(mLogoutReceiver);
} catch (Exception e) {
// 忽略异常
}
super.onDestroy();
}
@@ -304,4 +340,10 @@ public abstract class BaseAppCompatActivity<VDB extends ViewDataBinding> extends
// FxAppHelper fxAppHelper = FxAppHelper.builder().setContext(this).setLayout(R.layout.item_piaoping).build();
// FloatingX.install(fxAppHelper).show();
// }
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ChatInfo event) {
String id= event.getId().replace("g", "");
ARouter.getInstance().build(ARouteConstants.H5).withString("url", CommonAppContext.getInstance().getCurrentEnvironment().getH5Url()+"/web/index.html#/pages/union/setGroup?id="+ SpUtil.getToken()+"&guildId="+id).navigation();
}
}

View File

@@ -108,21 +108,21 @@ public abstract class BaseMvpActivity<P extends IPresenter, VDB extends ViewData
@Override
public void onSuccess() {
LogUtils.e("@@@","成功");
V2TIMUserFullInfo userFullInfo = new V2TIMUserFullInfo();
userFullInfo.setNickname(userBean.getNickname());
userFullInfo.setFaceUrl(userBean.getAvatar());
userFullInfo.setAllowType(userBean.getSex());
V2TIMManager.getInstance().setSelfInfo(userFullInfo, new V2TIMCallback() {
@Override
public void onSuccess() {
LogUtils.e("@@@", "成功");
}
@Override
public void onError(int code, String desc) {
LogUtils.e("@@@", "描述"+desc);
}
});
// V2TIMUserFullInfo userFullInfo = new V2TIMUserFullInfo();
// userFullInfo.setNickname(userBean.getNickname());
// userFullInfo.setFaceUrl(userBean.getAvatar());
// userFullInfo.setAllowType(userBean.getSex());
// V2TIMManager.getInstance().setSelfInfo(userFullInfo, new V2TIMCallback() {
// @Override
// public void onSuccess() {
// LogUtils.e("@@@", "成功");
// }
//
// @Override
// public void onError(int code, String desc) {
// LogUtils.e("@@@", "描述"+desc);
// }
// });
initLocation();
}
});
@@ -176,21 +176,21 @@ public abstract class BaseMvpActivity<P extends IPresenter, VDB extends ViewData
@Subscribe(threadMode = ThreadMode.MAIN)
public void userInfoEvent(UserInfo event) {
V2TIMUserFullInfo userFullInfo = new V2TIMUserFullInfo();
userFullInfo.setNickname(event.getNickname());
userFullInfo.setFaceUrl(event.getAvatar());
userFullInfo.setAllowType(event.getSex());
V2TIMManager.getInstance().setSelfInfo(userFullInfo, new V2TIMCallback() {
@Override
public void onSuccess() {
LogUtils.e("@@@", "成功");
}
@Override
public void onError(int code, String desc) {
LogUtils.e("@@@", "描述"+desc);
}
});
// V2TIMUserFullInfo userFullInfo = new V2TIMUserFullInfo();
// userFullInfo.setNickname(event.getNickname());
// userFullInfo.setFaceUrl(event.getAvatar());
// userFullInfo.setAllowType(event.getSex());
// V2TIMManager.getInstance().setSelfInfo(userFullInfo, new V2TIMCallback() {
// @Override
// public void onSuccess() {
// LogUtils.e("@@@", "成功");
// }
//
// @Override
// public void onError(int code, String desc) {
// LogUtils.e("@@@", "描述"+desc);
// }
// });
}
/**
* 显示全局飘屏消息(支持任意位置飘过)

View File

@@ -13,6 +13,7 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -68,6 +69,8 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
@Override
protected void initData() {
WebSettings webSettings = mBinding.webView.getSettings();
// 禁用水平滚动
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setJavaScriptEnabled(true);
@@ -75,14 +78,40 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
//增加JSBridge
mBinding.webView.addJavascriptInterface(new WebAppInterface(this), "Android");
// mBinding.webView.addJavascriptInterface(new WebViewBridgeConfig(title), WebViewBridgeConfig.NAME);
webSettings.setBuiltInZoomControls(false);
webSettings.setSupportZoom(false);
if (title!=null && !title.isEmpty()){
if (title.contains("协议")) {
webSettings.setDisplayZoomControls(false); // 隐藏默认缩放控件
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); // 使用正常布局算法
webSettings.setBuiltInZoomControls(false);
webSettings.setSupportZoom(false);
// 重要:设置布局算法为适应屏幕
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
// 设置初始缩放
mBinding.webView.setInitialScale(100);
mBinding.webView.getSettings().setUseWideViewPort(false);
}else {
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
}
}else {
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
}
webSettings.setBuiltInZoomControls(true);
webSettings.setSupportZoom(true);
webSettings.setDomStorageEnabled(true);
webSettings.setBlockNetworkImage(false);//解决图片不显示
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
// webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
mBinding.webView.setHorizontalScrollBarEnabled(false);//水平不显示
mBinding.webView.setVerticalScrollBarEnabled(false); //垂直不显示
mBinding.webView.setScrollbarFadingEnabled(false);
mBinding.webView.setScrollContainer(true);
mBinding.webView.setWebViewClient(new WebViewClient());
mBinding.webView.setBackgroundColor(Color.TRANSPARENT);
mBinding.webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
@@ -98,6 +127,7 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
// }
// });
mBinding.webView.setWebChromeClient(new WebChromeClient() {
//配置权限同样在WebChromeClient中实现
@@ -109,7 +139,32 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (title != null && !title.isEmpty()) {
if (title.contains("协议")) {
if (newProgress >= 100) {
// 页面加载完成后调整文本换行
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// 注入CSS确保文字自动换行
mBinding.webView.loadUrl("javascript:(function() {" +
"var sheets = document.styleSheets;" +
"for (var i = 0; i < sheets.length; i++) {" +
" try {" +
" sheets[i].addRule('*', 'word-wrap: break-word !important; white-space: normal !important; overflow-x: hidden !important;', 0);" +
" } catch(e) {" +
" try {" +
" sheets[i].insertRule('* { word-wrap: break-word !important; white-space: normal !important; overflow-x: hidden !important; }', 0);" +
" } catch(e2) {}" +
" }" +
"}" +
"})()");
}
}, 100);
}
}
}
}
// For Android < 3.0
@@ -142,6 +197,8 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
});
}
private void showFileChooser() {
@@ -190,7 +247,7 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
getWindow().getDecorView().setBackgroundResource(com.qxcm.moduleutil.R.color.white);
title = getIntent().getStringExtra("title");
url = getIntent().getStringExtra("url");
if (title!=null) {
if (title != null) {
if (title.equals("举报") || title.equals("等级") || title.equals("公会") || title.equals("邀请") || title.equals("反馈")) {
mBinding.topBar.setVisibility(GONE);
// mBinding.webView.setPadding(0,20,0,0);
@@ -198,20 +255,19 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
mBinding.topBar.setVisibility(VISIBLE);
mBinding.topBar.setTitle(title);
}
}else {
if (url.equals(CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=6")){
} else {
if (url.equals("file:///android_asset/page_yongh.html")) {
mBinding.topBar.setVisibility(VISIBLE);
mBinding.topBar.setTitle("用户协议");
}else if (url.equals(CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=4")){
} else if (url.equals("file:///android_asset/page_show.html")) {
mBinding.topBar.setVisibility(VISIBLE);
mBinding.topBar.setTitle("隐私协议");
}else {
} else {
mBinding.topBar.setVisibility(GONE);
}
}
}
@Override
@@ -226,6 +282,24 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
mContext = c;
}
@JavascriptInterface
public void adjustTextWrap() {
// 通过 JavaScript 注入 CSS 样式确保文字自动换行
final String javascript = "javascript:(function() {" +
"var style = document.createElement('style');" +
"style.type = 'text/css';" +
"style.innerHTML = '* { word-wrap: break-word; white-space: normal !important; overflow-x: hidden !important; } body { max-width: 100% !important; }';" +
"document.getElementsByTagName('head')[0].appendChild(style);" +
"})()";
((Activity) mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
mBinding.webView.loadUrl(javascript);
}
});
}
// 被 H5 调用的方法
@JavascriptInterface
public void showToast(String toast) {
@@ -254,6 +328,7 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
public void jumpRoomPage(String room_id) {
ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", room_id).navigation();
}
@JavascriptInterface
public void jumpWebPage(String objects) {
// ARouter.getInstance().build(ARouteConstants.USER_HOME_PAGE).navigation();
@@ -261,7 +336,7 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
}
@JavascriptInterface
public void enterGroupChat(String group_id,String cover,String guild_name) {
public void enterGroupChat(String group_id, String cover, String guild_name) {
Intent intent = new Intent(mContext, TUIGroupChatActivity.class);
intent.putExtra(TUIConstants.TUIChat.CHAT_ID, group_id);
intent.putExtra(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_GROUP);
@@ -269,14 +344,15 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
}
@JavascriptInterface
public void chatWithUser(String user_id,String nickname) {
public void chatWithUser(String user_id, String nickname) {
Intent intent = new Intent(mContext, TUIC2CChatActivity.class);
intent.putExtra(TUIConstants.TUIChat.CHAT_ID, user_id);
intent.putExtra(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_C2C);
mContext.startActivity(intent);
}
@JavascriptInterface
public void exchange(){
public void exchange() {
ARouter.getInstance().build(ARouteConstants.CURRENCY).navigation();
}
@@ -292,7 +368,7 @@ public class WebViewActivity extends BaseAppCompatActivity<ActivityWebViewBindin
}
@JavascriptInterface
public void Recharge(){
public void Recharge() {
ARouter.getInstance().build(ARouteConstants.RECHARGE_ACTIVITY).navigation();
}
}

View File

@@ -123,6 +123,7 @@ public class GiftPackAdapter extends BaseAdapter {
viewHolder.item_layout = (ConstraintLayout) convertView.findViewById(R.id.cl_gift);
viewHolder.ivDownOn = (ImageView) convertView.findViewById(R.id.iv_down_on);
viewHolder.cl_iv_down_on = (ConstraintLayout) convertView.findViewById(R.id.cl_iv_down_on);
viewHolder.integral = (TextView) convertView.findViewById(R.id.integral);
convertView.setTag(viewHolder);
} else {
@@ -131,10 +132,11 @@ public class GiftPackAdapter extends BaseAdapter {
viewHolder.item_layout.setOnClickListener(v -> {
// RoonGiftModel clickedModel = (RoonGiftModel) v.getTag();
EventBus.getDefault().post(new RoomGiftPackToEvent(this, giftModel, 1));
EventBus.getDefault().post(new RoomGiftPackToEvent(this, giftModel, 2));
});
viewHolder.integral.setVisibility(View.VISIBLE);
viewHolder.integral.setText("x"+giftModel.getNum());
//设置礼物名字
viewHolder.tv_gift_name.setText(giftModel.getGift_name());
//设置礼物价格
@@ -164,7 +166,7 @@ public class GiftPackAdapter extends BaseAdapter {
static class ViewHolder {
public ConstraintLayout item_layout;
public TextView tv_gift_name, tv_gift_price, tv_gift_num;
public TextView tv_gift_name, tv_gift_price, integral;
public ImageView iv_gift_pic;
public TextView tv_gift_change_love_values;
public ImageView ivDownOn;

View File

@@ -40,21 +40,26 @@ import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.ProcessUtils;
import com.blankj.utilcode.util.ServiceUtils;
import com.bumptech.glide.Glide;
import com.lahm.library.EasyProtectorLib;
import com.lahm.library.EmulatorCheckCallback;
import com.qxcm.moduleutil.R;
import com.qxcm.moduleutil.bean.UserBean;
import com.qxcm.moduleutil.event.AppLifecycleEvent;
import com.qxcm.moduleutil.event.RoomGiftRunable;
import com.qxcm.moduleutil.interfaces.AppLifecycleUtil;
import com.qxcm.moduleutil.listener.MessageListenerSingleton;
import com.qxcm.moduleutil.rtc.AgoraManager;
import com.qxcm.moduleutil.service.MyMqttService;
import com.qxcm.moduleutil.utils.ARouteConstants;
import com.qxcm.moduleutil.utils.FloatWindowHelper;
import com.qxcm.moduleutil.utils.ImageUtils;
import com.qxcm.moduleutil.utils.MemoryOptimizationUtils;
import com.qxcm.moduleutil.utils.SpUtil;
import com.qxcm.moduleutil.utils.UtilConfig;
import com.qxcm.moduleutil.utils.config.EnvironmentEnum;
import com.qxcm.moduleutil.utils.config.EnvironmentPrefs;
import com.qxcm.moduleutil.utils.location.SystemLocationProvider;
import com.qxcm.moduleutil.widget.CommonAppConfig;
import com.qxcm.moduleutil.widget.CustomRefreshHeader;
import com.qxcm.moduleutil.widget.PiaoPingManager;
@@ -69,6 +74,7 @@ import com.scwang.smartrefresh.layout.header.ClassicsHeader;
import com.tencent.bugly.crashreport.CrashReport;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuicore.interfaces.TUICallback;
import com.tencent.qcloud.tuikit.tuichat.bean.ChatInfo;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -116,26 +122,92 @@ public class CommonAppContext extends MultiDexApplication {
EnvironmentPrefs prefs = new EnvironmentPrefs(this);
currentEnvironment = prefs.getSelectedEnvironment();
initialization();
piaoPingManager = PiaoPingManager.getInstance(this);
// 在独立进程中运行 LeakCanary 分析
// leakcanary.LeakCanary.setConfig(new leakcanary.LeakCanary.Config());
// 禁用自动堆分析
}
public void initialization(){
UtilConfig.init(this);
registerActivityLifecycleCallbacks();
initWebView();
private void handleMemoryTrim(int level) {
LogUtils.w("MemoryTrim", "Memory trim level: " + level);
switch (level) {
case TRIM_MEMORY_RUNNING_MODERATE:
LogUtils.w("MemoryTrim", "Memory trim: moderate");
break;
case TRIM_MEMORY_RUNNING_LOW:
LogUtils.w("MemoryTrim", "Memory trim: low");
clearCaches();
break;
case TRIM_MEMORY_RUNNING_CRITICAL:
LogUtils.w("MemoryTrim", "Memory trim: critical");
clearCaches();
MemoryOptimizationUtils.forceGC();
break;
case TRIM_MEMORY_UI_HIDDEN:
LogUtils.w("MemoryTrim", "Memory trim: UI hidden");
clearCaches();
break;
case TRIM_MEMORY_BACKGROUND:
LogUtils.w("MemoryTrim", "Memory trim: background");
clearCaches();
break;
case TRIM_MEMORY_MODERATE:
LogUtils.w("MemoryTrim", "Memory trim: moderate background");
clearCaches();
break;
case TRIM_MEMORY_COMPLETE:
LogUtils.w("MemoryTrim", "Memory trim: complete");
clearCaches();
MemoryOptimizationUtils.forceGC();
break;
}
}
private void clearCaches() {
try {
// 清理图片缓存
MemoryOptimizationUtils.clearImageCache(this);
// 清理 Glide 内存缓存
Glide.get(this).clearMemory();
// 清理SVGA缓存
MemoryOptimizationUtils.clearSVGACache();
// 清理其他缓存
if (piaoPingManager != null) {
piaoPingManager.unsubscribe();
}
// 清理消息缓存
RoomGiftRunable.clearMqttCache();
} catch (Exception e) {
LogUtils.e("ClearCaches", "Error clearing caches: " + e.getMessage());
}
}
public void initialization() {
if (ProcessUtils.isMainProcess()) {
initARouter();
if (SpUtil.isAgreePolicy()) {
UtilConfig.init(this);
registerActivityLifecycleCallbacks();
initWebView();
checkInEmulator();
UtilConfig.checkInEmulator();
AgoraManager.getInstance(this).init(currentEnvironment.getSwSdkAppId());
MessageListenerSingleton.getInstance();
CrashReport.initCrashReport(this, "865cfe9555", true);/*bugly初始化*/
ServiceUtils.startService(MyMqttService.class);/*Mqtt初始化*/
// 每次启动应用时重置状态
SpUtil.getInstance().setBooleanValue("youth_model_shown", false);
}
}
piaoPingManager = PiaoPingManager.getInstance(this);
piaoPingManager.subscribe();
AgoraManager.getInstance(this).init(currentEnvironment.getSwSdkAppId());
MessageListenerSingleton.getInstance();
CrashReport.initCrashReport(this, "865cfe9555", true);/*bugly初始化*/
ServiceUtils.startService(MyMqttService.class);/*Mqtt初始化*/
}
private PiaoPingManager piaoPingManager;
@@ -234,7 +306,14 @@ public class CommonAppContext extends MultiDexApplication {
CommonAppConfig.getInstance().setFrontGround(true);
// FloatWindowHelper.setFloatWindowVisible(true);
AppLifecycleUtil.onAppFrontGround();
// 确保在主线程中订阅
if (activity != null && !activity.isFinishing()) {
activity.runOnUiThread(() -> {
if (piaoPingManager != null) {
piaoPingManager.subscribe();
}
});
}
}
}
@@ -258,6 +337,14 @@ public class CommonAppContext extends MultiDexApplication {
CommonAppConfig.getInstance().setFrontGround(false);
// FloatWindowHelper.setFloatWindowVisible(false);
AppLifecycleUtil.onAppBackGround();
// 确保在主线程中取消订阅
if (activity != null && !activity.isFinishing()) {
activity.runOnUiThread(() -> {
if (piaoPingManager != null) {
piaoPingManager.unsubscribe();
}
});
}
}
}
@@ -270,6 +357,7 @@ public class CommonAppContext extends MultiDexApplication {
public void onActivityDestroyed(Activity activity) {
}
});
}
@@ -369,6 +457,8 @@ public class CommonAppContext extends MultiDexApplication {
// SpUtil.getInstance().removeValue(
// SPConstants.USER_ID, SPConstants.TOKEN, SPConstants.USER_INFO
// );
isShow = false;
isPlaying = false;
mUserBean = null;
SpUtil.saveUserId(-1);
SpUtil.saveUserBean(new UserBean());
@@ -376,11 +466,16 @@ public class CommonAppContext extends MultiDexApplication {
Intent intent = new Intent("com.example.action.LAUNCH_PAGE");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(intent);
piaoPingManager.unsubscribe();
// piaoPingManager.unsubscribe();
FileUtils.deleteAllInDir(getCacheDir());
FileUtils.deleteAllInDir(getExternalCacheDir());
AgoraManager.getInstance(getApplicationContext()).destroy();
// 每次启动应用时重置状态
SpUtil.setBooleanValue("youth_model_shown", false);
// 发送广播通知所有Activity刷新状态
Intent refreshIntent = new Intent("com.qxcm.moduleutil.ACTION_USER_LOGOUT");
sendBroadcast(refreshIntent);
}
public static boolean isAlipayInstalled(Context context) {
@@ -417,4 +512,5 @@ public class CommonAppContext extends MultiDexApplication {
}
});
}
}

View File

@@ -44,7 +44,7 @@ public class UserInfo implements Serializable {
private String dress;//头像框
private String charm;//魅力值
private String room_id;
private String guile;//公会名称
private String guild;//公会名称
private String is_mute;//禁言状态 0否 1是
private String is_mute_pit;//禁麦状态 0否 1是
private String is_manager;//是否是管理员 0否 1是

View File

@@ -42,7 +42,9 @@ public class PolicyDialog extends Dialog {
ClickableSpan clickSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
ARouter.getInstance().build(ARouteConstants.H5).withString("url", CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=6" ).withString("title", "用户协议").navigation();
// ARouter.getInstance().build(ARouteConstants.H5).withString("url", CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=6" ).withString("title", "用户协议").navigation();
ARouter.getInstance().build(ARouteConstants.H5).withString("url", "file:///android_asset/page_yongh.html").withString("title", "用户协议").navigation();
}
@Override
@@ -54,7 +56,9 @@ public class PolicyDialog extends Dialog {
ClickableSpan ysClickSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
ARouter.getInstance().build(ARouteConstants.H5).withString("url", CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=4").withString("title", "隐私协议").navigation();
// ARouter.getInstance().build(ARouteConstants.H5).withString("url", CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=4").withString("title", "隐私协议").navigation();
ARouter.getInstance().build(ARouteConstants.H5).withString("url", "file:///android_asset/page_show.html").withString("title", "隐私协议").navigation();
}
@Override

View File

@@ -1,15 +1,7 @@
package com.qxcm.moduleutil.event;
import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.ServiceUtils;
import com.google.gson.Gson;
import com.qxcm.moduleutil.base.CommonAppContext;
import com.qxcm.moduleutil.service.FloatingWindow;
import com.qxcm.moduleutil.widget.PiaoPingManager;
import org.greenrobot.eventbus.EventBus;
@@ -27,6 +19,39 @@ public class RoomGiftRunable implements Runnable {
private static final List<MqttBean> mqttCache = new ArrayList<>();
private static final int MAX_CACHE_SIZE = 6;
private static final Object lock = new Object();
public static synchronized void addMqttCache(MqttBean mqttBean) {
if (mqttBean == null) return;
// 检查内存状态
if (isMemoryLow()) {
// 内存不足时清理缓存
clearMqttCache();
return;
}
// 限制缓存大小
if (mqttCache.size() >= MAX_CACHE_SIZE) {
mqttCache.remove(0); // 移除最旧的项
}
mqttCache.add(mqttBean);
}
public static synchronized void clearMqttCache() {
mqttCache.clear();
// 建议进行垃圾回收
// System.gc();
}
private static boolean isMemoryLow() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
return memoryUsage > 0.8; // 内存使用超过80%认为是低内存
}
public RoomGiftRunable(String data) {
this.data = data;
}
@@ -36,10 +61,26 @@ public class RoomGiftRunable implements Runnable {
MqttBean mqttBean= GsonUtils.fromJson(data, MqttBean.class);
// 线程安全地添加到缓存
synchronized (lock) {
if (mqttCache.size() >= MAX_CACHE_SIZE) {
mqttCache.remove(0); // 移除第一条数据
if (mqttBean == null) return;
// 检查内存状态
if (isMemoryLow()) {
// 内存不足时清理缓存
clearMqttCache();
return;
}
mqttCache.add(mqttBean); // 添加新数据
// 限制缓存大小
if (mqttCache.size() >= MAX_CACHE_SIZE) {
mqttCache.remove(0); // 移除最旧的项
}
mqttCache.add(mqttBean);
// if (mqttCache.size() >= MAX_CACHE_SIZE) {
// mqttCache.remove(0); // 移除第一条数据
// }
// mqttCache.add(mqttBean); // 添加新数据
}
EventBus.getDefault().post(mqttBean);
// new FloatingWindow(CommonAppContext.getInstance(),mqttBean);

View File

@@ -84,6 +84,14 @@ public interface ApiServer {
@POST(Constants.SEND_CODE)
Observable<BaseModel<Object>> sendCode(@Field("mobile") String mobile, @Field("event") String event);
@FormUrlEncoded //手机换绑
@POST(Constants.MODIFY_MOBILE)
Call<BaseModel<String>> mobileView(@Field("mobile") String mobile, @Field("new_mobile") String new_mobile,@Field("sms_code") String sms_code);
@FormUrlEncoded //手机绑定
@POST(Constants.BIND_MOBILE)
Call<BaseModel<String>> mobileView2( @Field("new_mobile") String new_mobile,@Field("sms_code") String sms_code);
@FormUrlEncoded
@POST(Constants.LOGIN)
Observable<BaseModel<List<UserBean>>> login(@Field("user_login") String user_login, @Field("sms_code") String sms_code);
@@ -254,7 +262,7 @@ public interface ApiServer {
@FormUrlEncoded
@POST(Constants.GET_EXPAND_COLUMN)
Observable<BaseModel<List<ExpandColumnBean>>> getExpandColumn(@Field("type") String type);
Observable<BaseModel<List<ExpandColumnBean>>> getExpandColumn(@Field("type") String type,@Field("page") String page, @Field("page_limit") String page_limit);
@GET(Constants.GET_OFFICIAL_NOTICE)
Observable<BaseModel<NewsDataBean>> getOfficialNotice();
@@ -644,7 +652,7 @@ public interface ApiServer {
@FormUrlEncoded
@POST(Constants.POST_CHARM_LIST)
Observable<BaseModel<RoomDetails>> getCharmList(@Field("room_id") String room_id, @Field("start_time") String start_time, @Field("end_time") String end_time);
Observable<BaseModel<RoomDetails>> getCharmList(@Field("room_id") String room_id, @Field("start_time") String start_time, @Field("end_time") String end_time,@Field("page") String page);
@FormUrlEncoded
@POST(Constants.POST_SET_UPLOAD_BG_IMG)

View File

@@ -144,7 +144,7 @@ public class RetrofitClient {
private OkHttpClient provideOkHttpClient() {
Map<String, String> headers = Collections.emptyMap();
if (SpUtil.isAgreePolicy()) {
headers = SystemUtils.getSystemParams();
headers = SystemUtils.getSystemParams();
}
SetCookieCache cookieCache = new SetCookieCache();
ClearableCookieJar cookieJar =
@@ -248,6 +248,41 @@ public class RetrofitClient {
sApiServer.sendCode(mobile, event).compose(new DefaultTransformer<>()).subscribe(observer);
}
public void mobileView(String mobile, String new_mobile, String sms_code, BaseObserver<String> observer) {
sApiServer.mobileView(mobile, new_mobile, sms_code).enqueue(new Callback<BaseModel<String>>() {
@Override
public void onResponse(Call<BaseModel<String>> call, Response<BaseModel<String>> response) {
if (response.code() == 200) {
BaseModel<String> body = response.body();
observer.onNext(body.getMsg());
}
}
@Override
public void onFailure(Call<BaseModel<String>> call, Throwable t) {
t.printStackTrace();
}
});
}
public void mobileView2(String new_mobile, String sms_code, BaseObserver<String> observer) {
sApiServer.mobileView2(new_mobile, sms_code).enqueue(new Callback<BaseModel<String>>() {
@Override
public void onResponse(Call<BaseModel<String>> call, Response<BaseModel<String>> response) {
if (response.code() == 200) {
BaseModel<String> body = response.body();
observer.onNext(body.getMsg());
}
}
@Override
public void onFailure(Call<BaseModel<String>> call, Throwable t) {
t.printStackTrace();
}
});
}
public void login(String mobile, String password, BaseObserver<List<UserBean>> observer) {
sApiServer.login(mobile, password).compose(new DefaultTransformer<>()).subscribe(observer);
}
@@ -453,24 +488,24 @@ public class RetrofitClient {
sApiServer.dailyTasksOpenBox(gift_box_id).compose(new DefaultTransformer<>()).subscribe(observer);
}
public void dailyTasksReceive(String task_id,BaseObserver<String> observer){
public void dailyTasksReceive(String task_id, BaseObserver<String> observer) {
sApiServer.dailyTasksReceive(task_id).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.code() == 200){
if (response.code() == 200) {
observer.onNext("任务完成");
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
t.printStackTrace();
}
});
}
public void dailyTasksComplete(String task_id,BaseObserver<RoomSingleton> observer) {
public void dailyTasksComplete(String task_id, BaseObserver<RoomSingleton> observer) {
sApiServer.dailyTasksComplete(task_id).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
@@ -494,6 +529,7 @@ public class RetrofitClient {
}
});
}
public void sendHeadine(String content, String money, String roomId, BaseObserver<String> observer) {
sApiServer.sendHeadine(content, money, roomId).enqueue(new Callback<BaseModel<String>>() {
@Override
@@ -543,7 +579,7 @@ public class RetrofitClient {
if (baseModel != null) {
if (baseModel.getData() != null && baseModel.getData().size() > 0) {
observer.onNext(baseModel.getData());
}else {
} else {
observer.onNext(new ArrayList<>());
}
}
@@ -718,16 +754,16 @@ public class RetrofitClient {
});
}
public void getThemeData(BaseObserver<ThemeBean> observer){
public void getThemeData(BaseObserver<ThemeBean> observer) {
sApiServer.getThemeData().enqueue(new Callback<BaseModel<ThemeBean>>() {
@Override
public void onResponse(Call<BaseModel<ThemeBean>> call, Response<BaseModel<ThemeBean>> response) {
if (response.code() == 200){
if (response.code() == 200) {
BaseModel<ThemeBean> baseModel = response.body();
if (baseModel.getData()!=null){
if (baseModel.getData() != null) {
observer.onNext(baseModel.getData());
}else {
} else {
observer.onNext(null);
}
}
@@ -760,7 +796,7 @@ public class RetrofitClient {
sApiServer.getBlacklist(page, page_limit).compose(new DefaultTransformer<>()).subscribe(observer);
}
public void getLockMiniList(String page, String page_limit, BaseObserver<List<BlackUserBean>> observer){
public void getLockMiniList(String page, String page_limit, BaseObserver<List<BlackUserBean>> observer) {
sApiServer.getLockMiniList(page, page_limit).compose(new DefaultTransformer<>()).subscribe(observer);
}
@@ -770,7 +806,7 @@ public class RetrofitClient {
//获取扩列数据
public void getExpandColumn(String type, String page, String page_limit, BaseObserver<List<ExpandColumnBean>> observer) {
sApiServer.getExpandColumn(type).compose(new DefaultTransformer<>()).subscribe(observer);
sApiServer.getExpandColumn(type,page, page_limit).compose(new DefaultTransformer<>()).subscribe(observer);
}
//获取官方公告数据或者系统消息,
@@ -1074,7 +1110,7 @@ public class RetrofitClient {
}
public void getCharmList(String roomId, String stime, String etime, String p, BaseObserver<RoomDetails> observer) {
sApiServer.getCharmList(roomId, stime, etime).compose(new DefaultTransformer<>()).subscribe(observer);
sApiServer.getCharmList(roomId, stime, etime, p).compose(new DefaultTransformer<>()).subscribe(observer);
}
// TODO: 2025/6/28 房间竞拍开始
@@ -1644,9 +1680,9 @@ public class RetrofitClient {
sApiServer.cancelUserDecorate(type).enqueue(new Callback<BaseModel<String>>() {
@Override
public void onResponse(Call<BaseModel<String>> call, Response<BaseModel<String>> response) {
if (response.code() == 200){
if (response.code() == 200) {
observer.onNext("取消成功");
}
}
}
@Override
@@ -1913,12 +1949,12 @@ public class RetrofitClient {
});
}
public void postRoomSwToken(String roomId, BaseObserver<PkSwTokenBean> observer){
public void postRoomSwToken(String roomId, BaseObserver<PkSwTokenBean> observer) {
sApiServer.postRoomSwToken(roomId).enqueue(new Callback<BaseModel<PkSwTokenBean>>() {
@Override
public void onResponse(Call<BaseModel<PkSwTokenBean>> call, Response<BaseModel<PkSwTokenBean>> response) {
if (response.code() == 200){
if (response.code() == 200) {
BaseModel<PkSwTokenBean> baseModel = response.body();
observer.onNext(baseModel.getData());
}

View File

@@ -1,5 +1,9 @@
package com.qxcm.moduleutil.listener;
import static com.liulishuo.okdownload.OkDownloadProvider.context;
import android.app.ActivityManager;
import android.content.Context;
import android.util.Log;
import com.blankj.utilcode.util.GsonUtils;
@@ -27,6 +31,8 @@ import org.greenrobot.eventbus.EventBus;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
/**
*@author qx

View File

@@ -103,8 +103,8 @@ public class AgoraManager {
private static List<Music> musicList;
private static boolean isBjMusic = false;
private static List<SoundLevelUpdateListener> soundLevelUpdateListeners = new CopyOnWriteArrayList<>();
private static ChannelMediaOptions options;
private static RtcConnection connection;
private static ChannelMediaOptions options;
private static RtcConnection connection;
private String pkRoomId = "";
private AgoraManager() {
@@ -148,7 +148,8 @@ public class AgoraManager {
* 初始化 Agora 引擎
*/
public static void init(String appId) {
if (rtcEngine != null) throw new IllegalStateException("RtcEngine已经初始化过");;
if (rtcEngine != null) throw new IllegalStateException("RtcEngine已经初始化过");
;
try {
RtcEngineConfig config = new RtcEngineConfig();
@@ -212,7 +213,7 @@ public class AgoraManager {
public void joinRoom(String token, String roomId, int uid, boolean isMicrophoneEnabled, boolean isCamerJs) {
if (rtcEngine == null) {
Log.e("AgoraManager", "RtcEngine not initialized");
return;
init(CommonAppContext.getInstance().getCurrentEnvironment().getSwSdkAppId());
}
if (!mRoomId.isEmpty()) {
leaveRoom();
@@ -256,6 +257,9 @@ public class AgoraManager {
}
public void joinChannelEx(String token, String channelId, int uid) {
if (rtcEngine == null) {
init(CommonAppContext.getInstance().getCurrentEnvironment().getSwSdkAppId());
}
if (rtcEngine != null) {
options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
@@ -346,21 +350,69 @@ public class AgoraManager {
* 销毁引擎
*/
public void destroy() {
// if (rtcEngine != null) {
//// rtcEngine.registerEventHandler(getDefaultEventHandler());
// rtcEngine.leaveChannel();
// rtcEngine.stopScreenCapture();
// rtcEngine.stopPreview();
// if (musicPlayer != null) {
// musicPlayer.destroy();
// }
// if (musicContentCenter != null) {
// musicContentCenter.destroy();
// }
//// rtcEngine.destroy();
//// rtcEngine = null;
// }
// eventHandlers.clear();
if (rtcEngine != null) {
// rtcEngine.registerEventHandler(getDefaultEventHandler());
rtcEngine.leaveChannel();
// 停止屏幕捕获
rtcEngine.stopScreenCapture();
// 停止预览
rtcEngine.stopPreview();
// 离开频道
rtcEngine.leaveChannel();
// 销毁音乐播放器
if (musicPlayer != null) {
musicPlayer.stop();
musicPlayer.destroy();
musicPlayer = null;
}
// 销毁音乐内容中心
if (musicContentCenter != null) {
musicContentCenter.destroy();
musicContentCenter = null;
}
// rtcEngine.destroy();
// 移除所有事件监听器
for (IRtcEngineEventHandler handler : eventHandlers) {
rtcEngine.removeHandler(handler);
}
eventHandlers.clear();
// // 销毁RtcEngine
// RtcEngine.destroy();
// rtcEngine = null;
}
eventHandlers.clear();
// 清理静态变量
musicList = null;
soundLevelUpdateListeners.clear();
mRoomId = "";
mSongCode = 0;
options = null;
connection = null;
pkRoomId = "";
isBjMusic = false;
// 清理实例
instance = null;
}
public void desMusic() {
@@ -374,6 +426,9 @@ public class AgoraManager {
*/
@SuppressLint("SuspiciousIndentation")
public void setLocalAudioEnabled(boolean enabled, String userId) {
if (rtcEngine == null) {
init(CommonAppContext.getInstance().getCurrentEnvironment().getSwSdkAppId());
}
if (rtcEngine != null) {
rtcEngine.enableLocalAudio(enabled); // 启用/禁用音频采集
this.isLocalAudioEnabled = enabled;
@@ -425,6 +480,9 @@ public class AgoraManager {
// RtcConnection connection = new RtcConnection();
// connection.channelId = mRoomId;
// connection.localUid = uid;
if (rtcEngine == null) {
init(CommonAppContext.getInstance().getCurrentEnvironment().getSwSdkAppId());
}
rtcEngine.muteLocalAudioStreamEx(enabled, connection);
}

View File

@@ -0,0 +1,87 @@
package com.qxcm.moduleutil.utils;
import android.content.Context;
import com.blankj.utilcode.util.LogUtils;
import com.bumptech.glide.Glide;
public class MemoryOptimizationUtils {
private static final String TAG = "MemoryOptimization";
/**
* 检查内存状态
*/
public static boolean isMemoryLow() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
LogUtils.d(TAG, "Memory usage: " + (memoryUsage * 100) + "%");
// 内存使用超过85%认为是低内存
return memoryUsage > 0.85;
}
/**
* 强制进行垃圾回收
*/
public static void forceGC() {
try {
LogUtils.d(TAG, "Forcing garbage collection");
Runtime.getRuntime().gc();
Thread.sleep(100); // 给GC一些时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 清理图片缓存
*/
public static void clearImageCache(Context context) {
try {
// 清理Glide缓存
Glide.get(context).clearMemory();
// 在后台线程清理磁盘缓存
new Thread(() -> {
try {
Glide.get(context).clearDiskCache();
} catch (Exception e) {
LogUtils.e(TAG, "Error clearing Glide disk cache: " + e.getMessage());
}
}).start();
} catch (Exception e) {
LogUtils.e(TAG, "Error clearing image cache: " + e.getMessage());
}
}
/**
* 获取当前内存使用情况
*/
public static String getMemoryInfo() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
return String.format("Max: %d MB, Total: %d MB, Used: %d MB, Free: %d MB",
maxMemory / (1024 * 1024),
totalMemory / (1024 * 1024),
usedMemory / (1024 * 1024),
freeMemory / (1024 * 1024));
}
/**
* 清理SVGA缓存
*/
public static void clearSVGACache() {
try {
// 如果SVGA库提供了清理缓存的方法调用它
// SVGAParser.clearCache(); // 假设有这样的方法
} catch (Exception e) {
LogUtils.e(TAG, "Error clearing SVGA cache: " + e.getMessage());
}
}
}

View File

@@ -2,7 +2,7 @@ package com.qxcm.moduleutil.utils.config;
public enum EnvironmentEnum {
PRODUCTION(//生产环境
"http://chat.qxmier.com/",
"https://chat.qxmier.com/",
"6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc=",
"https://oss-cn-hangzhou.aliyuncs.com/",
"LTAI5tJ2UYfFNF7K3F4e1siv",
@@ -15,7 +15,7 @@ public enum EnvironmentEnum {
"tcp://81.70.45.221",
"https://vespa.qxmier.com"),
TEST(//测试环境
"http://vschat.qxmier.com/",
"https://vschat.qxmier.com/",
"6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc=",
"https://oss-cn-hangzhou.aliyuncs.com/",
"LTAI5tJ2UYfFNF7K3F4e1siv",

View File

@@ -16,14 +16,100 @@ import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import com.blankj.utilcode.util.ToastUtils;
import com.qxcm.moduleutil.base.CommonAppContext;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;
public class SystemLocationProvider implements LocationProvider {
private LocationManager locationManager;
private LocationListener gpsLocationListener;
private LocationListener networkLocationListener;
private Handler timeoutHandler;
private Runnable timeoutRunnable;
// 使用静态内部类避免内存泄漏
private static class LocationListenerImpl implements LocationListener {
private final WeakReference<LocationCallback> callbackRef;
private final WeakReference<Context> contextRef;
private final boolean isGpsListener; // 标识是否是GPS监听器
LocationListenerImpl(LocationCallback callback, Context context, boolean isGpsListener) {
this.callbackRef = new WeakReference<>(callback);
this.contextRef = new WeakReference<>(context);
this.isGpsListener = isGpsListener;
}
@Override
public void onLocationChanged(@NonNull Location location) {
LocationCallback callback = callbackRef.get();
Context context = contextRef.get();
if (callback != null && context != null) {
String city = getCityName(context, location.getLatitude(), location.getLongitude());
callback.onLocationReceived(location.getLatitude(), location.getLongitude(), city);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// 可以添加日志或其他处理
Log.d("LocationDebug", "onStatusChanged: " + provider + ", status: " + status);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
Log.d("LocationDebug", "onProviderEnabled: " + provider);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
LocationCallback callback = callbackRef.get();
Context context = contextRef.get();
if (callback != null && context != null) {
if (isGpsListener) {
// GPS被禁用时回退到网络定位
fallbackToNetwork(callback, context);
} else {
callback.onFailed("网络定位不可用");
}
}
}
private String getCityName(Context context, double latitude, double longitude) {
android.location.Geocoder geocoder = new android.location.Geocoder(context, Locale.getDefault());
try {
List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);
if (addresses != null && !addresses.isEmpty()) {
return addresses.get(0).getAdminArea() + "," + addresses.get(0).getLocality() + "," + addresses.get(0).getSubLocality();
}
} catch (IOException e) {
e.printStackTrace();
}
return "未知城市";
}
private void fallbackToNetwork(LocationCallback callback, Context context) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null && locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
callback.onFailed("缺少定位权限");
return;
}
// 创建网络定位监听器
LocationListenerImpl networkListener = new LocationListenerImpl(callback, context, false);
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, networkListener, Looper.getMainLooper());
} else {
callback.onFailed("GPS 不可用且网络定位也未启用");
}
}
}
@Override
public void getLastKnownLocation(@NonNull Context context, @NonNull LocationCallback callback) {
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (locationManager == null) {
@@ -40,153 +126,40 @@ public class SystemLocationProvider implements LocationProvider {
Log.d("LocationDebug", "GPS Enabled: " + isGpsEnabled);
Log.d("LocationDebug", "Network Enabled: " + isNetworkEnabled);
if (!isGpsEnabled && !isNetworkEnabled) {
callback.onFailed("GPS 和 网络定位均未启用");
return;
}
// 清理之前的监听器
cleanupListeners();
final Handler handler = new Handler(Looper.getMainLooper());
Runnable timeout = () -> callback.onFailed("定位请求超时");
handler.postDelayed(timeout, 15000); // 15秒超时
LocationListener listener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
handler.removeCallbacks(timeout);
String city = getCityName(context, location.getLatitude(), location.getLongitude());
callback.onLocationReceived(location.getLatitude(), location.getLongitude(), city);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d("LocationDebug", "onStatusChanged: " + provider + ", status: " + status);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
Log.d("LocationDebug", "onProviderEnabled: " + provider);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
Log.d("LocationDebug", "onProviderDisabled: " + provider);
fallbackToNetwork(callback, context);
}
// 设置超时处理
timeoutHandler = new Handler(Looper.getMainLooper());
timeoutRunnable = () -> {
cleanupListeners();
callback.onFailed("定位请求超时");
};
if (isNetworkEnabled) {
Log.d("LocationDebug", "尝试使用 NETWORK 定位");
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, listener, Looper.getMainLooper());
}
else if (isGpsEnabled) {
timeoutHandler.postDelayed(timeoutRunnable, 15000); // 15秒超时
if (isGpsEnabled) {
Log.d("LocationDebug", "尝试使用 GPS 定位");
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, listener, Looper.getMainLooper());
gpsLocationListener = new LocationListenerImpl(callback, context, true);
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, gpsLocationListener, Looper.getMainLooper());
} else if (isNetworkEnabled) {
Log.d("LocationDebug", "尝试使用 NETWORK 定位");
networkLocationListener = new LocationListenerImpl(callback, context, false);
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, networkLocationListener, Looper.getMainLooper());
} else {
// 清理超时处理
if (timeoutHandler != null && timeoutRunnable != null) {
timeoutHandler.removeCallbacks(timeoutRunnable);
}
callback.onFailed("GPS 和 网络定位均未启用");
}
} catch (SecurityException e) {
Log.e("LocationDebug", "缺少定位权限", e);
callback.onFailed("缺少定位权限:" + e.getMessage());
}
}
// @Override
// public void getLastKnownLocation(@NonNull Context context, @NonNull LocationCallback callback) {
// locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// if (locationManager == null) {
// callback.onFailed("无法获取定位服务");
// return;
// }
//
// try {
// if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
// locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, new LocationListener() {
// @Override
// public void onLocationChanged(@NonNull Location location) {
// String city = getCityName(context, location.getLatitude(), location.getLongitude());
// callback.onLocationReceived(location.getLatitude(), location.getLongitude(), city);
// }
//
// @Override
// public void onStatusChanged(String provider, int status, Bundle extras) {
// ToastUtils.showShort(provider);
// }
//
// @Override
// public void onProviderEnabled(@NonNull String provider) {
// ToastUtils.showShort(provider);
// }
//
// @Override
// public void onProviderDisabled(@NonNull String provider) {
// fallbackToNetwork(callback, context);
// }
// }, Looper.getMainLooper());
// } else if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
// locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, new LocationListener() {
// @Override
// public void onLocationChanged(@NonNull Location location) {
// String city = getCityName(context, location.getLatitude(), location.getLongitude());
// callback.onLocationReceived(location.getLatitude(), location.getLongitude(), city);
// }
//
// @Override
// public void onStatusChanged(String provider, int status, Bundle extras) {
// ToastUtils.showShort(provider);
// }
//
// @Override
// public void onProviderEnabled(@NonNull String provider) {
// ToastUtils.showShort(provider);
// }
//
// @Override
// public void onProviderDisabled(@NonNull String provider) {
// callback.onFailed("网络定位不可用");
// }
// }, Looper.getMainLooper());
// } else {
// callback.onFailed("GPS 和 网络定位均未启用");
// }
// } catch (SecurityException e) {
// callback.onFailed("缺少定位权限:" + e.getMessage());
// }
// }
private void fallbackToNetwork(LocationCallback callback, Context context) {
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
// 清理超时处理
if (timeoutHandler != null && timeoutRunnable != null) {
timeoutHandler.removeCallbacks(timeoutRunnable);
}
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
String city = getCityName(context, location.getLatitude(), location.getLongitude());
callback.onLocationReceived(location.getLatitude(), location.getLongitude(), city);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
ToastUtils.showShort(provider);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
ToastUtils.showShort(provider);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
callback.onFailed("网络定位不可用");
}
}, Looper.getMainLooper());
} else {
callback.onFailed("GPS 不可用且网络定位也未启用");
callback.onFailed("缺少定位权限:" + e.getMessage());
}
}
@@ -197,15 +170,47 @@ public class SystemLocationProvider implements LocationProvider {
@Override
public void stopLocationUpdates() {
// 可扩展为停止监听
cleanupListeners();
}
private void cleanupListeners() {
try {
if (locationManager != null) {
if (gpsLocationListener != null) {
if (ActivityCompat.checkSelfPermission(CommonAppContext.getInstance(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(CommonAppContext.getInstance(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager.removeUpdates(gpsLocationListener);
}
gpsLocationListener = null;
}
if (networkLocationListener != null) {
if (ActivityCompat.checkSelfPermission(CommonAppContext.getInstance(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(CommonAppContext.getInstance(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager.removeUpdates(networkLocationListener);
}
networkLocationListener = null;
}
}
// 清理超时处理
if (timeoutHandler != null && timeoutRunnable != null) {
timeoutHandler.removeCallbacks(timeoutRunnable);
timeoutHandler = null;
timeoutRunnable = null;
}
} catch (Exception e) {
Log.e("LocationDebug", "清理监听器时出错", e);
}
}
// 保持原有的工具方法
private String getCityName(Context context, double latitude, double longitude) {
android.location.Geocoder geocoder = new android.location.Geocoder(context, Locale.getDefault());
try {
List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);
if (addresses != null && !addresses.isEmpty()) {
return addresses.get(0).getAdminArea() + "," + addresses.get(0).getLocality()+ "," + addresses.get(0).getSubLocality();
return addresses.get(0).getAdminArea() + "," + addresses.get(0).getLocality() + "," + addresses.get(0).getSubLocality();
}
} catch (IOException e) {
e.printStackTrace();
@@ -213,7 +218,6 @@ public class SystemLocationProvider implements LocationProvider {
return "未知城市";
}
public void useNetworkLocation(Context context, LocationCallback callback) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (locationManager == null) {
@@ -228,31 +232,15 @@ public class SystemLocationProvider implements LocationProvider {
}
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
String city = getCityName(context, location.getLatitude(), location.getLongitude());
callback.onLocationReceived(location.getLatitude(), location.getLongitude(), city);
}
// 清理之前的网络监听器
if (networkLocationListener != null) {
locationManager.removeUpdates(networkLocationListener);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
ToastUtils.showShort(provider);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
ToastUtils.showShort(provider);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
callback.onFailed("网络定位不可用");
}
}, Looper.getMainLooper());
networkLocationListener = new LocationListenerImpl(callback, context, false);
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, networkLocationListener, Looper.getMainLooper());
} else {
callback.onFailed("网络定位未启用");
}
}
}

View File

@@ -10,6 +10,7 @@ import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
@@ -19,6 +20,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.PathUtils;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
@@ -39,6 +41,7 @@ import com.qxcm.moduleutil.R;
import com.qxcm.moduleutil.databinding.RoomViewSvgaAnimBinding;
import com.qxcm.moduleutil.utils.DownloadUtil;
import com.qxcm.moduleutil.utils.Md5Utils;
import com.qxcm.moduleutil.utils.MemoryOptimizationUtils;
import com.qxcm.moduleutil.utils.SpUtil;
import com.qxcm.moduleutil.utils.logger.Logger;
import com.tencent.qgame.animplayer.AnimConfig;
@@ -46,13 +49,15 @@ import com.tencent.qgame.animplayer.inter.IAnimListener;
import com.tencent.qgame.animplayer.inter.IFetchResource;
import java.io.File;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class AvatarFrameView extends FrameLayout implements IAnimListener {
@Override
public void onFailed(int i, @Nullable String s) {
@@ -75,15 +80,37 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
@Override
public void onVideoComplete() {
// if (mType == 1) {
// mBinding.playView.startPlay(mFile); // 循环播放
// } else {
// isPlaying = false;
// playNextFromQueue(); // 播放下一项
// }
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() == Looper.getMainLooper()) {
handleVideoComplete();
} else {
mainHandler.post(() -> {
if (!isDestroyed) {
handleVideoComplete();
}
});
}
}
private void handleVideoComplete() {
if (isDestroyed) return;
if (mType == 1) {
mBinding.playView.startPlay(mFile); // 循环播放
if (mBinding != null && mFile != null) {
mBinding.playView.startPlay(mFile); // 循环播放
}
} else {
isPlaying = false;
playNextFromQueue(); // 播放下一项
}
}
@Override
public void onVideoDestroy() {
@@ -101,11 +128,19 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
private int mType;//1:循环播放 2:播放一次停止播放
private File mFile;
private final Queue<PlayItem> playQueue = new LinkedList<>();
private boolean isPlaying = false;
// 添加销毁标记
private boolean isDestroyed = false;
private static final String TAG = "AvatarFrameView";
private RoomViewSvgaAnimBinding mBinding;
// 内存监控
private static final long MAX_MEMORY_THRESHOLD = 300 * 1024 * 1024; // 300MB
private static final int MAX_SVGA_CACHE_SIZE = 3;
private final Map<String, WeakReference<SVGAVideoEntity>> svgaCache = new LruCache<>(MAX_SVGA_CACHE_SIZE);
public AvatarFrameView(@NonNull Context context) {
this(context, null);
}
@@ -121,6 +156,7 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
private void initViews() {
if (isDestroyed) return;
// 初始化 ExoPlayer View
playerView = new PlayerView(getContext());
playerView.setUseController(false);
@@ -142,10 +178,18 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
// addView(glSurfaceView);
// 初始化 ExoPlayer
exoPlayer = new ExoPlayer.Builder(getContext()).build();
playerView.setPlayer(exoPlayer);
if (!isDestroyed) {
try {
exoPlayer = new ExoPlayer.Builder(getContext()).build();
playerView.setPlayer(exoPlayer);
} catch (Exception e) {
LogUtils.e("AvatarFrameView", "Failed to initialize ExoPlayer: " + e.getMessage());
}
}
mBinding.playView.setAnimListener(this);
if (mBinding != null) {
mBinding.playView.setAnimListener(this);
}
}
private String getFileExtension(String url) {
if (url == null || url.isEmpty()) return "";
@@ -156,7 +200,18 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
return "";
}
private void playNextFromQueue() {
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(this::playNextFromQueue);
return;
}
// 再次检查内存状态
if (isMemoryLow()) {
LogUtils.w(TAG, "Low memory, clearing queue");
clearQueue();
return;
}
// 检查特效是否开启
if (SpUtil.getOpenEffect() != 1) {
clearQueue();
@@ -202,6 +257,18 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
}
public void setSource(String url, int type2) {
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> setSource(url, type2));
return;
}
// 检查内存状态
if (isMemoryLow()) {
LogUtils.w(TAG, "Low memory, skipping animation");
clearQueue();
return;
}
// 检查特效是否开启
if (SpUtil.getOpenEffect() != 1) {
// 特效关闭时清空队列并停止播放
@@ -219,7 +286,15 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
playNextFromQueue();
}
}
private boolean isMemoryLow() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
// 内存使用超过80%或绝对使用量超过阈值
return memoryUsage > 0.8 || usedMemory > MAX_MEMORY_THRESHOLD;
}
// public void setSource(String url, int type2) {
// if (SpUtil.getOpenEffect()==1) {
@@ -323,7 +398,157 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
}
private void handleSVGAComplete(SVGAVideoEntity videoItem, String url) {
if (isDestroyed || svgaSurface == null) return;
try {
// 缓存实体(使用弱引用)
svgaCache.put(url, new WeakReference<>(videoItem));
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
svgaSurface.setImageDrawable(drawable);
svgaSurface.setCallback(new SVGACallback() {
@Override
public void onStep(int i, double v) {
}
@Override
public void onRepeat() {
}
@Override
public void onPause() {
}
@Override
public void onFinished() {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
isPlaying = false;
playNextFromQueue();
});
} else {
isPlaying = false;
playNextFromQueue();
}
}
});
svgaSurface.startAnimation();
} catch (Exception e) {
LogUtils.e(TAG, "Error handling SVGA completion: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
}
private void playCachedSVGA(SVGAVideoEntity videoItem) {
if (isDestroyed || svgaSurface == null) return;
try {
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
svgaSurface.setImageDrawable(drawable);
svgaSurface.setCallback(new SVGACallback() {
@Override
public void onStep(int i, double v) {
}
@Override
public void onRepeat() {
}
@Override
public void onPause() {
}
@Override
public void onFinished() {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
isPlaying = false;
playNextFromQueue();
});
} else {
isPlaying = false;
playNextFromQueue();
}
}
});
svgaSurface.startAnimation();
} catch (Exception e) {
LogUtils.e(TAG, "Error playing cached SVGA: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
}
private void loadNewSVGA(String url) {
if (isDestroyed) return;
try {
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity videoItem) {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> handleSVGAComplete(videoItem, url));
} else {
handleSVGAComplete(videoItem, url);
}
}
@Override
public void onError() {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
isPlaying = false;
playNextFromQueue();
});
} else {
isPlaying = false;
playNextFromQueue();
}
}
});
} catch (Exception e) {
LogUtils.e(TAG, "Error parsing SVGA: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
}
private void loadSVGA(String url) {
if (isDestroyed || svgaSurface == null) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> loadSVGA(url));
return;
}
try {
svgaSurface.setVisibility(View.VISIBLE);
// 检查缓存
WeakReference<SVGAVideoEntity> cachedRef = svgaCache.get(url);
SVGAVideoEntity cachedEntity = cachedRef != null ? cachedRef.get() : null;
if (cachedEntity != null) {
// 使用缓存的实体
playCachedSVGA(cachedEntity);
} else {
// 加载新的SVGA
loadNewSVGA(url);
}
} catch (Exception e) {
LogUtils.e(TAG, "Error loading SVGA: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
svgaSurface.setVisibility(View.VISIBLE);
try {
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
@@ -406,38 +631,201 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
private void clearPrevious() {
// if (exoPlayer != null) {
// exoPlayer.stop();
// exoPlayer.clearVideoSurface();
// }
if (svgaSurface.getDrawable() instanceof SVGADrawable) {
((SVGADrawable) svgaSurface.getDrawable()).stop();
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
if (!isDestroyed) {
clearPrevious();
}
});
return;
}
try {
// 停止并清理 ExoPlayer
if (exoPlayer != null) {
try {
exoPlayer.stop(); // 这里可能会在错误线程中调用
exoPlayer.clearVideoSurface();
} catch (Exception e) {
Logger.e("Error stopping ExoPlayer: " + e.getMessage());
// 如果在错误线程中,切换到主线程重试
mainHandler.post(() -> {
try {
if (exoPlayer != null) {
exoPlayer.stop();
exoPlayer.clearVideoSurface();
}
} catch (Exception ex) {
Logger.e("Error stopping ExoPlayer on main thread: " + ex.getMessage());
}
});
}
}
// 停止并清理 SVGA 动画
if (svgaSurface != null && svgaSurface.getDrawable() instanceof SVGADrawable) {
SVGADrawable drawable = (SVGADrawable) svgaSurface.getDrawable();
if (drawable != null) {
drawable.stop();
// 清理 SVGADrawable 中的资源
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
}
}
// 隐藏所有视图
if (playerView != null) playerView.setVisibility(View.GONE);
if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
mBinding.playView.setVisibility(View.GONE);
// 停止播放器
if (mBinding.playView != null) {
mBinding.playView.stopPlay();
}
} catch (Exception e) {
LogUtils.e(TAG, "Error in clearPrevious: " + e.getMessage());
}
// if (svgaSurface.getDrawable() instanceof SVGADrawable) {
// ((SVGADrawable) svgaSurface.getDrawable()).stop();
// }
// if (playerView != null) playerView.setVisibility(View.GONE);
// if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
// if (glSurfaceView != null) glSurfaceView.setVisibility(View.GONE);
// mBinding.playView.setVisibility(View.GONE);
}
// 简单的 LRU Cache 实现
private static class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public void release() {
// if (exoPlayer != null) {
// exoPlayer.release();
// }
if (svgaSurface.getDrawable() instanceof SVGADrawable) {
((SVGADrawable) svgaSurface.getDrawable()).stop();
public LruCache(int maxSize) {
super(16, 0.75f, true);
this.maxSize = maxSize;
}
if (glSurfaceView != null) {
glSurfaceView.onPause(); // 必须调用生命周期方法
// glSurfaceView.requestRender();
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
/**
* 释放所有资源
*/
private void releaseResources() {
LogUtils.d(TAG, "Releasing all resources");
if (isDestroyed) return;
try {
// 清理 SVGA 资源
if (svgaSurface != null && svgaSurface.getDrawable() instanceof SVGADrawable) {
SVGADrawable drawable = (SVGADrawable) svgaSurface.getDrawable();
if (drawable != null) {
try {
drawable.stop();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing SVGA resources: " + e.getMessage());
}
}
}
// 停止并清理播放器
if (mBinding != null && mBinding.playView != null) {
mBinding.playView.stopPlay();
}
// 清理 ExoPlayer 资源
if (exoPlayer != null) {
try {
exoPlayer.stop();
exoPlayer.clearVideoSurface();
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing ExoPlayer resources: " + e.getMessage());
}
}
} catch (Exception e) {
LogUtils.e(TAG, "Error in releaseResources: " + e.getMessage());
}
}
/**
* 公共释放方法,用于外部主动释放资源
*/
public void release() {
Logger.d("AvatarFrameView", "Public release called");
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(this::release);
return;
}
isDestroyed = true;
try {
// 清空播放队列
clearQueue();
// 释放所有资源
releaseResources();
// 释放 ExoPlayer
if (exoPlayer != null) {
try {
exoPlayer.stop();
exoPlayer.release();
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing ExoPlayer: " + e.getMessage());
}
exoPlayer = null;
}
// 清理 PlayerView
if (playerView != null) {
try {
playerView.setPlayer(null);
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing PlayerView: " + e.getMessage());
}
playerView = null;
}
// 清理 SVGAImageView
if (svgaSurface != null) {
try {
svgaSurface.pauseAnimation();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing SVGAImageView: " + e.getMessage());
}
svgaSurface = null;
}
// 清理 binding
if (mBinding != null) {
mBinding = null;
}
// 清理队列
playQueue.clear();
} catch (Exception e) {
LogUtils.e(TAG, "Error in AvatarFrameView release: " + e.getMessage());
} finally {
// 建议进行垃圾回收
MemoryOptimizationUtils.forceGC();
}
}
public void clearQueue() {
if (isDestroyed) return;
playQueue.clear();
isPlaying = false;
// 清理当前正在播放的内容
clearPrevious();
}
@@ -454,8 +842,13 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
* 关闭特效
*/
public void closeEffect() {
// 清空队列
clearQueue();
// 释放资源
releaseResources();
//清空队列
playQueue.clear();
// playQueue.clear();
//关闭动画
// if (mBinding.image != null && isPlaying && mBinding.image.isAnimating()) {
// mBinding.image.setAnimation(null);

View File

@@ -285,6 +285,8 @@ public abstract class BaseWheatView extends ConstraintLayout implements IBaseWhe
if (pitBean != null && pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0")) {
if (pitBean.getUser_id().equals(userId + "")) {
iv_on_line.setVisibility(GONE);
mIvRipple.stopAnimation();
mIvRipple.setVisibility(GONE);
}
}
}
@@ -297,12 +299,16 @@ public abstract class BaseWheatView extends ConstraintLayout implements IBaseWhe
}
}else if (pitBean.getUser_id()==null || pitBean.getUser_id().equals("0") || pitBean.getUser_id().equals("")){
iv_on_line.setVisibility(GONE);
mIvRipple.stopAnimation();
mIvRipple.setVisibility(GONE);
}
}
});
if (pitBean.getUser_id()==null || pitBean.getUser_id().equals("0") || pitBean.getUser_id().equals("") ){
iv_on_line.setVisibility(GONE);
mIvRipple.stopAnimation();
mIvRipple.setVisibility(GONE);
}
}
@@ -361,6 +367,8 @@ public abstract class BaseWheatView extends ConstraintLayout implements IBaseWhe
if (pitBean.getUser_id().equals(isOnline.getUser_id())) {
if (isOnline.getIs_online() == 1) {
iv_on_line.setVisibility(GONE);
mIvRipple.stopAnimation();
mIvRipple.setVisibility(GONE);
} else {
iv_on_line.setVisibility(VISIBLE);
}

View File

@@ -1,122 +0,0 @@
package com.qxcm.moduleutil.widget
import android.graphics.SurfaceTexture
import android.opengl.GLES20
import android.opengl.Matrix
class ChannelSplitRenderer {
private val vertexShaderCode = """
attribute vec4 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = aPosition;
vTexCoord = aTexCoord;
}
"""
private val fragmentShaderCode = """
precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTexCoord;
void main() {
// 只使用左半部分作为最终颜色
vec2 leftCoord = vec2(vTexCoord.x * 0.5, vTexCoord.y);
vec4 color = texture2D(uTexture, leftCoord);
// 设置 alpha 为 1.0 表示完全不透明,或根据需求设为 0.0 表示全透明
gl_FragColor = vec4(color.rgb, 0.0); // 左通道颜色 + 不透明
}"""
private var program = 0
private var positionHandle = 0
private var texCoordHandle = 0
private var textureHandle = 0
private val projectionMatrix = FloatArray(16)
private val modelMatrix = FloatArray(16)
fun onSurfaceCreated(surface: SurfaceTexture, width: Int, height: Int) {
// 初始化着色器
val vertexShader = ShaderUtils.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader = ShaderUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
program = GLES20.glCreateProgram().also {
GLES20.glAttachShader(it, vertexShader)
GLES20.glAttachShader(it, fragmentShader)
GLES20.glLinkProgram(it)
}
positionHandle = GLES20.glGetAttribLocation(program, "aPosition")
texCoordHandle = GLES20.glGetAttribLocation(program, "aTexCoord")
textureHandle = GLES20.glGetUniformLocation(program, "uTexture")
// 初始化矩阵
Matrix.setIdentityM(projectionMatrix, 0)
Matrix.setIdentityM(modelMatrix, 0)
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
GLES20.glEnable(GLES20.GL_BLEND)
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)
}
fun onSurfaceChanged(width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
}
fun onDrawFrame(textureId: Int) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
GLES20.glUseProgram(program)
// 定义顶点坐标(全屏)
val vertices = floatArrayOf(
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f
)
// 修改纹理坐标,只映射左半部分视频内容到左侧屏幕
val texCoords = floatArrayOf(
0.0f, 1.0f, // 左下角
0.5f, 1.0f, // 右下角(对应视频中间)
0.0f, 0.0f, // 左上角
0.5f, 0.0f // 右上角
)
val vertexBuffer = ShaderUtils.createFloatBuffer(vertices)
val texBuffer = ShaderUtils.createFloatBuffer(texCoords)
GLES20.glEnableVertexAttribArray(positionHandle)
GLES20.glVertexAttribPointer(
positionHandle, 3,
GLES20.GL_FLOAT, false,
0, vertexBuffer
)
GLES20.glEnableVertexAttribArray(texCoordHandle)
GLES20.glVertexAttribPointer(
texCoordHandle, 2,
GLES20.GL_FLOAT, false,
0, texBuffer
)
// 绑定纹理
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
GLES20.glUniform1i(textureHandle, 0)
// 绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
// 清理
GLES20.glDisableVertexAttribArray(positionHandle)
GLES20.glDisableVertexAttribArray(texCoordHandle)
}
fun release() {
GLES20.glDeleteProgram(program)
}
}

View File

@@ -370,6 +370,8 @@ public class Constants {
public static final String dailyTasksComplete = "/api/Dailytasks/dailyTasksComplete";//领取每日任务奖励
public static final String POST_CANCEL_USER_DECORATE = "/api/Decorate/cancel_user_decorate";//取消装扮
public static final String GET_THEME_DATA = "/api/Theme/get_theme_data";//主题接口
public static final String MODIFY_MOBILE = "/api/UserData/modify_mobile";//手机换绑
public static final String BIND_MOBILE = "/api/UserData/bind_mobile";//手机绑定

View File

@@ -9,9 +9,11 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.blankj.utilcode.util.LogUtils;
import com.petterp.floatingx.FloatingX;
import com.petterp.floatingx.assist.FxGravity;
import com.petterp.floatingx.assist.helper.FxAppHelper;
@@ -23,6 +25,9 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* @Author
* @Time 2025/7/18 21:52
@@ -33,6 +38,8 @@ public class PiaoPingManager {
private WindowManager windowManager;
private View piaoPingView;
private boolean isPiaoPingShown = false;
private Queue<MqttBean> messageQueue = new ConcurrentLinkedQueue<>(); // 消息队列
private boolean isAnimating = false; // 动画状态标记
private PiaoPingManager(Context context) {
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -52,11 +59,64 @@ public class PiaoPingManager {
public void showPiaoPingMessage(MqttBean mqttBean) {
// 创建 FloatingX 配置
// 添加到队列
messageQueue.offer(mqttBean);
// 如果当前没有动画正在进行,则开始处理
if (!isAnimating) {
processNextMessage();
}
// // 获取悬浮窗视图并设置内容
//// 替换第72行及后续对 layoutView 的直接访问
// View floatingView = LayoutInflater.from(context).inflate(R.layout.item_piaoping, new FrameLayout(context), false);
// TextView textView = floatingView.findViewById(R.id.tv_name);
// TextView textView2 = floatingView.findViewById(R.id.tv_to_name);
// textView2.setText("送给" + mqttBean.getList().getToUserName());
// textView.setText(mqttBean.getList().getFromUserName());
// ImageUtils.loadHeadCC(mqttBean.getList().getGift_picture(), floatingView.findViewById(R.id.iv_piaoping));
// TextView tv_time = floatingView.findViewById(R.id.tv_num);
// tv_time.setText("x" + mqttBean.getList().getNumber());
//
// // 3秒后执行左侧滑动关闭动画
// floatingView.postDelayed(() -> {
// // 实现左侧滑出动画
// ObjectAnimator animator = ObjectAnimator.ofFloat(floatingView, "translationX", 0f, -floatingView.getWidth());
// animator.setDuration(300); // 300ms 动画时间
// animator.addListener(new AnimatorListenerAdapter() {
// @Override
// public void onAnimationEnd(Animator animation) {
// // 动画结束后移除悬浮窗
// FloatingX.uninstallAll();
// }
// });
// animator.start();
// }, 3000);
//
// assert floatingView != null;
// FxAppHelper fxAppHelper = FxAppHelper.builder()
// .setContext(context)
// .setLayoutView(floatingView)
// .setGravity(FxGravity.LEFT_OR_TOP)
// .setY(100)
// .build();
//
// FloatingX.install(fxAppHelper).show();
}
private void processNextMessage() {
if (messageQueue.isEmpty()) {
isAnimating = false;
return;
}
isAnimating = true;
MqttBean mqttBean = messageQueue.poll();
displayMessage(mqttBean);
}
private void displayMessage(MqttBean mqttBean) {
// 获取悬浮窗视图并设置内容
// 替换第72行及后续对 layoutView 的直接访问
View floatingView = LayoutInflater.from(context).inflate(R.layout.item_piaoping, new FrameLayout(context), false);
View floatingView = LayoutInflater.from(context).inflate(R.layout.item_piaoping, new FrameLayout(context), false);
TextView textView = floatingView.findViewById(R.id.tv_name);
TextView textView2 = floatingView.findViewById(R.id.tv_to_name);
textView2.setText("送给" + mqttBean.getList().getToUserName());
@@ -65,38 +125,74 @@ public class PiaoPingManager {
TextView tv_time = floatingView.findViewById(R.id.tv_num);
tv_time.setText("x" + mqttBean.getList().getNumber());
// 3秒后执行左侧滑动关闭动画
floatingView.postDelayed(() -> {
// 实现左侧滑出动画
ObjectAnimator animator = ObjectAnimator.ofFloat(floatingView, "translationX", 0f, -floatingView.getWidth());
animator.setDuration(300); // 300ms 动画时间
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 动画结束后移除悬浮窗
FloatingX.uninstallAll();
}
});
animator.start();
}, 3000);
// 先将视图放置在屏幕右侧外部,避免闪现问题
floatingView.setTranslationX(10000); // 先放到屏幕外
assert floatingView != null;
FxAppHelper fxAppHelper = FxAppHelper.builder()
.setContext(context)
.setLayoutView(floatingView)
.setGravity(FxGravity.LEFT_OR_TOP)
.setGravity(FxGravity.RIGHT_OR_TOP)
.setX(0)
.setY(100)
.build();
FloatingX.install(fxAppHelper).show();
// 首先从右侧滑入到屏幕中央
floatingView.post(() -> {
// 确保初始位置在屏幕右侧外部
floatingView.setTranslationX(floatingView.getWidth());
// 第一阶段:从右到屏幕右侧边缘(缓慢进入)
ObjectAnimator animator1 = ObjectAnimator.ofFloat(floatingView, "translationX",
floatingView.getWidth(), 0f);
animator1.setDuration(1500); // 延长动画时间到1.5秒
animator1.setInterpolator(new DecelerateInterpolator(2.0f)); // 更平缓的减速效果
animator1.start();
// 第二阶段延迟1秒后从当前位置向左滑出
floatingView.postDelayed(() -> {
ObjectAnimator animator2 = ObjectAnimator.ofFloat(floatingView, "translationX",
0f, -floatingView.getWidth());
animator2.setDuration(1500); // 延长动画时间到1.5秒
animator2.setInterpolator(new DecelerateInterpolator(2.0f)); // 更平缓的减速效果
animator2.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 动画结束后移除悬浮窗
FloatingX.uninstallAll();
// 处理下一个消息
processNextMessage();
}
});
animator2.start();
}, 1000); // 停留1秒
});
}
public void subscribe() {
EventBus.getDefault().register(this);
try {
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
} catch (Exception e) {
// 处理重复注册或其他异常
LogUtils.e("PiaoPingManager subscribe error: " + e.getMessage());
}
}
public void unsubscribe() {
EventBus.getDefault().unregister(this);
try {
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
} catch (Exception e) {
// 处理取消注册异常
LogUtils.e("PiaoPingManager unsubscribe error: " + e.getMessage());
}
messageQueue.clear();
isAnimating = false; // 重置动画状态
}
@Subscribe(threadMode = ThreadMode.MAIN)

View File

@@ -73,7 +73,19 @@
app:layout_constraintRight_toRightOf="@+id/iv_gift_pic"
app:layout_constraintTop_toBottomOf="@+id/tv_gift_name" />
<TextView
android:id="@+id/integral"
android:layout_width="@dimen/dp_35"
android:layout_height="@dimen/dp_13"
android:layout_gravity="center_horizontal"
android:background="@mipmap/text_bj"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:gravity="center"
android:text="x30"
android:textColor="@color/color_FF333333"
android:textSize="@dimen/sp_10"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -3,12 +3,13 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl_item"
android:layout_width="@dimen/dp_106"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_66"
android:layout_marginTop="@dimen/dp_5"
android:layout_marginEnd="@dimen/dp_12"
android:layout_marginStart="@dimen/dp_12"
android:layout_gravity="center"
android:background="@drawable/bg_r10_white">
<ImageView
@@ -28,12 +29,13 @@
android:id="@+id/tv_gold_num"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_20"
android:layout_marginLeft="@dimen/dp_5"
android:layout_marginStart="@dimen/dp_5"
android:textColor="@color/color_353535"
android:textSize="@dimen/sp_18"
android:layout_marginEnd="@dimen/dp_10"
app:layout_constraintLeft_toRightOf="@+id/iv_gold"
app:layout_constraintTop_toTopOf="@+id/iv_gold"
tools:text="600" />
tools:text="60000" />
<TextView
android:id="@+id/tv_money"
@@ -41,8 +43,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/dp_10"
android:layout_marginStart="@dimen/dp_25"
android:layout_marginEnd="@dimen/dp_40"
android:textColor="@color/color_FF666666"
android:textSize="@dimen/sp_14"
app:layout_constraintBottom_toBottomOf="parent"

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>