Android MQTTv5 代码示例

网上大部分 Android 链接 Mqtt 都是使用 v3 版本,paho 的客户端也多年没有更新,能在网上到的 MQTTv5 的例子很少,也没有在安卓上实践的代码。

经过我长时间摸索,踩了不少坑,最终稳定的代码如问下。

import java.io.FileInputStream
import java.util.Properties

plugins {
    id("com.android.application")
}
var SDK_DIR = System.getenv("ANDROID_SDK_HOME")
android {
    namespace = "cn.netkiller.student"
    compileSdk = 33

    defaultConfig {
        applicationId = "cn.netkiller.student"
        minSdk = 30
        targetSdk = 33
        versionCode = 1
        versionName = "1.3.3"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"


        if (SDK_DIR == null) {
            var props = Properties()
            props.load(FileInputStream(project.rootProject.file("local.properties")))
            SDK_DIR = props.get("sdk.dir").toString()
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        debug {

        }
        applicationVariants.all {
            var buildType = this.buildType.name
            outputs.all {
                if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
                    if (buildType == "debug") {
                        this.outputFileName =
                            "netkiller-${baseName}-v${versionName}-${versionCode}.apk"
                    } else {
                        this.outputFileName = "netkiller-${baseName}-v${versionName}.apk"
                    }
                    println("OutputFileName: $this.outputFileName")
                }
            }
        }
    }

    applicationVariants.all {
        outputs.all {
            if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
                this.outputFileName = "netkiller-${baseName}-v$versionName.apk"
                println("OutputFileName: $this.outputFileName")
            }
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    buildFeatures {
        viewBinding = true
    }

    sourceSets {
        named("main") {
            jniLibs.srcDir("libs")
        }

//        val main by getting{
//            jniLibs.srcDirs("src/main/libs")
            jni.srcDirs()
//        }

    }
}
fun getLayoutLibPath() {
//    return "${android.getSdkDirectory().getAbsolutePath()}" + "/platforms/" + android.compileSdkVersion + "/data/layoutlib.jar";
//    return "${SDK_DIR}/platforms/" + android.compileSdkVersion + "/data/layoutlib.jar";
}
dependencies {

//    compileOnly files ("${SDK_DIR}/platforms/android-24/data/layoutlib.jar")
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.8.0")
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
//    implementation(project(mapOf("path" to ":ai")))
    implementation("androidx.preference:preference:1.2.0")
    implementation("androidx.legacy:legacy-support-v4:1.0.0")
    implementation("androidx.recyclerview:recyclerview:1.3.0")
//    testImplementation("junit:junit:4.13.2")
//    androidTestImplementation("androidx.test.ext:junit:1.1.5")
//    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

//    implementation("com.alibaba:fastjson:2.0.20.android")
    implementation("com.auth0:java-jwt:4.4.0")
//    implementation("org.projectlombok:lombok:1.18.32")

//    implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
    implementation("org.eclipse.paho:org.eclipse.paho.mqttv5.client:1.2.5")
    implementation("org.eclipse.paho:org.eclipse.paho.android.service:1.1.1")

    // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp-sse
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("com.squareup.okhttp3:okhttp-sse:4.12.0")
    implementation("com.google.code.gson:gson:2.10.1")
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
//    implementation("com.belerweb:pinyin4j:2.5.1")
//    implementation("com.github.bumptech.glide:glide:4.16.0")

}

新手容易忽视的是 生命周期管理

onCreate 种启动 Service

onStop 种要关闭

onResume 恢复启动

onDestory 种断开有是 Mqtt 的链接

package cn.netkiller.student;


import static cn.netkiller.student.cloud.Api.token;
import static cn.netkiller.student.utils.AndroidManager.fullscreen;


import cn.netkiller.student.service.MessageQueueService;


public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    private ActivityMainBinding binding;

    private Intent messageQueueService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
       
        messageQueueService = new Intent(this, MessageQueueService.class);
        startService(messageQueueService);
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//            startForegroundService(new Intent(MainActivity.this, MessageQueueService.class));
//        } else {
//            startService(new Intent(MainActivity.this, MessageQueueService.class));
//        }

   
    }

    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(messageQueueService);
    }

    @Override
    public void onResume() {
        super.onResume();
        Config.Api.token = token();
        startService(messageQueueService);
    }

    @Override
    public void onStop() {
        super.onStop();
        stopService(messageQueueService);
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (intent != null) {
            boolean isExit = intent.getBooleanExtra("QUIT", false);
            if (isExit) {
                this.finish();
            }
        }
    }

}

Android Service 启动 MQTTv5 监听 Topic

package cn.netkiller.student.service;

import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import com.google.gson.Gson;

import org.eclipse.paho.mqttv5.client.IMqttToken;
import org.eclipse.paho.mqttv5.client.MqttAsyncClient;
import org.eclipse.paho.mqttv5.client.MqttCallback;
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
import org.eclipse.paho.mqttv5.client.MqttDisconnectResponse;
import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.eclipse.paho.mqttv5.common.MqttMessage;
import org.eclipse.paho.mqttv5.common.packet.MqttProperties;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;

import cn.netkiller.student.MainActivity;
import cn.netkiller.student.R;
import cn.netkiller.student.ai.aigc.AigcSpeech;
import cn.netkiller.student.cloud.Cache;
import cn.netkiller.student.cloud.Device;
import cn.netkiller.student.config.Config;
import cn.netkiller.student.receiver.SendBroadcast;

public class MessageQueueService extends Service {

    private static final String TAG = MessageQueueService.class.getName();
    private final Gson gson = new Gson();
    int qos = 2;
    private String clientId;
    private String topic;
    private MqttAsyncClient mqttAsyncClient;
    private final MqttCallback mqttCallback = new MqttCallback() {

        @Override
        public void disconnected(MqttDisconnectResponse disconnectResponse) {
            Log.d(TAG, "disconnected " + disconnectResponse.toString());
        }

        @Override
        public void mqttErrorOccurred(MqttException exception) {
            Log.d(TAG, "mqttErrorOccurred " + exception.getMessage());
        }

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            String msg = new String(message.getPayload());
            Log.d(TAG, String.format("messageArrived Topic: %s, Id: %s, QoS: %s, Message: %s, ", topic, message.getId(), message.getQos(), message));

            if (topic.equals(Config.Api.appId + "/" + Config.Android.androidId + "/picture")) {
                Map<String, String> map = gson.fromJson(msg, LinkedHashMap.class);
                broadcastImage(map.get("session"), map.get("data"));
            } else if (topic.equals(Config.Api.appId + "/" + Config.Android.androidId + "/audio")) {
                Map<String, String> map = gson.fromJson(msg, LinkedHashMap.class);
                broadcastAudio(map.get("session"), map.get("data"));
            } else if (topic.equals(Config.Api.appId + "/" + Config.Android.androidId + "/story")) {
                Map<String, String> map = gson.fromJson(msg, LinkedHashMap.class);
                broadcastStory(map.get("session"), map.get("data"));
            } else if (topic.equals(Config.Api.appId + "/" + Config.Android.androidId + "/warning")) {
                broadcastAudio("", msg);
            } else if (topic.equals(Config.Api.appId + "/" + Config.Android.androidId + "/notification") || topic.equals(Config.Api.appId + "/notification")) {
                Map<String, String> map = gson.fromJson(msg, LinkedHashMap.class);
                createNotification(map.get("title"), map.get("message"));
            } else if (topic.equals(Config.Api.appId + "/".concat(Config.Android.androidId).concat("/call"))) {
                AigcSpeech aigcSpeech = new AigcSpeech();
                aigcSpeech.say(msg);
            }

        }

        @Override
        public void deliveryComplete(IMqttToken token) {
            Log.d(TAG, "deliveryComplete");
        }

        @Override
        public void connectComplete(boolean reconnect, String serverURI) {
            Log.d(TAG, "connectComplete: " + topic + " reconnect: " + reconnect + " serverURI: " + serverURI);
        }

        @Override
        public void authPacketArrived(int reasonCode, MqttProperties properties) {
            Log.d(TAG, "authPacketArrived reasonCode: " + reasonCode + " properties: " + properties.toString());
        }
    };


    public MessageQueueService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        topic = Config.Api.appId + "/" + Config.Android.androidId + "/#";
        clientId = Config.Api.appId + "-" + Config.Android.androidId;

        try {

            MemoryPersistence persistence = new MemoryPersistence();
            mqttAsyncClient = new MqttAsyncClient(Config.Mqtt.mqttServerUri, clientId, persistence);
            MqttConnectionOptions mqttConnectionOptions = new MqttConnectionOptions();
            mqttConnectionOptions.setUserName(Config.Mqtt.mqttUsername);
            mqttConnectionOptions.setPassword(Config.Mqtt.mqttPassword.getBytes());
            mqttConnectionOptions.setCleanStart(false);
            mqttConnectionOptions.setConnectionTimeout(60);
            mqttConnectionOptions.setKeepAliveInterval(30);
            mqttConnectionOptions.setAutomaticReconnect(true);
            mqttAsyncClient.setCallback(mqttCallback);
            IMqttToken token = mqttAsyncClient.connect(mqttConnectionOptions);

            token.waitForCompletion();
            if (token.isComplete()) {
//                Log.d(TAG, "Subscribe: " + topic);
                mqttAsyncClient.subscribe(topic, qos);
            }

        } catch (MqttException me) {
            Log.e(TAG, String.format("MqttException: %s, msg: %s, loc: %s, cause: %s", me.getReasonCode(), me.getMessage(), me.getLocalizedMessage(), me.getCause()));
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "isConnected " + mqttAsyncClient.isConnected());

        if (mqttAsyncClient.isConnected()) {
            try {
                mqttAsyncClient.disconnect();
                mqttAsyncClient.close();
            } catch (MqttException e) {
                Log.d(TAG, "onDestroy " + e);
            }
            Log.d(TAG, "Close client." + mqttAsyncClient.isConnected());
        }

        Log.d(TAG, "onDestroy.");
    }

    private void broadcastImage(String session, String url) {
        Intent intent = new Intent();
        intent.setAction("main.mqtt");
        intent.putExtra("session", session);
        intent.putExtra("image", url);
        sendBroadcast(intent);
    }

    private void broadcastAudio(String session, String url) {
        Intent intent = new Intent();
        intent.setAction("main.mqtt");
        intent.putExtra("session", session);
        intent.putExtra("audio", url);
        sendBroadcast(intent);
    }

    private void broadcastStory(String session, String text) {
        Intent intent = new Intent();
        intent.setAction("main.mqtt");
        intent.putExtra("session", session);
        intent.putExtra("story", text);
        sendBroadcast(intent);
    }

    private void broadcastProgress(String progress) {
        Intent intent = new Intent();
        intent.setAction("main.progress");
        intent.putExtra("progress", Integer.valueOf(progress));
        sendBroadcast(intent);
    }

    private void toast(String message) {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(() -> {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        });
    }

    public String logcat(String filter) {
        StringBuffer buffer = new StringBuffer();
        String command = "logcat -t 100";
        if (!filter.isEmpty()) {
            command.concat(String.format(" | grep %s", filter));
        } else {
            command.concat("");
        }
        try {
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String temp;
            while ((temp = reader.readLine()) != null) {
                buffer.append(temp).append("\n");
            }

        } catch (IOException e) {
            Log.e(TAG, e.toString());
        }
        return buffer.toString();
    }

    private int createNotification(String title, String text) {
        String channelId = "channelId";
        String channelName = "channelName";
        String description = "description";
        String group = "group";
        int notificationId = new Random().nextInt(101);

        Intent intent = new Intent(this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);

        NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        channel.setDescription(description);
        notificationManagerCompat.createNotificationChannel(channel);

        NotificationCompat.Builder notification = new NotificationCompat.Builder(this, channelId)
                .setContentTitle(title).setContentText(text)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                   }
        notificationManagerCompat.notify(notificationId, notification.build());
        return notificationId;
    }

}

最后祝你成功

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

netkiller-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值