添加礼物飘屏功能

This commit is contained in:
2025-07-19 14:24:04 +08:00
parent 7f0df85123
commit 86b32f65d4
23 changed files with 1036 additions and 577 deletions

View File

@@ -1,78 +1,52 @@
package com.qxcm.moduleutil.service;
import android.annotation.SuppressLint;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.TextView;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.blankj.utilcode.util.NetworkUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.orhanobut.logger.Logger;
import com.qxcm.moduleutil.base.CommonAppContext;
import com.qxcm.moduleutil.bean.UserBean;
import com.qxcm.moduleutil.event.BossMsgEvent;
import com.qxcm.moduleutil.event.QDZMqttEvent;
import com.qxcm.moduleutil.utils.GsonUtils;
import com.qxcm.moduleutil.utils.SpUtil;
import com.tencent.qcloud.tuicore.TUIConstants;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.greenrobot.eventbus.EventBus;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.qxcm.moduleutil.R;
import com.qxcm.moduleutil.event.MqttBean;
import com.qxcm.moduleutil.utils.ImageUtils;
public class EMqttService extends Service implements NetworkUtils.OnNetworkStatusChangedListener {
private final static String TAG = "EMQTT消息";
private static final int qos = 2;
private static final String HOST = "tcp://81.70.45.221";
@SuppressLint("StaticFieldLeak")
private static MqttAndroidClient mqttAndroidClient;
private MqttConnectOptions mMqttConnectOptions;
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
private static final String TOPIC_BOSS = "boss";
private static final String TOPIC_ROOM = "qx_room_topic";
private static final String TOPIC_QIANG_TANG_GUO = "red_envelope_single_room_real_time_data"; // 抢糖果
public static String sRoomId = "qx_room_topic";
public static String sUserId;
private BroadcastReceiver unreadCountReceiver;
public class EMqttService extends Service {
private WindowManager windowManager;
private View piaoPingView;
@Override
public void onCreate() {
super.onCreate();
Logger.e(TAG, "服务创建成功");
NetworkUtils.registerNetworkStatusChangedListener(this);
// scheduledExecutorService.scheduleWithFixedDelay(connectTask, 0, 10, TimeUnit.SECONDS);
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
}
// private final Runnable connectTask = this::doClientConnection;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
if (intent != null && intent.hasExtra("mqttBean")) {
MqttBean mqttBean = (MqttBean) intent.getSerializableExtra("mqttBean");
showPiaoPingMessage(mqttBean);
}
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (piaoPingView != null) {
windowManager.removeView(piaoPingView);
}
}
@Override
@@ -80,304 +54,76 @@ public class EMqttService extends Service implements NetworkUtils.OnNetworkStatu
return null;
}
public static void subscribeRoom(String roomId) {
subscribe("qx_room_topic");
}
public void showPiaoPingMessage(MqttBean mqttBean) {
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int screenHeight = getResources().getDisplayMetrics().heightPixels;
public static void cleanSubscribeRoom(String roomId) {
cleanSubscribe("qx_room_topic");
}
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.TRANSLUCENT);
public static void subscribeUser(String userId) {
sUserId = userId;
subscribe("user_" + userId);
}
// 设置 Gravity 为左上角
layoutParams.gravity = Gravity.TOP | Gravity.START;
public static void cleanSubscribeUser() {
cleanSubscribe("user_" + sUserId);
sUserId = null;
}
// Y 轴随机位置
layoutParams.y = (int) ((Math.random() * (screenHeight - 200)));
/**
* 订阅主题
*/
public static void subscribe(String topic) {
try {
if (isAlreadyConnected()) {
IMqttToken subToken = mqttAndroidClient.subscribe(topic, qos);
subToken.setActionCallback(new IMqttActionListener() {
// 初始 X 设为负值,确保 View 在屏幕左侧外
layoutParams.x = -screenWidth;
piaoPingView = LayoutInflater.from(this).inflate(R.layout.item_piaoping, null);
TextView textView = piaoPingView.findViewById(R.id.tv_name);
textView.setText(mqttBean.getList().getText());
ImageUtils.loadHeadCC(mqttBean.getList().getGift_picture(), piaoPingView.findViewById(R.id.iv_piaoping));
TextView tv_time = piaoPingView.findViewById(R.id.tv_num);
tv_time.setText(mqttBean.getList().getNumber());
windowManager.addView(piaoPingView, layoutParams);
piaoPingView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
piaoPingView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// 设置锚点为左上角,避免偏移干扰
piaoPingView.setPivotX(0);
piaoPingView.setPivotY(0);
// 启动动画:从左外滑入 -> 右外滑出
ObjectAnimator animator = ObjectAnimator.ofFloat(
piaoPingView,
"translationX",
0f, // 初始偏移为 0此时 View 在左侧外)
screenWidth // 向右移动整个屏幕宽度
);
animator.setDuration(2000); // 整个动画的时长为2秒
// 强制 GPU 渲染
piaoPingView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
// 延迟显示2秒后开始滑出屏幕的动画
piaoPingView.postDelayed(new Runnable() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
Logger.e(TAG, "主题订阅成功", topic);
public void run() {
animator.start();
}
}, 2000);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Logger.e(TAG, "主题订阅失败");
ThreadUtils.runOnUiThreadDelayed(() -> subscribe(topic), 1000);
public void onAnimationEnd(Animator animation) {
windowManager.removeView(piaoPingView);
stopSelf();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void cleanSubscribe(String topic) {
try {
if (isAlreadyConnected()) {
IMqttToken subToken = mqttAndroidClient.unsubscribe(topic);
subToken.setActionCallback(new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
Logger.e(TAG, "取消主题成功");
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Logger.e(TAG, "取消主题失败");
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 初始化客户端连接
*/
private String clientId = "";
private void init() throws MqttException {
UserBean user = CommonAppContext.getInstance().getUser();
if (mqttAndroidClient == null) {
clientId = "android-" + user.getUser_id() + "-" + MqttClient.generateClientId();
mqttAndroidClient = new MqttAndroidClient(this, HOST, clientId, MqttAndroidClient.Ack.AUTO_ACK);
}
mMqttConnectOptions = new MqttConnectOptions();
mMqttConnectOptions.setCleanSession(true);
mMqttConnectOptions.setConnectionTimeout(10);
mMqttConnectOptions.setKeepAliveInterval(60);
mMqttConnectOptions.setUserName("public");
mMqttConnectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);
mqttAndroidClient.setCallback(mqttCallback);
}
private synchronized void doClientConnection() throws MqttException {
Logger.e("doClientConnection", "doClientConnection");
if (!isAlreadyConnected() && isConnectIsNomarl()) {
releaseMQTT();
init();
} else {
releaseMQTT();
init();
}
}
/**
* 是否已连接
*/
public static boolean isAlreadyConnected() {
return mqttAndroidClient != null && mqttAndroidClient.isConnected();
}
/**
* 网络是否可用
*/
private boolean isConnectIsNomarl() {
if (NetworkUtils.isConnected()) {
return true;
} else {
Log.i(TAG, "没有可用网络");
return false;
}
}
/**
* 连接状态监听
*/
private final IMqttActionListener iMqttActionListener = new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken arg0) {
long time = 0;
Logger.e(TAG, "链接状态成功");
EventBus.getDefault().post(EmqttState.CONNECTED);
if (sRoomId != null) {
subscribeRoom(sRoomId);
}
subscribe(TOPIC_BOSS);
subscribe(TOPIC_ROOM);
subscribe(TOPIC_QIANG_TANG_GUO);
subscribeUser(CommonAppContext.getInstance().getUser().getUser_id() + "");
}
@Override
public void onFailure(IMqttToken arg0, Throwable arg1) {
arg1.printStackTrace();
if (arg1 instanceof MqttException) {
MqttException mqttException = (MqttException) arg1;
Logger.e(TAG, "链接状态失败:" + mqttException.getMessage());
}
// ThreadUtils.runOnUiThreadDelayed(connectTask, 1000);
}
};
/**
* 消息回调
*/
private final MqttCallback mqttCallback = new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
Logger.e(TAG, "链接断开,请检查网络");
try {
doClientConnection();
} catch (MqttException e) {
throw new RuntimeException(e);
}
}
@Override
public void messageArrived(String topic, MqttMessage message) {
Logger.e(TAG, "收到的消息", "主题:" + topic + " 内容:" + message.toString());
if (TOPIC_BOSS.equals(topic)) {
if (EventBus.getDefault().hasSubscriberForEvent(BossMsgEvent.class)) {
EventBus.getDefault().post(new BossMsgEvent(message.toString()));
}
return;
}
if (TOPIC_QIANG_TANG_GUO.equals(topic)) {
if (EventBus.getDefault().hasSubscriberForEvent(QDZMqttEvent.class)) {
EventBus.getDefault().post(new QDZMqttEvent(message.toString()));
}
return;
}
receiveMessage(topic, message.toString());
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// 发送完成回调
}
};
@Override
public void onDestroy() {
try {
NetworkUtils.unregisterNetworkStatusChangedListener(this);
scheduledExecutorService.shutdownNow();
scheduledExecutorService = null;
cleanSubscribeRoom(sRoomId);
cleanSubscribeUser();
cleanSubscribe(TOPIC_BOSS);
cleanSubscribe(TOPIC_ROOM);
cleanSubscribe(TOPIC_QIANG_TANG_GUO);
releaseMQTT();
if (mqttAndroidClient != null) {
mqttAndroidClient.setCallback(null);
mqttAndroidClient.disconnect();
mqttAndroidClient = null;
}
} catch (Exception e) {
e.printStackTrace();
}
super.onDestroy();
}
public synchronized void releaseMQTT() {
if (mqttAndroidClient != null) {
try {
// stopAlarmPingSender(mqttAndroidClient); // 防止BroadcastReceiver泄漏
mqttAndroidClient.disconnect();
mqttAndroidClient.unregisterResources();
mqttAndroidClient = null;
} catch (Exception e) {
e.printStackTrace();
} finally {
mqttAndroidClient = null;
}
}
}
/**
* 停止 Paho SDK 的 AlarmPingSender
*/
private void stopAlarmPingSender(MqttAndroidClient client) {
try {
Field field = MqttAndroidClient.class.getDeclaredField("client");
field.setAccessible(true);
Object internalClient = field.get(client);
Field pingSenderField = internalClient.getClass().getDeclaredField("pingSender");
pingSenderField.setAccessible(true);
Object pingSender = pingSenderField.get(internalClient);
// if (pingSender instanceof AlarmpingSender) {
// ((AlarmPingSender) pingSender).stop();
// }
} catch (Exception e) {
e.printStackTrace();
}
}
private void receiveMessage(String topic, String data) {
JSONObject jsonObject;
try {
jsonObject = JSON.parseObject(data);
} catch (Exception e) {
e.printStackTrace();
return;
}
int type = jsonObject.getIntValue("type");
String message = jsonObject.getString("msg");
if (TOPIC_ROOM.equals(topic)) {
if (type == 10000) {
// 全服红包
}
}
switch (type) {
// 根据业务逻辑处理不同的消息类型
}
}
private <T> void sendEvent(String message, Class<T> tClass) {
EventBus.getDefault().post(JSON.parseObject(message, tClass));
}
private <T> void sendEventList(String message, Class<T> tClass) {
List<T> list = JSON.parseArray(message, tClass);
EventBus.getDefault().post(list);
}
private <T> void sendKtEvent(String message, Class<T> tClass) {
EventBus.getDefault().post(GsonUtils.GsonToBean(message, tClass));
}
@Override
public void onConnected(NetworkUtils.NetworkType networkType) {
try {
doClientConnection();
} catch (MqttException e) {
throw new RuntimeException(e);
}
}
@Override
public void onDisconnected() {
// 可选:做网络断开后的清理工作
}
public enum EmqttState {
CONNECTED,
DISCONNECTED
});
}
}