|
|
@@ -13,81 +13,82 @@ import android.bld.scan.configuration.DecoderConfigValues;
|
|
|
import android.util.Log;
|
|
|
|
|
|
/**
|
|
|
- * 扫码核心服务(扫描头独立管理+双超时+低功耗)
|
|
|
- * 适配场景:
|
|
|
- * 1. 打开扫描头(进入场景时),扫码时直接复用,提升响应速度;
|
|
|
- * 2. 单次扫码超时(5秒):停止解码,保留扫描头;
|
|
|
- * 3. 闲置超时(5分钟):无新扫码请求则关闭扫描头+注销广播;
|
|
|
- * 4. 离开场景可手动关闭扫描头,立即释放资源;
|
|
|
- * 5. 默认仅开启瞄准灯,降低功耗。
|
|
|
+ * 扫描核心服务
|
|
|
+ * 特性:
|
|
|
+ * 1. 屏幕状态自动管理扫描头(息屏关闭,亮屏打开)
|
|
|
+ * 2. 页面离开时释放资源
|
|
|
+ * 3. 移除了闲置超时机制
|
|
|
+ * 4. 提供完整的API供外部调用
|
|
|
*/
|
|
|
public class ScanService extends Service {
|
|
|
- private static final String TAG = "ScanService";
|
|
|
-
|
|
|
- // -------------------------- 可配置常量 --------------------------
|
|
|
- /** 单次扫码超时时间(毫秒):无结果则停止解码,保留扫描头 */
|
|
|
- public static final long SCAN_SINGLE_TIMEOUT_MS = 5000;
|
|
|
- /** 扫描头闲置超时时间(毫秒):打开后无扫码请求则关闭 */
|
|
|
- public static final long SCAN_IDLE_TIMEOUT_MS = 120 * 60 * 1000L; // 120分钟
|
|
|
- /** 默认灯光模式:开启照明+瞄准 */
|
|
|
- public static final int DEFAULT_LIGHT_MODE = ScanConstants.LIGHT_MODE_BOTH;
|
|
|
-
|
|
|
- // -------------------------- 核心变量 --------------------------
|
|
|
+ private static final String TAG = ScanConstants.TAG;
|
|
|
+
|
|
|
+ // 核心变量
|
|
|
private ScanManager scanManager;
|
|
|
- private ScanCallback scanCallback;
|
|
|
+ private ScanListener listener;
|
|
|
private PowerManager.WakeLock wakeLock;
|
|
|
- private boolean isScanning = false; // 是否正在解码
|
|
|
- private Handler scanHandler; // 统一处理延迟任务
|
|
|
- private Runnable singleTimeoutRunnable; // 单次扫码超时任务
|
|
|
- private Runnable idleTimeoutRunnable; // 扫描头闲置超时任务
|
|
|
-
|
|
|
- // 广播接收器(仅扫描头打开时注册)
|
|
|
- private final BroadcastReceiver screenReceiver = new BroadcastReceiver() {
|
|
|
+ private boolean isScanning = false;
|
|
|
+ private boolean isScreenOn = true; // 屏幕状态标记
|
|
|
+ private Handler scanHandler;
|
|
|
+ private Runnable scanTimeoutRunnable;
|
|
|
+
|
|
|
+ // 广播接收器注册状态跟踪
|
|
|
+ private boolean isScreenReceiverRegistered = false;
|
|
|
+ private boolean isScanReceiverRegistered = false;
|
|
|
+
|
|
|
+ private final ScanBinder binder = new ScanBinder(this);
|
|
|
+
|
|
|
+ // 屏幕状态广播接收器(处理息屏/亮屏)
|
|
|
+ private final BroadcastReceiver screenStateReceiver = new BroadcastReceiver() {
|
|
|
@Override
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
- if (intent == null || intent.getAction() == null) return;
|
|
|
-
|
|
|
- switch (intent.getAction()) {
|
|
|
- case Intent.ACTION_SCREEN_ON:
|
|
|
- // 屏幕亮起 - 重新打开扫描头
|
|
|
- Log.d(TAG, "屏幕亮起");
|
|
|
- openScanHead();
|
|
|
- break;
|
|
|
- case Intent.ACTION_SCREEN_OFF:
|
|
|
- // 屏幕熄灭 - 关闭扫描头
|
|
|
- Log.d(TAG, "屏幕熄灭");
|
|
|
- closeScanHead();
|
|
|
- break;
|
|
|
+ String action = intent.getAction();
|
|
|
+ if (Intent.ACTION_SCREEN_ON.equals(action)) {
|
|
|
+ // 屏幕亮起 - 打开扫描头
|
|
|
+ isScreenOn = true;
|
|
|
+ Log.d(TAG, "屏幕亮起");
|
|
|
+ openScanHead();
|
|
|
+
|
|
|
+ } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
|
|
|
+ // 屏幕熄灭 - 关闭扫描头
|
|
|
+ isScreenOn = false;
|
|
|
+ Log.d(TAG, "屏幕熄灭");
|
|
|
+ closeScanHead();
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
private final BroadcastReceiver scanReceiver = new BroadcastReceiver() {
|
|
|
@Override
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
- if (intent == null || scanCallback == null || !isScanning) return;
|
|
|
-
|
|
|
+ if (intent == null || listener == null || !isScanning)
|
|
|
+ return;
|
|
|
+
|
|
|
try {
|
|
|
byte[] barcode = intent.getByteArrayExtra(ScanConstants.SCAN_RESULT_DATA);
|
|
|
int barcodeLen = intent.getIntExtra("length", 0);
|
|
|
-
|
|
|
+
|
|
|
if (barcode != null && barcodeLen > 0) {
|
|
|
String barcodeStr = new String(barcode, 0, barcodeLen);
|
|
|
- scanCallback.onScanResult(barcodeStr);
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanResult(barcodeStr);
|
|
|
+ }
|
|
|
Log.d(TAG, "扫描成功:" + barcodeStr);
|
|
|
- stopSingleScan(); // 单次扫码完成,停止解码(保留扫描头)
|
|
|
+ stopScan(); // 单次扫码完成,停止解码
|
|
|
} else {
|
|
|
- scanCallback.onScanError("扫描结果为空");
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("扫描结果为空");
|
|
|
+ }
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- scanCallback.onScanError("解析扫描结果失败:" + e.getMessage());
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("解析扫描结果失败:" + e.getMessage());
|
|
|
+ }
|
|
|
Log.e(TAG, "解析扫描结果失败", e);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
- // Binder实例
|
|
|
- private final ScanBinder binder = new ScanBinder(this);
|
|
|
|
|
|
@Override
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
@@ -97,175 +98,247 @@ public class ScanService extends Service {
|
|
|
@Override
|
|
|
public void onCreate() {
|
|
|
super.onCreate();
|
|
|
- Log.d(TAG, "扫码服务创建");
|
|
|
- scanHandler = new Handler(getMainLooper());
|
|
|
- acquireWakeLock(); // 仅轻量级唤醒锁,不初始化扫描硬件
|
|
|
+ Log.d(TAG, "扫描服务创建");
|
|
|
+ scanHandler = new Handler();
|
|
|
+ initWakeLock();
|
|
|
+ registerScreenStateReceiver();
|
|
|
}
|
|
|
|
|
|
- // -------------------------- 懒加载初始化 --------------------------
|
|
|
- private void lazyInitScanManager() {
|
|
|
- if (scanManager == null) {
|
|
|
- scanManager = ScanManager.getDefaultInstance(this);
|
|
|
- if (scanManager == null) {
|
|
|
- Log.e(TAG, "获取ScanManager实例失败");
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("扫描硬件初始化失败");
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- Log.d(TAG, "扫描管理器懒加载初始化成功");
|
|
|
+ /**
|
|
|
+ * 初始化唤醒锁
|
|
|
+ */
|
|
|
+ private void initWakeLock() {
|
|
|
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
|
|
+ if (pm != null) {
|
|
|
+ wakeLock = pm.newWakeLock(
|
|
|
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
|
|
|
+ ScanConstants.WAKE_LOCK_TAG);
|
|
|
+ wakeLock.setReferenceCounted(false);
|
|
|
+ } else {
|
|
|
+ Log.e(TAG, "获取PowerManager失败,无法初始化唤醒锁");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void registerScanReceivers() {
|
|
|
+ /**
|
|
|
+ * 注册屏幕状态广播
|
|
|
+ */
|
|
|
+ private void registerScreenStateReceiver() {
|
|
|
+ if (isScreenReceiverRegistered) {
|
|
|
+ Log.w(TAG, "屏幕状态广播已注册,无需重复注册");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
- IntentFilter screenFilter = new IntentFilter();
|
|
|
- screenFilter.addAction(Intent.ACTION_SCREEN_ON);
|
|
|
- screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
|
|
- registerReceiver(screenReceiver, screenFilter);
|
|
|
-
|
|
|
- IntentFilter scanFilter = new IntentFilter(ScanConstants.SCAN_ACTION);
|
|
|
- registerReceiver(scanReceiver, scanFilter);
|
|
|
-
|
|
|
- Log.d(TAG, "广播接收器注册完成");
|
|
|
+ IntentFilter filter = new IntentFilter();
|
|
|
+ filter.addAction(Intent.ACTION_SCREEN_ON);
|
|
|
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
|
|
|
+ registerReceiver(screenStateReceiver, filter);
|
|
|
+ isScreenReceiverRegistered = true;
|
|
|
+ Log.d(TAG, "屏幕状态广播已注册");
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "注册广播接收器失败", e);
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("注册扫描广播失败:" + e.getMessage());
|
|
|
+ Log.e(TAG, "注册屏幕状态广播失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 注册扫描结果广播
|
|
|
+ */
|
|
|
+ private void registerScanReceiver() {
|
|
|
+ if (isScanReceiverRegistered) {
|
|
|
+ Log.w(TAG, "扫描结果广播已注册,无需重复注册");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ IntentFilter filter = new IntentFilter(ScanConstants.SCAN_ACTION);
|
|
|
+ registerReceiver(scanReceiver, filter);
|
|
|
+ isScanReceiverRegistered = true;
|
|
|
+ Log.d(TAG, "扫描结果广播已注册");
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e(TAG, "注册扫描结果广播失败", e);
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("注册扫描结果广播失败:" + e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void unregisterScanReceivers() {
|
|
|
+ /**
|
|
|
+ * 注销扫描结果广播
|
|
|
+ */
|
|
|
+ private void unregisterScanReceiver() {
|
|
|
+ if (!isScanReceiverRegistered) {
|
|
|
+ Log.w(TAG, "扫描结果广播未注册,无需注销");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
- unregisterReceiver(screenReceiver);
|
|
|
unregisterReceiver(scanReceiver);
|
|
|
- Log.d(TAG, "广播接收器已注销");
|
|
|
+ isScanReceiverRegistered = false;
|
|
|
+ Log.d(TAG, "扫描结果广播已注销");
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "注销广播接收器失败", e);
|
|
|
+ Log.e(TAG, "注销扫描结果广播失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // -------------------------- 扫描头独立管理API(给Flutter调用) --------------------------
|
|
|
/**
|
|
|
- * 打开扫描头(进入场景时调用)
|
|
|
- * 逻辑:初始化扫描硬件+注册广播+启动闲置超时+默认开启瞄准灯
|
|
|
+ * 注销屏幕状态广播
|
|
|
+ */
|
|
|
+ private void unregisterScreenStateReceiver() {
|
|
|
+ if (!isScreenReceiverRegistered) {
|
|
|
+ Log.w(TAG, "屏幕状态广播未注册,无需注销");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ unregisterReceiver(screenStateReceiver);
|
|
|
+ isScreenReceiverRegistered = false;
|
|
|
+ Log.d(TAG, "屏幕状态广播已注销");
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e(TAG, "注销屏幕状态广播失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // -------------------------- 对外暴露的核心API --------------------------
|
|
|
+ /**
|
|
|
+ * 设置回调监听
|
|
|
+ */
|
|
|
+ public void setListener(ScanListener listener) {
|
|
|
+ this.listener = listener;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 打开扫描头
|
|
|
*/
|
|
|
public boolean openScanHead() {
|
|
|
- // 1. 懒加载初始化扫描管理器
|
|
|
- lazyInitScanManager();
|
|
|
+ // 初始化扫描管理器
|
|
|
if (scanManager == null) {
|
|
|
- return false;
|
|
|
+ scanManager = ScanManager.getDefaultInstance(this);
|
|
|
+ if (scanManager == null) {
|
|
|
+ Log.e(TAG, "获取ScanManager实例失败");
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("扫描硬件初始化失败");
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Log.d(TAG, "扫描管理器初始化成功");
|
|
|
}
|
|
|
+
|
|
|
try {
|
|
|
setDefaultParams();
|
|
|
- // 2. 打开扫描头硬件
|
|
|
+ // 打开扫描头硬件
|
|
|
scanManager.openScanner();
|
|
|
int retryCount = 0;
|
|
|
while (!scanManager.isScannerOpen() && retryCount < 10) {
|
|
|
- Thread.sleep(50);
|
|
|
+ try {
|
|
|
+ Thread.sleep(50);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ Log.e(TAG, "等待扫描头打开时被中断", e);
|
|
|
+ break;
|
|
|
+ }
|
|
|
retryCount++;
|
|
|
}
|
|
|
if (!scanManager.isScannerOpen()) {
|
|
|
Log.e(TAG, "扫描头打开超时");
|
|
|
return false;
|
|
|
}
|
|
|
- // 3. 设置默认灯光(照明+瞄准)
|
|
|
- setLightingMode(DEFAULT_LIGHT_MODE);
|
|
|
- // 4. 设置广播模式
|
|
|
+
|
|
|
+ // 设置默认灯光(照明+瞄准)
|
|
|
+ setLightingMode(ScanConstants.LIGHT_MODE_BOTH);
|
|
|
+ // 设置广播模式
|
|
|
scanManager.setOPMode(ScanConstants.SCAN_OP_MODE_BROADCAST);
|
|
|
- // 5. 注册广播接收器(只在第一次打开时注册)
|
|
|
- if (screenReceiver != null && scanReceiver != null) {
|
|
|
- try {
|
|
|
- unregisterReceiver(screenReceiver);
|
|
|
- unregisterReceiver(scanReceiver);
|
|
|
- } catch (Exception e) {
|
|
|
- // 忽略注销异常
|
|
|
- }
|
|
|
- registerScanReceivers();
|
|
|
+
|
|
|
+ // 注册扫描结果广播
|
|
|
+ registerScanReceiver();
|
|
|
+
|
|
|
+ // 通知监听器扫描头已打开
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanHeadStateChanged(true);
|
|
|
}
|
|
|
-
|
|
|
- // 6. 启动扫描头闲置超时(核心调整:打开扫描头就启动)
|
|
|
- startIdleTimeout();
|
|
|
- Log.d(TAG, "扫描头已打开(闲置超时:" + SCAN_IDLE_TIMEOUT_MS + "ms)");
|
|
|
+
|
|
|
+ Log.d(TAG, "扫描头已打开");
|
|
|
return true;
|
|
|
} catch (Exception e) {
|
|
|
Log.e(TAG, "打开扫描头失败", e);
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("打开扫描头失败:" + e.getMessage());
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("打开扫描头失败:" + e.getMessage());
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void setDefaultParams() {
|
|
|
- /**
|
|
|
- * 自定义广播 action 以接收数据
|
|
|
- */
|
|
|
- scanManager.setBroadcastAction(ScanConstants.SCAN_ACTION);
|
|
|
- /**
|
|
|
- * 自定义广播 key 以接收数据
|
|
|
- */
|
|
|
- scanManager.setBroadcastKey(ScanConstants.SCAN_RESULT_DATA);
|
|
|
- /**
|
|
|
- * 0-广播模式,通过注册广播获取扫描数据
|
|
|
- * 1-输入框模式,在此模式下,任何输入框都可以填充扫描条形码
|
|
|
- * 2-键盘模式,模拟键盘输入
|
|
|
- * 3-单输入框模式,任何输入框均可填充扫描条形码,无重复
|
|
|
- * 4-剪贴板,将扫描的条形码放在剪贴板上,用户可以将其粘贴到任何地方
|
|
|
- */
|
|
|
- // scanManager.setOPMode(0);
|
|
|
- /**
|
|
|
- * 0 - none
|
|
|
- * 1 - Enter
|
|
|
- * 2 - Tab
|
|
|
- */
|
|
|
- scanManager.setEndMark(0);
|
|
|
- /**
|
|
|
- * 是否启用手柄按钮
|
|
|
- */
|
|
|
- scanManager.setHandleKey(false);
|
|
|
- /**
|
|
|
- * 是否播放声音
|
|
|
- */
|
|
|
- scanManager.setPlaySound(true);
|
|
|
- /**
|
|
|
- * 是否震动
|
|
|
- */
|
|
|
- scanManager.setVibrate(true);
|
|
|
- /**
|
|
|
- * 前缀
|
|
|
- */
|
|
|
- scanManager.setPrefix("");
|
|
|
- /**
|
|
|
- * 后缀
|
|
|
- */
|
|
|
- scanManager.setSuffix("");
|
|
|
- /**
|
|
|
- * 设置连续扫描 (设置为禁用时为 true,允许为 false)
|
|
|
- */
|
|
|
- scanManager.setContinueScan(false);
|
|
|
- /**
|
|
|
- * 是否启用所有一维码(true-启用; false-不启用)
|
|
|
- */
|
|
|
- scanManager.enableSYM1D(true);
|
|
|
- /**
|
|
|
- * 是否启用所有二维码(true-启用; false-不启用)
|
|
|
- */
|
|
|
- scanManager.enableSYM2D(true);
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 设置默认参数
|
|
|
+ */
|
|
|
+ private void setDefaultParams() {
|
|
|
+ try {
|
|
|
+ /**
|
|
|
+ * 自定义广播 action 以接收数据
|
|
|
+ */
|
|
|
+ scanManager.setBroadcastAction(ScanConstants.SCAN_ACTION);
|
|
|
+ /**
|
|
|
+ * 自定义广播 key 以接收数据
|
|
|
+ */
|
|
|
+ scanManager.setBroadcastKey(ScanConstants.SCAN_RESULT_DATA);
|
|
|
+ /**
|
|
|
+ * 0-广播模式,通过注册广播获取扫描数据
|
|
|
+ * 1-输入框模式,在此模式下,任何输入框都可以填充扫描条形码
|
|
|
+ * 2-键盘模式,模拟键盘输入
|
|
|
+ * 3-单输入框模式,任何输入框均可填充扫描条形码,无重复
|
|
|
+ * 4-剪贴板,将扫描的条形码放在剪贴板上,用户可以将其粘贴到任何地方
|
|
|
+ */
|
|
|
+ scanManager.setOPMode(0);
|
|
|
+ /**
|
|
|
+ * 0 - none
|
|
|
+ * 1 - Enter
|
|
|
+ * 2 - Tab
|
|
|
+ */
|
|
|
+ scanManager.setEndMark(0);
|
|
|
+ /**
|
|
|
+ * 是否启用手柄按钮
|
|
|
+ */
|
|
|
+ scanManager.setHandleKey(false);
|
|
|
+ /**
|
|
|
+ * 是否播放声音
|
|
|
+ */
|
|
|
+ scanManager.setPlaySound(true);
|
|
|
+ /**
|
|
|
+ * 是否震动
|
|
|
+ */
|
|
|
+ scanManager.setVibrate(true);
|
|
|
+ /**
|
|
|
+ * 前缀
|
|
|
+ */
|
|
|
+ scanManager.setPrefix("");
|
|
|
+ /**
|
|
|
+ * 后缀
|
|
|
+ */
|
|
|
+ scanManager.setSuffix("");
|
|
|
+ /**
|
|
|
+ * 设置连续扫描 (设置为禁用时为 true,允许为 false)
|
|
|
+ */
|
|
|
+ scanManager.setContinueScan(false);
|
|
|
+ /**
|
|
|
+ * 是否启用所有一维码(true-启用; false-不启用)
|
|
|
+ */
|
|
|
+ scanManager.enableSYM1D(true);
|
|
|
+ /**
|
|
|
+ * 是否启用所有二维码(true-启用; false-不启用)
|
|
|
+ */
|
|
|
+ scanManager.enableSYM2D(true);
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e(TAG, "设置默认参数失败", e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 手动关闭扫描头(离开场景时调用)
|
|
|
- * 逻辑:停止解码+关闭硬件+注销广播+取消所有超时
|
|
|
+ * 关闭扫描头
|
|
|
*/
|
|
|
public void closeScanHead() {
|
|
|
- // 1. 取消所有超时任务
|
|
|
- cancelAllTimeouts();
|
|
|
+ // 取消扫描超时任务
|
|
|
+ cancelScanTimeout();
|
|
|
|
|
|
- // 2. 停止解码(若正在扫码)
|
|
|
+ // 停止解码
|
|
|
if (scanManager != null && isScanning) {
|
|
|
try {
|
|
|
scanManager.stopDecode();
|
|
|
@@ -274,304 +347,244 @@ public class ScanService extends Service {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 3. 关闭扫描头硬件
|
|
|
- if (scanManager != null && isScanHeadOpened()) {
|
|
|
+ // 关闭扫描头硬件
|
|
|
+ if (scanManager != null && scanManager.isScannerOpen()) {
|
|
|
try {
|
|
|
scanManager.closeScanner();
|
|
|
- Log.d(TAG, "扫描头已手动关闭");
|
|
|
+ Log.d(TAG, "扫描头已关闭");
|
|
|
} catch (Exception e) {
|
|
|
Log.e(TAG, "关闭扫描头失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 4. 更新状态
|
|
|
+ // 注销扫描结果广播
|
|
|
+ unregisterScanReceiver();
|
|
|
+
|
|
|
isScanning = false;
|
|
|
+
|
|
|
+ // 通知监听器扫描头已关闭
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanHeadStateChanged(false);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // -------------------------- 扫码解码API(给Flutter调用) --------------------------
|
|
|
/**
|
|
|
- * 开始单次扫码(扫描头已打开时直接复用,未打开则自动打开)
|
|
|
+ * 开始扫描
|
|
|
*/
|
|
|
public boolean startScan() {
|
|
|
- if (isScanning) {
|
|
|
- Log.w(TAG, "扫码已在进行中,无需重复调用");
|
|
|
- resetSingleTimeout(); // 重置单次超时
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- // 1. 若扫描头未打开,自动打开
|
|
|
if (!isScanHeadOpened()) {
|
|
|
- Log.d(TAG, "自动打开扫描头");
|
|
|
- boolean openSuccess = openScanHead();
|
|
|
- if (!openSuccess) {
|
|
|
+ boolean opened = openScanHead();
|
|
|
+ if (!opened) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int retryCount = 0;
|
|
|
+ while (!isScanHeadOpened() && retryCount < 10) {
|
|
|
+ retryCount++;
|
|
|
+ try {
|
|
|
+ Thread.sleep(100);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ Log.e(TAG, "等待扫描头打开时被中断", e);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!isScanHeadOpened()) {
|
|
|
+ Log.e(TAG, "打开扫描头超时");
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (isScanning) {
|
|
|
+ Log.w(TAG, "扫描已在进行中");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
- // 2. 开始解码(复用已打开的扫描头)
|
|
|
+ // 开始解码
|
|
|
scanManager.startDecode();
|
|
|
isScanning = true;
|
|
|
- // 3. 启动单次扫码超时
|
|
|
- startSingleTimeout();
|
|
|
- // 4. 重置闲置超时(扫码行为刷新闲置计时)
|
|
|
- resetIdleTimeout();
|
|
|
- Log.d(TAG, "开始单次扫码(超时:" + SCAN_SINGLE_TIMEOUT_MS + "ms)");
|
|
|
+
|
|
|
+ // 启动扫描超时
|
|
|
+ startScanTimeout();
|
|
|
+
|
|
|
+ Log.d(TAG, "开始扫描");
|
|
|
return true;
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "开始扫码失败", e);
|
|
|
+ Log.e(TAG, "开始扫描失败", e);
|
|
|
isScanning = false;
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("开始扫码失败:" + e.getMessage());
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("开始扫描失败:" + e.getMessage());
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 停止单次扫码(仅停止解码,保留扫描头)
|
|
|
+ * 停止扫描
|
|
|
*/
|
|
|
- public void stopSingleScan() {
|
|
|
- // 1. 取消单次扫码超时
|
|
|
- cancelSingleTimeout();
|
|
|
+ public void stopScan() {
|
|
|
+ // 取消扫描超时任务
|
|
|
+ cancelScanTimeout();
|
|
|
|
|
|
- // 2. 停止解码
|
|
|
+ // 停止解码
|
|
|
if (scanManager != null && isScanning) {
|
|
|
try {
|
|
|
scanManager.stopDecode();
|
|
|
- Log.d(TAG, "单次扫码停止(保留扫描头)");
|
|
|
+ Log.d(TAG, "扫描已停止");
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "停止解码失败", e);
|
|
|
+ Log.e(TAG, "停止扫描失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
isScanning = false;
|
|
|
- // 闲置超时仍在运行,到期自动关闭扫描头
|
|
|
}
|
|
|
|
|
|
- // -------------------------- 超时任务管理(核心调整) --------------------------
|
|
|
/**
|
|
|
- * 启动扫描头闲置超时(打开扫描头时调用)
|
|
|
+ * 启动扫描超时
|
|
|
*/
|
|
|
- private void startIdleTimeout() {
|
|
|
- cancelIdleTimeout(); // 先取消旧任务
|
|
|
- idleTimeoutRunnable = () -> {
|
|
|
- if (isScanHeadOpened() && !isScanning) {
|
|
|
- Log.d(TAG, "扫描头闲置超时(" + SCAN_IDLE_TIMEOUT_MS + "ms),自动关闭");
|
|
|
- closeScanHead(); // 关闭扫描头+注销广播
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("扫描头闲置超时已自动关闭");
|
|
|
+ private void startScanTimeout() {
|
|
|
+ cancelScanTimeout(); // 先取消旧任务
|
|
|
+ scanTimeoutRunnable = () -> {
|
|
|
+ if (isScanning) {
|
|
|
+ Log.d(TAG, "扫描超时,自动停止");
|
|
|
+ stopScan();
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("扫描超时,未检测到条码");
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
- scanHandler.postDelayed(idleTimeoutRunnable, SCAN_IDLE_TIMEOUT_MS);
|
|
|
+ scanHandler.postDelayed(scanTimeoutRunnable, ScanConstants.DEFAULT_SCAN_TIMEOUT_MS);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 重置扫描头闲置超时(扫码/手动刷新时调用)
|
|
|
+ * 取消扫描超时
|
|
|
*/
|
|
|
- public void resetIdleTimeout() {
|
|
|
- if (isScanHeadOpened() && idleTimeoutRunnable != null) {
|
|
|
- scanHandler.removeCallbacks(idleTimeoutRunnable);
|
|
|
- scanHandler.postDelayed(idleTimeoutRunnable, SCAN_IDLE_TIMEOUT_MS);
|
|
|
- Log.d(TAG, "扫描头闲置超时已重置");
|
|
|
+ private void cancelScanTimeout() {
|
|
|
+ if (scanTimeoutRunnable != null) {
|
|
|
+ scanHandler.removeCallbacks(scanTimeoutRunnable);
|
|
|
+ scanTimeoutRunnable = null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 启动单次扫码超时(扫码时调用)
|
|
|
+ * 重置超时时间
|
|
|
*/
|
|
|
- private void startSingleTimeout() {
|
|
|
- cancelSingleTimeout(); // 先取消旧任务
|
|
|
- singleTimeoutRunnable = () -> {
|
|
|
- if (isScanning) {
|
|
|
- Log.d(TAG, "单次扫码超时(" + SCAN_SINGLE_TIMEOUT_MS + "ms),停止解码");
|
|
|
- stopSingleScan();
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("单次扫码超时,未检测到条码");
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- scanHandler.postDelayed(singleTimeoutRunnable, SCAN_SINGLE_TIMEOUT_MS);
|
|
|
+ public void resetTimeout() {
|
|
|
+ if (isScanning && scanTimeoutRunnable != null) {
|
|
|
+ scanHandler.removeCallbacks(scanTimeoutRunnable);
|
|
|
+ scanHandler.postDelayed(scanTimeoutRunnable, ScanConstants.DEFAULT_SCAN_TIMEOUT_MS);
|
|
|
+ Log.d(TAG, "扫描超时已重置");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 重置单次扫码超时(屏幕亮起时调用)
|
|
|
+ * 设置灯光模式
|
|
|
*/
|
|
|
- private void resetSingleTimeout() {
|
|
|
- if (singleTimeoutRunnable != null) {
|
|
|
- scanHandler.removeCallbacks(singleTimeoutRunnable);
|
|
|
- scanHandler.postDelayed(singleTimeoutRunnable, SCAN_SINGLE_TIMEOUT_MS);
|
|
|
- Log.d(TAG, "单次扫码超时已重置");
|
|
|
+ public void setLightingMode(int mode) {
|
|
|
+ if (scanManager != null && isScanHeadOpened()) {
|
|
|
+ try {
|
|
|
+ int[] paramIDList = { DecoderConfigValues.ParamID.LIGHTS_MODE };
|
|
|
+ int[] symValue = { mode };
|
|
|
+ scanManager.setSYMValueInts(paramIDList, symValue);
|
|
|
+ Log.d(TAG, "灯光模式设置为:" + mode);
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e(TAG, "设置灯光模式失败", e);
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("设置灯光模式失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (listener != null) {
|
|
|
+ listener.onScanError("扫描头未打开,无法设置灯光");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 取消单次扫码超时
|
|
|
- */
|
|
|
- private void cancelSingleTimeout() {
|
|
|
- if (singleTimeoutRunnable != null) {
|
|
|
- scanHandler.removeCallbacks(singleTimeoutRunnable);
|
|
|
- singleTimeoutRunnable = null;
|
|
|
- }
|
|
|
+ // -------------------------- 状态查询API --------------------------
|
|
|
+ public boolean isScanning() {
|
|
|
+ return isScanning;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 取消闲置超时
|
|
|
- */
|
|
|
- private void cancelIdleTimeout() {
|
|
|
- if (idleTimeoutRunnable != null) {
|
|
|
- scanHandler.removeCallbacks(idleTimeoutRunnable);
|
|
|
- idleTimeoutRunnable = null;
|
|
|
+ public boolean isScanHeadOpened() {
|
|
|
+ if (scanManager == null) {
|
|
|
+ return false;
|
|
|
}
|
|
|
+ return scanManager.isScannerOpen();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 取消所有超时任务
|
|
|
- */
|
|
|
- private void cancelAllTimeouts() {
|
|
|
- cancelSingleTimeout();
|
|
|
- cancelIdleTimeout();
|
|
|
+ public boolean isScreenOn() {
|
|
|
+ return isScreenOn;
|
|
|
}
|
|
|
|
|
|
- // -------------------------- 辅助方法 --------------------------
|
|
|
/**
|
|
|
- * 暂停扫码(屏幕熄灭时):仅停止解码,保留扫描头
|
|
|
+ * 获取唤醒锁
|
|
|
*/
|
|
|
- private void pauseScan() {
|
|
|
- if (scanManager != null && isScanning) {
|
|
|
+ private void acquireWakeLock() {
|
|
|
+ if (wakeLock != null && !wakeLock.isHeld()) {
|
|
|
try {
|
|
|
- scanManager.stopDecode();
|
|
|
- Log.d(TAG, "扫码已暂停(屏幕熄灭)");
|
|
|
+ wakeLock.acquire(ScanConstants.WAKE_LOCK_TIMEOUT_MS);
|
|
|
+ Log.d(TAG, "唤醒锁已获取");
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "暂停扫码失败", e);
|
|
|
+ Log.e(TAG, "获取唤醒锁失败", e);
|
|
|
}
|
|
|
}
|
|
|
- // 确保在暂停后将扫描状态设置为false,以便在屏幕亮起后能重新开始扫描
|
|
|
- isScanning = false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 恢复扫码(屏幕亮起时):重新解码+重置单次超时
|
|
|
+ * 释放唤醒锁
|
|
|
*/
|
|
|
- private void resumeScan() {
|
|
|
- if (scanManager != null && isScanHeadOpened()) {
|
|
|
+ private void releaseWakeLock() {
|
|
|
+ if (wakeLock != null && wakeLock.isHeld()) {
|
|
|
try {
|
|
|
- scanManager.startDecode();
|
|
|
- Log.d(TAG, "扫码已恢复(屏幕亮起)");
|
|
|
+ wakeLock.release();
|
|
|
+ Log.d(TAG, "唤醒锁已释放");
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "恢复扫码失败", e);
|
|
|
- // 出错时重置扫描状态,以便下次可以重新开始扫描
|
|
|
- isScanning = false;
|
|
|
+ Log.e(TAG, "释放唤醒锁失败", e);
|
|
|
}
|
|
|
- } else {
|
|
|
- // 如果扫描头未打开,重置状态
|
|
|
- isScanning = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 释放资源(页面离开时调用)
|
|
|
+ */
|
|
|
+ public void releaseResources() {
|
|
|
+ Log.d(TAG, "释放扫描服务资源");
|
|
|
|
|
|
- private String getLightModeDesc(int mode) {
|
|
|
- switch (mode) {
|
|
|
- case ScanConstants.LIGHT_MODE_OFF: return "关闭";
|
|
|
- case ScanConstants.LIGHT_MODE_AIM_ONLY: return "仅瞄准";
|
|
|
- case ScanConstants.LIGHT_MODE_ILLUM_ONLY: return "仅照明";
|
|
|
- case ScanConstants.LIGHT_MODE_ALTERNATE: return "交替";
|
|
|
- case ScanConstants.LIGHT_MODE_BOTH: return "瞄准+照明";
|
|
|
- default: return "未知模式(" + mode + ")";
|
|
|
- }
|
|
|
- }
|
|
|
+ // 停止扫描
|
|
|
+ stopScan();
|
|
|
|
|
|
- // -------------------------- 基础方法 --------------------------
|
|
|
- private void acquireWakeLock() {
|
|
|
- try {
|
|
|
- PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
|
- if (powerManager != null) {
|
|
|
- wakeLock = powerManager.newWakeLock(
|
|
|
- PowerManager.PARTIAL_WAKE_LOCK,
|
|
|
- ScanConstants.WAKE_LOCK_TAG
|
|
|
- );
|
|
|
- // 唤醒锁时长适配最长闲置时间+单次扫码时间
|
|
|
- wakeLock.acquire(SCAN_IDLE_TIMEOUT_MS + SCAN_SINGLE_TIMEOUT_MS);
|
|
|
- Log.d(TAG, "唤醒锁已获取(适配闲置超时)");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- Log.e(TAG, "获取唤醒锁失败", e);
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("获取唤醒锁失败:" + e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // 关闭扫描头
|
|
|
+ closeScanHead();
|
|
|
|
|
|
- // -------------------------- 对外暴露的状态/回调 --------------------------
|
|
|
- public void setScanCallback(ScanCallback callback) {
|
|
|
- this.scanCallback = callback;
|
|
|
- }
|
|
|
+ // 注销屏幕状态广播
|
|
|
+ unregisterScreenStateReceiver();
|
|
|
|
|
|
- public boolean isScanning() {
|
|
|
- return isScanning;
|
|
|
- }
|
|
|
+ // 释放唤醒锁
|
|
|
+ releaseWakeLock();
|
|
|
|
|
|
- public boolean isScanHeadOpened() {
|
|
|
- if (scanManager == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return scanManager.isScannerOpen();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置灯光模式
|
|
|
- */
|
|
|
- public void setLightingMode(int mode) {
|
|
|
- if (scanManager != null && isScanHeadOpened()) {
|
|
|
- try {
|
|
|
- // {DecoderConfigValues.ParamID.CODE128_ENABLE} 条码模式
|
|
|
- // {DecoderConfigValues.ParamID.LIGHTS_MODE} 灯光模式
|
|
|
- int[] paramIDList = {DecoderConfigValues.ParamID.LIGHTS_MODE};
|
|
|
- int[] symValue = {mode};
|
|
|
- scanManager.setSYMValueInts(paramIDList, symValue);
|
|
|
- Log.d(TAG, "灯光模式设置为:" + getLightModeDesc(mode));
|
|
|
- } catch (Exception e) {
|
|
|
- Log.e(TAG, "设置灯光模式失败", e);
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("设置灯光模式失败:" + e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (scanCallback != null) {
|
|
|
- scanCallback.onScanError("扫描头未打开,无法设置灯光");
|
|
|
- }
|
|
|
- }
|
|
|
+ scanManager = null;
|
|
|
+ listener = null;
|
|
|
}
|
|
|
|
|
|
// -------------------------- 生命周期 --------------------------
|
|
|
@Override
|
|
|
public void onDestroy() {
|
|
|
super.onDestroy();
|
|
|
- Log.d(TAG, "扫码服务销毁");
|
|
|
-
|
|
|
- // 彻底关闭扫描头
|
|
|
- closeScanHead();
|
|
|
-
|
|
|
+ Log.d(TAG, "扫描服务销毁");
|
|
|
+
|
|
|
+ // 释放资源
|
|
|
+ releaseResources();
|
|
|
+
|
|
|
// 释放Handler
|
|
|
if (scanHandler != null) {
|
|
|
scanHandler.removeCallbacksAndMessages(null);
|
|
|
scanHandler = null;
|
|
|
}
|
|
|
-
|
|
|
- // 释放唤醒锁
|
|
|
- if (wakeLock != null && wakeLock.isHeld()) {
|
|
|
- wakeLock.release();
|
|
|
- Log.d(TAG, "唤醒锁已释放");
|
|
|
- }
|
|
|
-
|
|
|
- scanCallback = null;
|
|
|
- scanManager = null;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public boolean onUnbind(Intent intent) {
|
|
|
- // 解绑时关闭扫描头(可选,根据业务调整)
|
|
|
+ // 解绑时关闭扫描头
|
|
|
closeScanHead();
|
|
|
return true;
|
|
|
}
|