1 | 一、单例模式的核心思想 |
安卓设计模式之工厂模式
1 | 一、工厂模式核心思想 |
安卓gradle版本和插件版本对应关系
···
AGP 版本 Gradle 版本 Kotlin 插件版本 说明
4.2.2 6.7.1 1.5.21(推荐) 最后一个支持 compile 的版本
7.0.2 7.0.2 1.5.31 Android Studio Arctic Fox
7.1.3 7.2 1.6.10 Android Studio Bumblebee
7.2.2 7.3 1.6.21 Android Studio Chipmunk
7.3.1 7.4 1.7.10 Android Studio Dolphin
7.4.2 7.5 1.8.10 Android Studio Electric Eel
8.0.2 8.0 1.8.21 Android Studio Flamingo
8.1.2 8.1 1.9.0 Android Studio Giraffe
8.2.2 8.2 1.9.10 Android Studio Hedgehog
8.3.1+ 8.4 2.0.0(兼容) Android Studio Iguana(K2 编译器)
✅ 示例配置(以 AGP 8.1.2 为例)
build.gradle(Project):
buildscript {
ext.kotlin_version = ‘1.9.0’
dependencies {
classpath ‘com.android.tools.build:gradle:8.1.2’
classpath “org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version”
}
}
gradle-wrapper.properties:
distributionUrl=https://services.gradle.org/distributions/gradle-8.1-bin.zip
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-bin.zip
✅ Kotlin DSL(build.gradle.kts)用户
plugins {
id(“com.android.application”) version “8.1.2” apply false
kotlin(“android”) version “1.9.0” apply false
}
⚠️ 注意事项
Kotlin 1.9+ 之后使用了新的 K2 编译器,速度提升但有兼容性变化。
从 AGP 8.0 开始必须使用命名空间(即 namespace = “xxx” 取代 manifest package)。
Kotlin 插件和 AGP 插件必须配套更新,否则构建会报错。
···
安卓内存泄露排查LeakCanary
1 | 在 Android 开发中,内存泄漏是非常常见但又隐蔽的问题,严重时会导致 应用卡顿、OOM(内存溢出)或崩溃。 |
安卓高德地图基础设置
1 | <com.amap.api.maps.MapView |
安卓版本升级功能
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import androidx.fragment.app.DialogFragment;
import com.htnova.fly.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
1 | public class HtUpdateDialogFragment extends DialogFragment { |
R.layout.dialog_update
1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
application中添加
1 | <provider |
xml文件夹下新建file_paths.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
使用new HtUpdateDialogFragment(this).show(getSupportFragmentManager(), “update_dialog”);
安卓LiveData通知UI层更新
LiveData 是 ViewModel 用来“安全地通知 UI 层更新”的方式。
- ViewModel 中定义与更新 LiveData
public class MyViewModel extends ViewModel {
private final MutableLiveData
public LiveData<String> getMessageLiveData() {
return messageLiveData;
}
public void updateMessage(String msg) {
messageLiveData.setValue(msg); // 主线程使用 setValue()
// messageLiveData.postValue(msg); // 子线程也可以用 postValue()
}
}
2. Activity/Fragment 中观察 LiveData
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.getMessageLiveData().observe(this, message -> {
textView.setText(message); // UI 自动刷新
});
3. LiveData 常用操作
方法 说明
setValue() 主线程更新数据
postValue() 子线程更新数据
observe() 观察数据变化(生命周期感知)
observeForever() 永久观察(需手动移除,慎用)
🔁 和 RxJava 比较
特性 LiveData RxJava
生命周期感知 ✅ 内建 ❌ 需手动配合如 RxLifecycle
学习成本 ✅ 简单 ❌ 复杂,操作符多
功能丰富 ❌ 仅支持数据通知 ✅ 支持变换、合并、背压等复杂操作
UI 通知 ✅ 主线程安全 ✅ 手动指定线程
推荐组合用法:业务逻辑用 RxJava,最终结果放入 LiveData 中供 UI 层观察。
🧱 高阶用法
- MediatorLiveData
观察多个 LiveData 源,合并逻辑:
MediatorLiveData
result.addSource(liveData1, value -> {
result.setValue(“来自 liveData1: “ + value);
});
result.addSource(liveData2, value -> {
result.setValue(“来自 liveData2: “ + value);
});
2. SingleLiveEvent(防止重复触发)
防止如“跳转页面”这种事件被重复触发,常自定义如下:
public class SingleLiveEvent
private final AtomicBoolean pending = new AtomicBoolean(false);
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, t -> {
if (pending.compareAndSet(true, false)) {
observer.onChanged(t);
}
});
}
@MainThread
public void setValue(T t) {
pending.set(true);
super.setValue(t);
}
}
🧭 总结:LiveData 用在哪里?
场景 是否推荐用 LiveData
后台网络请求结果通知 UI ✅ 推荐
定时更新状态(配合 RxJava) ✅ 推荐
复杂数据链式变换 ❌ 用 RxJava 更合适
临时事件(弹窗、跳转) ✅ 用 SingleLiveEvent
安卓临时缓存sp工具类
1 | 支持对象存储(自动 JSON 序列化和反序列化)的 Android SPUtils 工具类,使用 Gson 实现对象与字符串的互转。 |
安卓网络请求框架搭建Retrofit+OkHttp+RxJava+MVVM
Retrofit:网络请求框架
OkHttp3:Retrofit 底层网络请求库
RxJava2:响应式编程,处理异步流
MVVM 架构:解耦视图和数据逻辑
ViewModel + LiveData:生命周期感知组件
1 |
|
✅ 总结
层级 类名 作用
网络配置 ApiClient Retrofit 初始化,添加拦截器
拦截器 HeaderInterceptor 动态添加统一请求头
API 接口 ApiService 定义 Retrofit 请求
数据层 UserRepository 请求封装、解耦逻辑
ViewModel UserViewModel 调用 Repository 并分发到界面
Observer RxObserver 自定义网络回调处理逻辑
📁 项目目录结构
├── activity/ # UI 层 Activity 或 Fragment
├── model/ # 数据模型类(Bean)
├── network/ # 网络核心配置(Retrofit + 拦截器)
│ ├── ApiClient.java
│ ├── ApiService.java
│ ├── HeaderInterceptor.java
│ └── RxObserver.java
├── repository/ # 数据仓库层
│ └── UserRepository.java
├── viewmodel/ # ViewModel 层
│ ├── BaseViewModel.java
│ └── UserViewModel.java
安卓mqtt集成
在 Android 中使用 MQTT(Message Queuing Telemetry Transport) 与服务器交互,是实现物联网、实时通信等场景的常用方式
一、准备工作
在 build.gradle(:app) 中添加:
implementation ‘org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5’
implementation ‘org.eclipse.paho:org.eclipse.paho.android.service:1.1.1’
二、配置权限
在 AndroidManifest.xml 添加:
三、初始化 MQTT 客户端
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
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.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import java.nio.charset.StandardCharsets;
/**
@author xqm
@date 2025/7/18 17:01
@description MqttManager 类功能说明
*/
public class MqttManager {
private static final String TAG = “MqttManager”;
private static MqttManager instance;
private final MqttAndroidClient mqttAndroidClient;
private final MqttConnectOptions connectOptions;private String serverUri = “tcp://broker.hivemq.com:1883”; // 可替换为你自己的服务器
private final String clientId = “AndroidClient_” + System.currentTimeMillis();
private final Context context;private OnMessageReceiveListener onMessageReceiveListener;
public MqttManager(Context context,String serverUriAdd,String clientIdAdd,String userName,String pwd) {
this.context = context.getApplicationContext();
mqttAndroidClient = new MqttAndroidClient(context, serverUriAdd, clientIdAdd);
connectOptions = new MqttConnectOptions();
connectOptions.setUserName(userName);
connectOptions.setPassword(pwd.toCharArray());
connectOptions.setCleanSession(true);
connectOptions.setAutomaticReconnect(false);
initCallback();
}// 单例获取方法
public static synchronized MqttManager getInstance(Context context, String serverUriAdd, String clientIdAdd, String userName, String pwd) {
if (instance == null) {
instance = new MqttManager(context, serverUriAdd, clientIdAdd, userName, pwd);
}
return instance;
}// 可选:无参数获取(需提前初始化)
public static synchronized MqttManager getInstance() {
if (instance == null) {
throw new IllegalStateException(“MqttManager 尚未初始化!”);
}
return instance;
}public boolean isConnected() {
return mqttAndroidClient != null && mqttAndroidClient.isConnected();
}private void initCallback() {
mqttAndroidClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
Log.e(TAG, “连接丢失,准备重连”, cause);
reconnectWithDelay();
}
@Override
public void messageArrived(String topic, MqttMessage message) {
String payload = new String(message.getPayload(), StandardCharsets.UTF_8);
Log.d(TAG, “收到消息: topic=” + topic + “, message=” + payload);
if (onMessageReceiveListener != null) {
onMessageReceiveListener.onMessage(topic, payload);
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.d(TAG, “消息发送完成”);
}
});
}public void connect() {
try {
mqttAndroidClient.connect(connectOptions, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d(TAG, “连接成功”);
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e(TAG, “连接失败”, exception);
reconnectWithDelay();
}
});
} catch (MqttException e) {
Log.e(TAG, “连接异常”, e);
}
}private void reconnectWithDelay() {
new Handler(Looper.getMainLooper()).postDelayed(this::connect, 3000);
}public void subscribe(String topic) {
try {
mqttAndroidClient.subscribe(topic, 1, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d(TAG, “订阅成功: “ + topic);
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e(TAG, “订阅失败: “ + topic, exception);
}
});
} catch (MqttException e) {
Log.e(TAG, “订阅异常”, e);
}
}public void publish(String topic, String message) {
try {
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(message.getBytes(StandardCharsets.UTF_8));
mqttAndroidClient.publish(topic, mqttMessage);
Log.d(TAG, “发送消息: topic=” + topic + “, message=” + message);
} catch (MqttException e) {
Log.e(TAG, “发送失败”, e);
}
}public void disconnect() {
try {
mqttAndroidClient.disconnect();
Log.d(TAG, “断开连接”);
} catch (MqttException e) {
Log.e(TAG, “断开异常”, e);
}
}public void setOnMessageReceiveListener(OnMessageReceiveListener listener) {
this.onMessageReceiveListener = listener;
}public interface OnMessageReceiveListener {
void onMessage(String topic, String message);
}
}
四、在 Activity 中使用
mqttManager = MqttManager.getInstance(getApplicationContext(), mqttUrl, clientId, userName, pwd);
mqttManager.setOnMessageReceiveListener((topic, message) -> {
runOnUiThread(() -> {
Toast.makeText(this, “收到: “ + message, Toast.LENGTH_SHORT).show();
});
});
mqttManager.connect();
mqttManager.subscribe(“your/topic”);
订阅多个 Topic(推荐做法)
mqttManager.subscribe(“drone/telemetry”); // 无人机遥测数据
mqttManager.subscribe(“drone/status”); // 状态变化
mqttManager.subscribe(“drone/camera”); // 摄像头控制消息
mqttManager.subscribe(“drone/command”); // 控制指令
也可以封装成数组遍历订阅:
String[] topics = {
“drone/telemetry”,
“drone/status”,
“drone/camera”,
“drone/command”
};
for (String topic : topics) {
mqttManager.subscribe(topic);
}
messageArrived 回调中区分 Topic 内容
mqttManager.setOnMessageReceiveListener((topic, message) -> {
switch (topic) {
case “drone/telemetry”:
// 解析遥测 JSON
break;
case “drone/status”:
// 更新状态 UI
break;
case “drone/command”:
// 响应控制指令
break;
default:
Log.w(“MQTT”, “未知 topic:” + topic);
break;
}
});
扩展建议
如你有“Topic + 消息体 JSON”的组合格式,可以创建实体类并使用 Gson 解析:
TelemetryData data = new Gson().fromJson(message, TelemetryData.class);
五、服务端推荐 MQTT Broker
免费测试:
HiveMQ: tcp://broker.hivemq.com:1883
Eclipse: tcp://iot.eclipse.org:1883
自建服务器推荐:
EMQX
Mosquitto
六、注意事项
保持后台连接:可以考虑 MQTT 放入前台服务中,保证连接不断。
网络断开重连处理:建议实现 connectionLost() 中的自动重连机制。
安全连接:生产环境使用 ssl:// + 用户认证更安全。