Android 应用开发指南
工业 Android App 开发特点
工业 Android App 与消费级 App 的主要区别:
| 特性 | 消费级 App | 工业 App |
|---|---|---|
| 目标用户 | 普通消费者 | 专业操作员 |
| UI 设计 | 美观、时尚 | 简洁、高效、大字体 |
| 网络依赖 | 强依赖 | 支持离线工作 |
| 更新方式 | Google Play | MDM 推送 / 本地更新 |
| 权限 | 标准权限 | 系统级权限(串口、GPIO) |
| 稳定性 | 一般 | 极高(7×24小时) |
| 安全性 | 一般 | 高(数据加密、防篡改) |
串口通信开发
工业设备通常通过串口与外设通信(打印机、扫码枪、传感器等)。
使用移远串口库
java
// build.gradle 添加依赖
// 移远提供的串口库(从开发者中心下载)
implementation files('libs/quectel-serialport.jar')
// 串口通信封装类
public class SerialPortManager {
private SerialPort serialPort;
private InputStream inputStream;
private OutputStream outputStream;
private Thread readThread;
private volatile boolean isRunning = false;
public interface DataCallback {
void onDataReceived(byte[] data, int length);
}
private DataCallback callback;
public boolean open(String portPath, int baudRate, DataCallback cb) {
try {
this.callback = cb;
serialPort = new SerialPort(new File(portPath), baudRate, 0);
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
isRunning = true;
startReadThread();
return true;
} catch (Exception e) {
Log.e("SerialPort", "Open failed: " + e.getMessage());
return false;
}
}
private void startReadThread() {
readThread = new Thread(() -> {
byte[] buffer = new byte[1024];
while (isRunning) {
try {
int bytesRead = inputStream.read(buffer);
if (bytesRead > 0 && callback != null) {
byte[] data = Arrays.copyOf(buffer, bytesRead);
callback.onDataReceived(data, bytesRead);
}
} catch (IOException e) {
if (isRunning) {
Log.e("SerialPort", "Read error: " + e.getMessage());
}
}
}
});
readThread.start();
}
public void send(byte[] data) {
try {
outputStream.write(data);
outputStream.flush();
} catch (IOException e) {
Log.e("SerialPort", "Send error: " + e.getMessage());
}
}
public void close() {
isRunning = false;
try {
if (serialPort != null) serialPort.close();
} catch (Exception e) {
Log.e("SerialPort", "Close error: " + e.getMessage());
}
}
}使用示例
java
// 在 Activity 中使用串口
SerialPortManager serialManager = new SerialPortManager();
serialManager.open("/dev/ttyHS1", 9600, (data, length) -> {
String received = new String(data, 0, length);
runOnUiThread(() -> {
tvReceived.setText(received);
});
});
// 发送数据
btnSend.setOnClickListener(v -> {
String text = etInput.getText().toString();
serialManager.send(text.getBytes());
});条码扫描集成
使用 ZXing 库
java
// build.gradle
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
// 启动扫码
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES);
integrator.setPrompt("扫描条码");
integrator.setCameraId(0);
integrator.setBeepEnabled(true);
integrator.setBarcodeImageEnabled(false);
integrator.initiateScan();
// 处理扫码结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(
requestCode, resultCode, data);
if (result != null) {
if (result.getContents() == null) {
Toast.makeText(this, "取消扫描", Toast.LENGTH_LONG).show();
} else {
String barcode = result.getContents();
processBarcode(barcode);
}
}
}离线数据缓存
工业 App 必须支持离线工作,网络恢复后自动同步。
Room 数据库
java
// 定义实体
@Entity(tableName = "sensor_data")
public class SensorData {
@PrimaryKey(autoGenerate = true)
public int id;
public float temperature;
public float humidity;
public long timestamp;
@ColumnInfo(name = "is_synced")
public boolean isSynced = false;
}
// 定义 DAO
@Dao
public interface SensorDataDao {
@Insert
void insert(SensorData data);
@Query("SELECT * FROM sensor_data WHERE is_synced = 0")
List<SensorData> getUnsyncedData();
@Query("UPDATE sensor_data SET is_synced = 1 WHERE id = :id")
void markAsSynced(int id);
}
// 数据同步服务
public class SyncService extends Service {
private SensorDataDao dao;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 检查网络
if (isNetworkAvailable()) {
syncData();
}
return START_STICKY;
}
private void syncData() {
List<SensorData> unsyncedData = dao.getUnsyncedData();
for (SensorData data : unsyncedData) {
if (uploadToServer(data)) {
dao.markAsSynced(data.id);
}
}
}
}MDM 远程管理
工业设备通常需要 MDM(Mobile Device Management)进行远程管理。
常用 MDM 功能
java
// 远程锁定设备
DevicePolicyManager dpm = (DevicePolicyManager)
getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.lockNow();
// 远程擦除数据
dpm.wipeData(0);
// 静默安装 APK(需要系统权限)
PackageInstaller installer = getPackageManager().getPackageInstaller();
// ... 静默安装逻辑
// 禁用 USB 调试(生产环境)
Settings.Global.putInt(getContentResolver(),
Settings.Global.ADB_ENABLED, 0);自定义 MDM 客户端
java
// MDM 客户端:接收服务器指令
public class MDMClient {
private static final String MDM_SERVER = "https://mdm.example.com";
// 定期轮询服务器指令
public void pollCommands() {
String deviceId = getDeviceId();
// 发送心跳并获取指令
String url = MDM_SERVER + "/device/" + deviceId + "/commands";
// HTTP GET 请求...
// 执行指令
for (Command cmd : commands) {
executeCommand(cmd);
}
}
private void executeCommand(Command cmd) {
switch (cmd.type) {
case "install_app":
installApp(cmd.params.get("apk_url"));
break;
case "update_config":
updateConfig(cmd.params);
break;
case "reboot":
reboot();
break;
case "collect_logs":
uploadLogs();
break;
}
}
}应用稳定性保障
崩溃恢复
java
// 全局异常处理
public class CrashHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
// 记录崩溃日志
saveCrashLog(ex);
// 上报到服务器
uploadCrashLog();
// 重启 App
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
// 结束当前进程
android.os.Process.killProcess(android.os.Process.myPid());
}
}
// 在 Application 中注册
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());看门狗
java
// 软件看门狗:定期检查 App 状态
public class WatchdogService extends Service {
private Handler handler = new Handler();
private static final int WATCHDOG_INTERVAL = 30000; // 30秒
private Runnable watchdogRunnable = new Runnable() {
@Override
public void run() {
// 检查关键服务是否正常
if (!isMainServiceRunning()) {
// 重启主服务
startMainService();
}
// 检查网络连接
if (!isNetworkAvailable()) {
// 尝试重连
reconnectNetwork();
}
// 继续下一次检查
handler.postDelayed(this, WATCHDOG_INTERVAL);
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler.post(watchdogRunnable);
return START_STICKY; // 服务被杀死后自动重启
}
}