Commit 9c6290b4 by pye52

1、所有指令采取worker的方式重构

2、目前更新方案存在一定问题
parent f2198266
package com.bgycc.smartcanteen.activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import android.content.Context;
import android.content.pm.PackageInfo;
......@@ -24,10 +31,11 @@ import android.widget.TextView;
import com.bgycc.smartcanteen.BuildConfig;
import com.bgycc.smartcanteen.Injection;
import com.bgycc.smartcanteen.R;
import com.bgycc.smartcanteen.command.CommandHelper;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.PayData;
import com.bgycc.smartcanteen.executor.SCTaskExecutor;
import com.bgycc.smartcanteen.socket.SCWebSocketClient;
import com.bgycc.smartcanteen.state.CommandState;
import com.bgycc.smartcanteen.state.ConnectState;
import com.bgycc.smartcanteen.state.PayOfflineState;
import com.bgycc.smartcanteen.state.PayOnlineState;
......@@ -35,7 +43,7 @@ import com.bgycc.smartcanteen.state.QRCodeState;
import com.bgycc.smartcanteen.utils.NetworkUtils;
import com.bgycc.smartcanteen.utils.SmartCanteenUtils;
import com.bgycc.smartcanteen.utils.TTSHelper;
import com.bgycc.smartcanteen.utils.WorkerUtils;
import com.bgycc.smartcanteen.utils.MonitorUtils;
import com.bgycc.smartcanteen.viewModel.CommandViewModel;
import com.bgycc.smartcanteen.viewModel.PayOfflineViewModel;
import com.bgycc.smartcanteen.viewModel.PayOnlineViewModel;
......@@ -77,6 +85,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
private TextView message;
private AudioManager audioManager;
private WorkManager workManager;
private Handler handler = new Handler();
private SimpleDateFormat payDateFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault());
private SimpleDateFormat socketConnectedTimeDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
......@@ -110,6 +119,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
workManager = WorkManager.getInstance(this);
ViewModelFactory factory = Injection.injectFactory(deviceSN);
ViewModelProvider provider = new ViewModelProvider(this, factory);
payOnlineViewModel = provider.get(PayOnlineViewModel.class);
......@@ -243,23 +253,84 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
}
});
commandViewModel.getCommandStateEvent().observe(this, event -> {
switch (event.getState()) {
case CommandState.IDLE:
settingLayout.animate().setDuration(300).alpha(0f);
break;
case CommandState.WAIT:
settingText.setText(event.getMessage());
settingLayout.animate().setDuration(300).alpha(1f);
break;
case CommandState.SUCCESS:
case CommandState.FAILED:
settingText.setText(event.getMessage());
settingLayout.animate().setDuration(300).alpha(1f);
break;
case CommandState.TOGGLE_DEBUG:
commandViewModel.getCommandTask().observe(this, new Observer<Command>() {
private static final String WORKER_NAME = "smartcanteen_command";
private LiveData<WorkInfo> runningLiveData;
private Command runningCommand;
private OneTimeWorkRequest runningRequest;
@Override
public void onChanged(Command command) {
// 若为debug模式切换命令,则直接执行即可
if (CommandHelper.isLogConfig(command)) {
toggleDebugLayout();
break;
commandViewModel.commandFinish(command);
return;
}
// 检查当前是否有在执行的任务
if (runningCommand != null) {
// 若已经有在执行的任务,则跳过该次响应
return;
}
reset();
runningCommand = command;
runningRequest = commandViewModel.getCommandWorker(command);
workManager.beginUniqueWork(WORKER_NAME, ExistingWorkPolicy.REPLACE, runningRequest)
.enqueue();
runningLiveData = workManager.getWorkInfoByIdLiveData(runningRequest.getId());
runningLiveData.observe(MainActivity.this, workInfoObserver);
}
private Observer<WorkInfo> workInfoObserver = new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
if (workInfo == null) {
settingLayout.animate().setDuration(50).alpha(0f);
return;
}
WorkInfo.State state = workInfo.getState();
if (state.isFinished()) {
Data data = workInfo.getOutputData();
switch (state) {
case SUCCEEDED:
case FAILED:
String message = CommandHelper.getMessage(data);
settingText.setText(message);
settingLayout.animate().setDuration(50).alpha(1f);
break;
}
// 任务完成后,隐藏提示语
settingLayout.animate()
.setStartDelay(50)
.setDuration(1300)
.alpha(0f);
// 通知数据库更新命令的执行状态
if (runningCommand != null) {
commandViewModel.commandFinish(runningCommand);
}
// 重置所有变量,使下次命令能成功执行
reset();
return;
}
if (workInfo.getState() == WorkInfo.State.RUNNING) {
Data data = workInfo.getProgress();
String message = CommandHelper.getMessage(data);
settingText.setText(message);
settingLayout.animate().setDuration(300).alpha(1f);
return;
}
}
};
private void reset() {
runningCommand = null;
runningRequest = null;
if (runningLiveData != null) {
runningLiveData.removeObserver(workInfoObserver);
runningLiveData = null;
}
}
});
......@@ -270,8 +341,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
commandViewModel.initialize();
handler.post(updateTimeRunnable);
WorkerUtils.startLogFilesMonitor(this);
WorkerUtils.startDatabaseMonitor(this);
MonitorUtils.startLogFilesMonitor(this);
MonitorUtils.startDatabaseMonitor(this);
SCWebSocketClient.getInstance().tryConnect();
}
......@@ -335,8 +406,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
TTSHelper.release();
SCWebSocketClient.getInstance().realClose();
SCTaskExecutor.getInstance().quit();
WorkerUtils.stopDatabaseMonitor(this);
WorkerUtils.stopLogFilesMonitor(this);
MonitorUtils.stopDatabaseMonitor(this);
MonitorUtils.stopLogFilesMonitor(this);
}
private void initViews() {
......
package com.bgycc.smartcanteen.command;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.google.gson.Gson;
public abstract class CommandHandler {
protected Command command;
protected Gson gson;
protected CommandProgressCallback commandProgressCallback;
public CommandHandler(Command command, Gson gson, CommandProgressCallback commandProgressCallback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = commandProgressCallback;
}
public abstract CommandResponse run() throws Exception;
void idle(String message, int progress) {
commandProgressCallback.idle(message, progress);
}
void wait(String message, int progress) {
commandProgressCallback.wait(message, progress);
}
void success(String message, int progress) {
commandProgressCallback.success(message, progress);
}
void failed(String message, int progress) {
commandProgressCallback.failed(message, progress);
}
CommandResponse failedResult(String reason) {
return CommandResponse.failed(reason);
}
CommandResponse successResult(String reason) {
return CommandResponse.success(reason);
}
}
package com.bgycc.smartcanteen.command;
import androidx.work.Data;
import androidx.work.ListenableWorker;
import androidx.work.OneTimeWorkRequest;
import com.bgycc.smartcanteen.entity.Command;
import com.google.gson.Gson;
import java.util.HashSet;
import java.util.Set;
public class CommandHelper {
private static final String LOG_UPLOAD = "LOG_PULL";
private static final String APP_UPDATE = "CONFIG_UPDATE";
private static final String CONFIG_WIFI = "CONFIG_WIFI";
private static final String CONFIG_LOG = "CONFIG_LOG";
// 设备指令白名单
private static final Set<String> COMMAND_WHITELIST = new HashSet<String>()
{
{
add(LOG_UPLOAD);
add(APP_UPDATE);
add(CONFIG_WIFI);
add(CONFIG_LOG);
}
};
public static String getMessage(Data data) {
return CommandWorker.getMessage(data);
}
public static boolean isLogConfig(Command command) {
return command.getAction().equals(CONFIG_LOG);
}
public static boolean isCommand(String action) {
return COMMAND_WHITELIST.contains(action);
}
public static OneTimeWorkRequest createWorker(Gson gson, Command command, String deviceSN) {
OneTimeWorkRequest worker = null;
Class<? extends ListenableWorker> workerClass = null;
switch (command.getAction()) {
case LOG_UPLOAD:
workerClass = LogCommandWorker.class;
break;
case APP_UPDATE:
workerClass = UpdateCommandWorker.class;
break;
case CONFIG_WIFI:
workerClass = WifiConfigCommandWorker.class;
break;
}
if (workerClass != null) {
Data inputData = CommandWorker.createBaseData(gson.toJson(command), deviceSN);
worker = new OneTimeWorkRequest.Builder(workerClass)
.setInputData(inputData)
.addTag(command.getAction())
.build();
}
return worker;
}
}
package com.bgycc.smartcanteen.command;
public interface CommandProgressCallback {
void idle(String message, int progress);
void wait(String message, int progress);
void success(String message, int progress);
void failed(String message, int progress);
}
package com.bgycc.smartcanteen.command;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import com.bgycc.smartcanteen.Injection;
import com.bgycc.smartcanteen.entity.Command;
import com.google.gson.Gson;
public abstract class CommandWorker extends Worker {
private static final String INPUT_DEVICESN = "input_devcesn";
private static final String INPUT_COMMAND = "input_command";
private static final String OUTPUT_MESSAGE = "output_message";
protected Gson gson;
protected Command command;
protected String deviceSN;
public static Data createBaseData(String jsonCommand, String deviceSN) {
return new Data.Builder()
.putString(INPUT_DEVICESN, deviceSN)
.putString(INPUT_COMMAND, jsonCommand)
.build();
}
public static String getMessage(Data data) {
return data.getString(OUTPUT_MESSAGE);
}
public CommandWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
gson = Injection.provideGson();
deviceSN = getInputData().getString(INPUT_DEVICESN);
command = gson.fromJson(getInputData().getString(INPUT_COMMAND), Command.class);
}
protected void inProgress(String message) {
Data data = new Data.Builder()
.putString(OUTPUT_MESSAGE, message)
.build();
setProgressAsync(data);
}
protected Result success() {
return Result.success();
}
protected Result success(String message) {
Data data = new Data.Builder()
.putString(OUTPUT_MESSAGE, message)
.build();
return Result.success(data);
}
protected Result failed() {
return Result.failure();
}
protected Result failed(String message) {
Data data = new Data.Builder()
.putString(OUTPUT_MESSAGE, message)
.build();
return Result.failure(data);
}
}
package com.bgycc.smartcanteen.command;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.WorkerParameters;
import com.bgycc.smartcanteen.api.SCApi;
import com.bgycc.smartcanteen.api.SCRetrofit;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandLog;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.PathUtils;
import com.blankj.utilcode.util.ZipUtils;
import com.google.gson.Gson;
import org.jetbrains.annotations.NotNull;
......@@ -33,49 +35,39 @@ import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
/**
* 设备指令: 上传日志
*/
public class LogCommandHandler extends CommandHandler {
public class LogCommandWorker extends CommandWorker {
private static final String BOOT_LOG = "system";
private static final String ZIP_FILE = "log.zip";
private static final String UPLOAD_DIR = "upload_log";
private static final long DEFAULT_DELAY = 1000;
private volatile boolean start = false;
private SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
private SimpleDateFormat nameFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
private String deviceSN;
private CommandLog commandLog;
private File uploadDir;
private Date startTime;
private Date endTime;
private static LogCommandHandler instance;
public static synchronized LogCommandHandler getInstance(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
if (instance == null) {
instance = new LogCommandHandler(command, gson, deviceSN, callback);
} else {
if (!instance.start) {
instance.init(command, gson, deviceSN, callback);
}
}
return instance;
}
private void init(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = callback;
this.deviceSN = deviceSN;
public LogCommandWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
this.commandLog = gson.fromJson(command.getData(), CommandLog.class);
parseDate();
}
private LogCommandHandler(Command command, Gson gson, String deviceSN, CommandProgressCallback callback) {
super(command, gson, callback);
this.deviceSN = deviceSN;
this.commandLog = gson.fromJson(command.getData(), CommandLog.class);
@NonNull
@Override
public Result doWork() {
parseDate();
if (!checkLogCommand()) {
return failed("日志上传指令不符合规范");
}
File logFile = getZipLogs();
if (logFile != null) {
inProgress("上传压缩文件至服务器");
upload(logFile);
return success();
}
return failed();
}
private void parseDate() {
......@@ -101,14 +93,17 @@ public class LogCommandHandler extends CommandHandler {
}
// 获取指定类型及日期的日志文件
private File getZipLogs() throws InterruptedException {
private File getZipLogs() {
CommandLog.CommandLogData data = commandLog.getData();
File logDir = getLogDirByType(data.getLogType());
wait("建立临时目录", 0);
inProgress("建立临时目录");
uploadDir = tempDirInit();
Thread.sleep(DEFAULT_DELAY);
try {
Thread.sleep(DEFAULT_DELAY);
} catch (Exception ignored) {
}
if (uploadDir == null) return null;
if (!copyTargetFiles(logDir, uploadDir)) {
......@@ -116,10 +111,13 @@ public class LogCommandHandler extends CommandHandler {
return null;
}
Thread.sleep(DEFAULT_DELAY);
try {
Thread.sleep(DEFAULT_DELAY);
} catch (Exception ignored) {
}
File zip = new File(PathUtils.getExternalStoragePath() + File.separator + ZIP_FILE);
try {
wait("开始压缩", 40);
inProgress("开始压缩");
ZipUtils.zipFile(uploadDir, zip);
} catch (IOException e) {
LogUtils.e(TAG, "压缩日志文件失败: " + e.getMessage(), e);
......@@ -164,7 +162,7 @@ public class LogCommandHandler extends CommandHandler {
};
List<File> logFiles = FileUtils.listFilesInDirWithFilter(src, filter, false, null);
boolean copyResult = true;
wait("筛选目标日志文件", 10);
inProgress("筛选目标日志文件");
int count = 0;
File descFile;
boolean tempCopyResult;
......@@ -200,26 +198,4 @@ public class LogCommandHandler extends CommandHandler {
FileUtils.delete(zip);
}
}
@Override
public synchronized CommandResponse run() throws InterruptedException {
if (start) {
LogUtils.w(TAG, "日志上传任务已启动");
return null;
}
start = true;
if (!checkLogCommand()) {
start = false;
return failedResult("日志上传指令不符合规范");
}
File logFile = getZipLogs();
if (logFile != null) {
wait("上传压缩文件至服务器", 60);
upload(logFile);
start = false;
return successResult("");
}
start = false;
return failedResult("");
}
}
package com.bgycc.smartcanteen.command;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.WorkerParameters;
import com.bgycc.smartcanteen.BuildConfig;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.bgycc.smartcanteen.entity.CommandUpdate;
import com.bgycc.smartcanteen.utils.DeviceProxy;
import com.blankj.utilcode.util.AppUtils;
......@@ -10,63 +13,38 @@ import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.PathUtils;
import com.blankj.utilcode.util.Utils;
import com.google.gson.Gson;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloadListener;
import com.liulishuo.filedownloader.FileDownloadSampleListener;
import com.liulishuo.filedownloader.FileDownloader;
import java.io.File;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
/**
* 设备指令: 更新 <br/>
* 更新任务处理为单例,避免多个更新任务同时进行浪费资源
*/
public class UpdateCommandHandler extends CommandHandler {
public class UpdateCommandWorker extends CommandWorker {
private static final String UPDATE_APK = "SmartCanteen-update.apk";
private static final String UPDATE_APK_PATH = PathUtils.getExternalStoragePath() + File.separator + UPDATE_APK;
private static final long DEFAULT_DELAY = 5 * 1000;
// 保证更新任务不会在同一时间内重复执行
private volatile boolean start = false;
private CommandUpdate commandUpdate;
private static UpdateCommandHandler instance;
public static synchronized UpdateCommandHandler getInstance(Command command, Gson gson, CommandProgressCallback callback) {
if (instance == null) {
instance = new UpdateCommandHandler(command, gson, callback);
} else {
if (!instance.start) {
instance.init(command, gson, callback);
}
}
return instance;
}
private UpdateCommandHandler(Command command, Gson gson, CommandProgressCallback callback) {
super(command, gson, callback);
this.commandUpdate = gson.fromJson(command.getData(), CommandUpdate.class);
}
private void init(Command command, Gson gson, CommandProgressCallback callback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = callback;
this.commandUpdate = gson.fromJson(command.getData(), CommandUpdate.class);
public UpdateCommandWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
commandUpdate = gson.fromJson(command.getData(), CommandUpdate.class);
}
@NonNull
@Override
public synchronized CommandResponse run() {
if (start) {
LogUtils.w(TAG, "更新任务已启动");
return null;
}
start = true;
public Result doWork() {
if (commandUpdate.getData() == null || commandUpdate.getData().getUrl() == null) {
LogUtils.d(TAG, "更新包地址异常: " + commandUpdate.toString());
return failedResult("更新包地址异常");
return failed("更新包地址异常");
}
String url = commandUpdate.getData().getUrl();
FileDownloader.setup(Utils.getApp());
......@@ -77,33 +55,31 @@ public class UpdateCommandHandler extends CommandHandler {
.setSyncCallback(true)
.setForceReDownload(true)
.setListener(listener)
.setSyncCallback(true)
.start();
return successResult("开始下载");
return success("开始下载");
}
private FileDownloadListener listener = new FileDownloadSampleListener() {
@Override
protected void started(BaseDownloadTask task) {
LogUtils.d(TAG, "更新包开始下载: " + task.getUrl());
UpdateCommandHandler.this.wait("开始下载", 0);
inProgress("开始下载");
}
@Override
protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
int per = (int) (soFarBytes * 1f / totalBytes * 100);
UpdateCommandHandler.this.wait("下载进度: " + per + "%", per);
inProgress("下载进度: " + per + "%");
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
LogUtils.e(TAG, "下载失败: " + e.getMessage(), e);
failed("下载失败", 0);
inProgress("更新包下载失败");
try {
Thread.sleep(DEFAULT_DELAY);
} catch (Exception ignored) {
} finally {
idle("", 0);
start = false;
}
}
......@@ -114,8 +90,7 @@ public class UpdateCommandHandler extends CommandHandler {
LogUtils.d(TAG, "更新包下载成功,开始安装: " + (info == null ? "null" : info.getPackageName()));
if (info == null || !info.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
FileUtils.delete(updateApk);
idle("", 0);
start = false;
inProgress("安装包包名异常");
return;
}
if (info.getVersionCode() == BuildConfig.VERSION_CODE) {
......@@ -124,20 +99,17 @@ public class UpdateCommandHandler extends CommandHandler {
} else if (info.getVersionCode() < BuildConfig.VERSION_CODE) {
FileUtils.delete(updateApk);
LogUtils.d(TAG, "不允许安装低版本");
failed("不允许安装低版本", 0);
inProgress("不允许安装低版本");
} else {
if (DeviceProxy.updateApp(updateApk)) {
success("开始安装", 100);
inProgress("开始安装");
} else {
failed("安装文件权限修改失败", 0);
inProgress("安装文件权限修改失败");
}
}
try {
Thread.sleep(DEFAULT_DELAY);
} catch (Exception ignored) {
} finally {
idle("", 0);
start = false;
}
}
};
......
package com.bgycc.smartcanteen.command;
import android.content.Context;
import android.net.wifi.WifiInfo;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import androidx.annotation.NonNull;
import androidx.work.WorkerParameters;
import com.bgycc.smartcanteen.entity.CommandWifiConfig;
import com.bgycc.smartcanteen.utils.NetworkUtils;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.Gson;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
public class WifiConfigCommandHandler extends CommandHandler {
public class WifiConfigCommandWorker extends CommandWorker {
private static final long DEFAULT_DELAY = 3000;
private static final long POLLING_DELAY = 100;
private CommandWifiConfig wifiConfig;
private volatile boolean start = false;
private static WifiConfigCommandHandler instance;
public static synchronized WifiConfigCommandHandler getInstance(Command command, Gson gson, CommandProgressCallback callback) {
if (instance == null) {
instance = new WifiConfigCommandHandler(command, gson, callback);
} else {
if (!instance.start) {
instance.init(command, gson, callback);
}
}
return instance;
}
private WifiConfigCommandHandler(Command command, Gson gson, CommandProgressCallback callback) {
super(command, gson, callback);
this.wifiConfig = gson.fromJson(command.getData(), CommandWifiConfig.class);
}
private void init(Command command, Gson gson, CommandProgressCallback callback) {
this.command = command;
this.gson = gson;
this.commandProgressCallback = callback;
this.wifiConfig = gson.fromJson(command.getData(), CommandWifiConfig.class);
public WifiConfigCommandWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
wifiConfig = gson.fromJson(command.getData(), CommandWifiConfig.class);
}
@NonNull
@Override
public synchronized CommandResponse run() throws InterruptedException {
if (start) {
LogUtils.w(TAG, "Wifi配置任务已启动");
return null;
}
start = true;
public Result doWork() {
CommandWifiConfig.CommandWifiConfigData data = wifiConfig.getData();
if (data == null) {
start = false;
return null;
return failed();
}
if (!NetworkUtils.isWifiEnabled()) {
wait("正在启动Wifi", 5);
inProgress("正在启动Wifi");
if (!NetworkUtils.setEnable(true)) {
String failedMessage = "无法启动Wifi";
wait(failedMessage, 5);
inProgress(failedMessage);
LogUtils.e(TAG, failedMessage);
Thread.sleep(DEFAULT_DELAY);
start = false;
return failedResult(failedMessage);
try {
Thread.sleep(DEFAULT_DELAY);
} catch (Exception ignored) {
}
return failed(failedMessage);
}
}
......@@ -73,7 +49,7 @@ public class WifiConfigCommandHandler extends CommandHandler {
String pwd = data.getPwd();
String type = data.getType();
wait("正在配置Wifi", 10);
inProgress("正在配置Wifi");
LogUtils.d(TAG, "开始配置wifi, ssid: " + ssid + ", identity: " + identity + ", pwd: " + pwd + ", type: " + type);
try {
NetworkUtils.connect(ssid, identity, pwd, type);
......@@ -85,18 +61,15 @@ public class WifiConfigCommandHandler extends CommandHandler {
continue;
}
String message = "Wifi配置成功";
wait(message, 50);
inProgress(message);
LogUtils.d(TAG, message);
Thread.sleep(DEFAULT_DELAY);
return successResult(message);
return success(message);
}
} catch (Exception e) {
LogUtils.e(TAG, "链接wifi过程出错: " + e.getMessage(), e);
Thread.sleep(DEFAULT_DELAY);
return failedResult(e.getMessage());
} finally {
start = false;
return failed(e.getMessage());
}
return failedResult("无法连接Wifi");
return failed("无法连接Wifi");
}
}
package com.bgycc.smartcanteen.worker;
package com.bgycc.smartcanteen.monitor;
import android.content.Context;
......
package com.bgycc.smartcanteen.worker;
package com.bgycc.smartcanteen.monitor;
import android.content.Context;
......
......@@ -2,16 +2,15 @@ package com.bgycc.smartcanteen.utils;
import android.content.Context;
import androidx.work.ExistingWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import com.bgycc.smartcanteen.worker.DatabaseMonitor;
import com.bgycc.smartcanteen.worker.LogFileMonitor;
import com.bgycc.smartcanteen.monitor.DatabaseMonitor;
import com.bgycc.smartcanteen.monitor.LogFileMonitor;
import java.util.concurrent.TimeUnit;
public class WorkerUtils {
public class MonitorUtils {
private static final String LOG_MONITOR_WORKER = "worker_log_monitor";
private static final String DATABASE_MONITOR_WORKER = "database_log_monitor";
......
......@@ -4,27 +4,20 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.work.OneTimeWorkRequest;
import com.bgycc.smartcanteen.command.CommandProgressCallback;
import com.bgycc.smartcanteen.command.CommandHandler;
import com.bgycc.smartcanteen.command.LogCommandHandler;
import com.bgycc.smartcanteen.command.UpdateCommandHandler;
import com.bgycc.smartcanteen.command.WifiConfigCommandHandler;
import com.bgycc.smartcanteen.command.CommandHelper;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.bgycc.smartcanteen.executor.SCTaskExecutor;
import com.bgycc.smartcanteen.socket.SCWebSocketClient;
import com.bgycc.smartcanteen.socket.SCWebSocketListener;
import com.bgycc.smartcanteen.socket.SCWebSocketListenerAdapter;
import com.bgycc.smartcanteen.state.CommandState;
import com.bgycc.smartcanteen.repository.CommandRepository;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
......@@ -33,31 +26,16 @@ import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
* 监听Command数据库的变动,按如下流程进行处理: <br/>
* 从数据库读取 -> 解析执行 -> 更新到数据库
*/
public class CommandViewModel extends ViewModel implements CommandProgressCallback {
private static final String LOG_UPLOAD = "LOG_PULL";
private static final String APP_UPDATE = "CONFIG_UPDATE";
private static final String CONFIG_WIFI = "CONFIG_WIFI";
private static final String CONFIG_LOG = "CONFIG_LOG";
// 设备指令白名单
private static final Set<String> COMMAND_WHITELIST = new HashSet<String>()
{
{
add(LOG_UPLOAD);
add(APP_UPDATE);
add(CONFIG_WIFI);
add(CONFIG_LOG);
}
};
public class CommandViewModel extends ViewModel {
private CommandRepository commandRepository;
private Gson gson;
private String deviceSN;
private MutableLiveData<CommandState> commandState = new MutableLiveData<>();
private MutableLiveData<Command> commandWorker = new MutableLiveData<>();
private LiveData<List<Command>> dataLiveData;
public LiveData<CommandState> getCommandStateEvent() {
return commandState;
public LiveData<Command> getCommandTask() {
return commandWorker;
}
public CommandViewModel(CommandRepository commandRepository, Gson gson, String deviceSN) {
......@@ -67,7 +45,6 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
// 监听数据库的变动,并执行未完成的指令
this.dataLiveData = commandRepository.queryUndoneCommand();
this.dataLiveData.observeForever(dataObserver);
this.commandState.postValue(new CommandState(CommandState.IDLE));
}
public void initialize() {
......@@ -84,104 +61,40 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
private Observer<List<Command>> dataObserver = commands -> {
if (commands == null || commands.isEmpty()) {
commandState.postValue(new CommandState(CommandState.IDLE));
return;
}
Command first = commands.get(0);
RequestRunnable runnable = new RequestRunnable(first);
SCTaskExecutor.getInstance().executeOnDiskIO(runnable);
commandWorker.postValue(first);
};
@Override
public void idle(String message, int progress) {
commandState.postValue(new CommandState(CommandState.IDLE, message, progress));
}
@Override
public void wait(String message, int progress) {
commandState.postValue(new CommandState(CommandState.WAIT, message, progress));
public OneTimeWorkRequest getCommandWorker(Command command) {
return CommandHelper.createWorker(gson, command, deviceSN);
}
@Override
public void success(String message, int progress) {
commandState.postValue(new CommandState(CommandState.SUCCESS, message, progress));
}
@Override
public void failed(String message, int progress) {
commandState.postValue(new CommandState(CommandState.FAILED, message, progress));
public void commandFinish(Command command) {
UpdateDatabaseRunnable runnable = new UpdateDatabaseRunnable(command);
SCTaskExecutor.getInstance().executeOnDiskIO(runnable);
}
private SCWebSocketListener listener = new SCWebSocketListenerAdapter() {
private static final String RESPONSE_PAY_RESULT = "PAY_RESULT";
@Override
public void onMessage(String action, JsonObject obj, String original) {
if (!COMMAND_WHITELIST.contains(action)) return;
if (!CommandHelper.isCommand(action)) return;
LogUtils.d(TAG, "设备下发指令: " + original);
ResponseRunnable runnable = new ResponseRunnable(original, action);
SCTaskExecutor.getInstance().executeOnDiskIO(runnable);
}
};
private class RequestRunnable implements Runnable {
private class UpdateDatabaseRunnable implements Runnable {
private Command command;
RequestRunnable(Command command) {
UpdateDatabaseRunnable(Command command) {
this.command = command;
}
@Override
public void run() {
CommandHandler handler = null;
LogUtils.d(TAG, "开始执行指令: " + command.toString());
try {
switch (command.getAction()) {
case LOG_UPLOAD:
handler = LogCommandHandler.getInstance(command, gson, deviceSN,CommandViewModel.this);
break;
case APP_UPDATE:
handler = UpdateCommandHandler.getInstance(command, gson, CommandViewModel.this);
break;
case CONFIG_WIFI:
handler = WifiConfigCommandHandler.getInstance(command, gson, CommandViewModel.this);
break;
case CONFIG_LOG:
// CONFIG_LOG后直接return
commandState.postValue(new CommandState(CommandState.TOGGLE_DEBUG));
commandFinishAndUpdateDB();
return;
}
} catch (Exception e) {
commandState.postValue(new CommandState(CommandState.FAILED, e.getMessage()));
handler = null;
}
if (handler == null) {
LogUtils.w(TAG, "无法识别指令: " + command.toString());
commandFinishAndUpdateDB();
return;
}
try {
commandState.postValue(new CommandState(CommandState.WAIT));
CommandResponse response = handler.run();
if (response == null) {
LogUtils.d(TAG, "指令无返回结果: " + command.toString());
return;
}
if (response.success()) {
commandState.postValue(new CommandState(CommandState.SUCCESS, response.getMessage()));
} else {
commandState.postValue(new CommandState(CommandState.FAILED, response.getMessage()));
}
} catch (Exception e) {
commandState.postValue(new CommandState(CommandState.FAILED, e.getMessage()));
} finally {
commandFinishAndUpdateDB();
}
}
// 将执行完毕的指令更新到数据库,此时会触发dataObserver的动作(会继续搜索下一条未执行完毕的指令)
private void commandFinishAndUpdateDB() {
command.finish();
LogUtils.d(TAG, "指令执行完毕: " + command.toString());
commandRepository.updateCommand(command);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment