使用MVVM和架构组件的Ble,有哪些正确的方式?

26

我正在使用蓝牙低功耗设备,思考我的当前方法和最佳实践。目前,我有一个处理连接和GattCallbacks的活动,并决定重构代码以获得更好的概述和可维护性,因为它实际上相当混乱。

我找到了来自NordicSemiconductor的BleManager https://github.com/NordicSemiconductor/Android-BLE-Library/

它是连接BLE设备的基本步骤的抽象,处理GattCallbacks并提供适当的接口以从服务或ViewModel中使用它。

我想使用ViewModel方法,但我对MVC、MVP、MVVM模式不是很熟悉,还有一些问题我仍然无法回答

此类扩展了BleManager (BlinkyManager.java)

它显示了如何利用BleManager,所以我采用了这个类并将其称为ECountBleManager

编辑:
在过去的6天里,我进行了研究,特别是面对MVVM模式和架构组件。不幸的是,仍有许多问题我无法回答。但我真的想变得更好,所以我制定了我的当前概念草案。希望您能帮助我回答我的问题并改进我的项目。

我特别关注最佳实践。

这是我的草案:

enter image description here

以下是我的类实现:
ECountActivity.java

public class ECountActivity extends AppCompatActivity {

    private ECountViewModel viewModel;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.detail_view);

        // hide magnifier icon
        GifImageView customLoader = findViewById(R.id.progressBar);
        customLoader.setVisibility(View.GONE);

        // Get additional data from previous activity
        final BluetoothDevice device = getIntent().getParcelableExtra("device");

        initViewModel();
        viewModel.connect(device);
    }

    private void initViewModel() {
        viewModel = ViewModelProviders.of(this).get(ECountViewModel.class);
        subscribeDataStreams(viewModel);
    }

    private void subscribeDataStreams(ECountViewModel viewModel) {
        viewModel.isDeviceReady().observe(this, deviceReady -> openOptionsFragment());


        viewModel.isConnected().observe(this, status -> {
            // Todo: ...
        });
    }

    private void openOptionsFragment() {
        // load options fragment
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.contentFragment, new OptionsFragment());
        ft.commitNow();
    }

}

OtaFragment.java

public class OtaFragment extends Fragment implements FolderChooserDialog.FolderCallback,
        FileChooserDialog.FileCallback {

    private Button partialOtaButton;
    private Button fullOtaButton;
    private Button submitButton;
    private SeekBar mtuSeekBar;
    private EditText mtuInput;
    private LinearLayout stacklayout;
    private Button browseAppButton;
    private TextView folderPathText;
    private TextView appFileNameText;

    private MaterialDialog otaPrepareDialog;
    private MaterialDialog otaProgressDialog;

    private ECountViewModel viewModel;
    private OtaViewModel otaViewModel;


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initViewModel();
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // inflate the layout for this fragment
        View view = inflater.inflate(R.layout.ota_fragment, container, false);
        initViews(view);
        return view;
    }

    @Override
    public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File folder) {
        final String otaFolderPath = folder.toString();
        otaViewModel.setOtaFolderPath(otaFolderPath);
        folderPathText.setText(otaFolderPath.substring(otaFolderPath.lastIndexOf("/")));
        // enable app browse
        browseAppButton.setClickable(true);
        browseAppButton.setEnabled(true);
    }

    @Override
    public void onFolderChooserDismissed(@NonNull FolderChooserDialog dialog) {}

    @Override
    public void onFileSelection(@NonNull FileChooserDialog dialog, @NonNull File file) {
        final String otaAppFilePath = file.toString();
        otaViewModel.setOtaAppFilePath(otaAppFilePath);
        appFileNameText.setText(otaAppFilePath.substring(otaAppFilePath.lastIndexOf("/")));
        // enable submitButton button
        submitButton.setClickable(true);
        submitButton.setEnabled(true);
    }

    @Override
    public void onFileChooserDismissed(@NonNull FileChooserDialog dialog) {}

    private void subscribeDataStreams(ECountViewModel viewModel) {
        viewModel.isOtaMode().observe(this, otaMode -> {
            otaPrepareDialog.dismiss();
            initOtaProgressDialog();
            otaProgressDialog.show();

            // Todo: how can i get mtu?
            viewModel.requestMtu(512);
        });
    }

    private void initViewModel() {
        viewModel = ViewModelProviders.of(getActivity()).get(ECountViewModel.class);
        otaViewModel = ViewModelProviders.of(getActivity()).get(OtaViewModel.class);
        subscribeDataStreams(viewModel);
    }

    private void initViews(View view) {
        // get resources
        final Button browseFolderButton = view.findViewById(R.id.browseFolder);
        final Button cancelButton = view.findViewById(R.id.ota_cancel);
        final SeekBar prioritySeekBar = view.findViewById(R.id.connection_seekBar);
        partialOtaButton = view.findViewById(R.id.radio_ota);
        fullOtaButton = view.findViewById(R.id.radio_ota_full);
        browseAppButton = view.findViewById(R.id.browseApp);
        folderPathText = view.findViewById(R.id.folderPathText);
        appFileNameText = view.findViewById(R.id.appFileNameText);
        stacklayout = view.findViewById(R.id.stacklayout);
        submitButton = view.findViewById(R.id.ota_proceed);
        mtuSeekBar = view.findViewById(R.id.mtu_seekBar);
        mtuInput = view.findViewById(R.id.mtu_value);

        // set initial states
        mtuSeekBar.setMax(512-23);
        mtuSeekBar.setProgress(244);
        prioritySeekBar.setMax(2);
        prioritySeekBar.setProgress(1);
        browseAppButton.setClickable(false);
        browseAppButton.setEnabled(false);
        submitButton.setClickable(false);
        submitButton.setEnabled(false);

        mtuInput.setOnEditorActionListener((v, actionId, event) -> {
            final Editable mtuText = mtuInput.getText();
            if (mtuText != null) {
                int mtu = Integer.valueOf(mtuText.toString());
                if (mtu < 23)  mtu = 23;
                if (mtu > 512) mtu = 512;
                mtuSeekBar.setProgress(mtu);
                viewModel.setMtu(mtu);
            }
            return false;
        });


        mtuSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                mtuInput.setText(String.valueOf(progress));
                viewModel.setMtu(progress);
            }
        });

        prioritySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                viewModel.setPriority(progress);
            }
        });

        browseFolderButton.setOnClickListener(v -> new FolderChooserDialog.Builder(getActivity())
                .chooseButton(R.string.positiveTextChoose)
                .tag("#folder")
                .show(getChildFragmentManager()));

        browseAppButton.setOnClickListener(v -> new FileChooserDialog.Builder(getActivity())
                .initialPath(otaViewModel.getOtaFolderPath())
                .extensionsFilter(".ebl")
                .tag("#app")
                .show(getChildFragmentManager()));

        cancelButton.setOnClickListener(v -> Log.i("ota", "cancelButton"));

        submitButton.setOnClickListener(v -> {
            // disable OTA submitButton button
            submitButton.setClickable(false);
            submitButton.setEnabled(false);
            // init OTA process
            viewModel.initOtaMode();
            // show OTA preparing dialog
            otaPrepareDialog.show();
        });

        fullOtaButton.setOnClickListener(v -> {
            stacklayout.setVisibility(View.VISIBLE);
            partialOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
            fullOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
        });

        partialOtaButton.setOnClickListener(v -> {
            stacklayout.setVisibility(View.GONE);
            partialOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
            fullOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
        });

        otaPrepareDialog = new MaterialDialog.Builder(getActivity())
                .title(R.string.otaDialogHeaderText)
                .content(R.string.waiting)
                .progress(true, 0)
                .progressIndeterminateStyle(true)
                .build();

        otaProgressDialog = new MaterialDialog.Builder(getActivity())
                .title("test")
                .customView(R.layout.ota_progress2, false)
                .build();
    }

    private void initOtaProgressDialog() {
        // Todo: ...
    }
}

ECountViewModel.java

public class ECountViewModel extends AndroidViewModel implements ECountBleManagerCallbacks {
    private final ECountBleManager eCountBleManager;

    // Connection states Connecting, Connected, Disconnecting, Disconnected etc.
    private final MutableLiveData<String> connectionState = new MutableLiveData<>();

    // Flag to determine if the device is connected
    private final MutableLiveData<Boolean> isConnected = new MutableLiveData<>();

    // Flag to determine if the device is ready
    private final MutableLiveData<Void> onDeviceReady = new MutableLiveData<>();

    // Flag to determine if the device is in OTA mode
    private final MutableLiveData<Void> onOtaMode = new MutableLiveData<>();


    public LiveData<Void> isDeviceReady() {
        return onDeviceReady;
    }

    public LiveData<Void> isOtaMode() {
        return onOtaMode;
    }

    public LiveData<String> getConnectionState() {
        return connectionState;
    }

    public LiveData<Boolean> isConnected() {
        return isConnected;
    }

    public ECountViewModel(@NonNull final Application application) {
        super(application);

        // Initialize the manager
        eCountBleManager = new ECountBleManager(getApplication());
        eCountBleManager.setGattCallbacks(this);
    }

    /**
     * Connect to peripheral
     */
    public void connect(final BluetoothDevice device) {
        eCountBleManager.connect(device);
    }

    /**
     * Disconnect from peripheral
     */
    private void disconnect() {
        eCountBleManager.disconnect();
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        if (eCountBleManager.isConnected()) {
            disconnect();
        }
    }

    @Override
    public void onDeviceConnecting(BluetoothDevice device) {
    }

    @Override
    public void onDeviceConnected(BluetoothDevice device) {
        isConnected.postValue(true);
    }

    @Override
    public void onDeviceDisconnecting(BluetoothDevice device) {
        isConnected.postValue(false);
    }

    @Override
    public void onDeviceDisconnected(BluetoothDevice device) {
        isConnected.postValue(false);
    }

    @Override
    public void onLinklossOccur(BluetoothDevice device) {
        isConnected.postValue(false);
    }

    @Override
    public void onServicesDiscovered(BluetoothDevice device, boolean optionalServicesFound) {
    }

    @Override
    public void onDeviceReady(BluetoothDevice device) {
        onDeviceReady.postValue(null);
    }

    @Override
    public void onOptionalServiceSupported(BluetoothDevice device) {
        onOtaMode.postValue(null);
    }

    @Override
    public void onBondingRequired(BluetoothDevice device) {

    }

    @Override
    public void onBonded(BluetoothDevice device) {

    }

    @Override
    public void onError(BluetoothDevice device, String message, int errorCode) {

    }

    @Override
    public void onDeviceNotSupported(BluetoothDevice device) {
        disconnect();
    }

    // delegate call from options fragment to ECountBleManager
    public String getDeviceId() {
        return BinaryUtils.byteArrayToHexString(eCountBleManager.getDeviceId());
    }

    // delegate call from ota fragment to ECountBleManager
    public void setMtu(final int value) {
        eCountBleManager.setMtu(value);
    }

    public void setPriority(final int value) {
        eCountBleManager.setPriority(value);
    }

ECountBleManager.java

public class ECountBleManager extends BleManager<BleManagerCallbacks> {

    private static final String TAG = ECountBleManager.class.getSimpleName();
    private final Handler handler;

    private BluetoothGattCharacteristic authCharacteristic;
    private BluetoothGattCharacteristic deviceIdCharacteristic;
    private BluetoothGattCharacteristic deviceVersionCharacteristic;
    private BluetoothGattCharacteristic configIdCharacteristic;
    private BluetoothGattCharacteristic configTransmissionIntervalCharacteristic;
    private BluetoothGattCharacteristic configKeepAliveIntervalCharacteristic;
    private BluetoothGattCharacteristic configRadioModeCharacteristic;
    private BluetoothGattCharacteristic configGpsCharacteristic;
    private BluetoothGattCharacteristic configRadarCharacteristic;
    private BluetoothGattCharacteristic configOperationModeCharacteristic;
    private BluetoothGattCharacteristic configLoRaAppEuiCharacteristic;
    private BluetoothGattCharacteristic configLoRaAppKeyCharacteristic;
    private BluetoothGattCharacteristic configLoRaDeviceEuiCharacteristic;
    private BluetoothGattCharacteristic operationCmdCharacteristic;
    private BluetoothGattCharacteristic nemeusStatusCharacteristic;
    private BluetoothGattCharacteristic gmrStatusCharacteristic;
    private BluetoothGattCharacteristic radarStatusCharacteristic;
    private BluetoothGattCharacteristic otaControlCharacteristic;
    private BluetoothGattCharacteristic otaDataCharacteristic;

    private byte[] configTransmissionInterval;
    private byte[] configKeepAliveInterval;
    private byte[] configRadioMode;
    private byte[] configOperationMode;
    private byte[] configId;
    private byte[] deviceId;
    private byte[] deviceVersion;
    private byte[] configGps;
    private byte[] configRadar;
    private byte[] configLoRaAppEui;
    private byte[] configLoRaAppKey;
    private byte[] configLoRaDeviceEui;
    private byte[] operationCmd;
    private byte[] nemeusStatus;
    private byte[] gmrStatus;
    private byte[] radarStatus;

    // OTA flags
    private boolean isOtaProcessing = false;
    private boolean isReconnectRequired = false;
    private MutableLiveData<Boolean> isOtaMode = new MutableLiveData<>();
    // OTA variables
    private int mtu = 512;
    private int priority = BluetoothGatt.CONNECTION_PRIORITY_HIGH;
    private byte[] otaAppFileStream;
    ////////////////////////////

    public ECountBleManager(Context context) {
        super(context);
        handler = new Handler();
    }

    @Override
    protected BleManagerGattCallback getGattCallback() {
        return gattCallback;
    }

    @Override
    protected boolean shouldAutoConnect() {
        return true;
    }

    /**
     * BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc
     */
    private final BleManagerGattCallback gattCallback = new BleManagerGattCallback() {

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

        @Override
        protected void onOptionalServiceSupported() {
            super.onOptionalServiceSupported();
            isOtaMode.postValue(true);
        }

        @Override
        protected boolean isOptionalServiceSupported(BluetoothGatt gatt) {
            final BluetoothGattService otaService = gatt.getService(DC_UUID.otaService);
            otaDataCharacteristic = otaService.getCharacteristic(DC_UUID.otaData);
            return otaDataCharacteristic != null;
        }

        @Override
        protected boolean isRequiredServiceSupported(BluetoothGatt gatt) {
            final BluetoothGattService dcService = gatt.getService(DC_UUID.dcService);
            final BluetoothGattService otaService = gatt.getService(DC_UUID.otaService);

            if (dcService == null || otaService == null) return false;

            authCharacteristic = dcService.getCharacteristic(DC_UUID.authentication);
            deviceIdCharacteristic = dcService.getCharacteristic(DC_UUID.deviceId);
            deviceVersionCharacteristic = dcService.getCharacteristic(DC_UUID.deviceVersion);
            configIdCharacteristic = dcService.getCharacteristic(DC_UUID.configId);
            configTransmissionIntervalCharacteristic = dcService.getCharacteristic(DC_UUID.configTransmissionInterval);
            configKeepAliveIntervalCharacteristic = dcService.getCharacteristic(DC_UUID.configKeepAliveInterval);
            configRadioModeCharacteristic = dcService.getCharacteristic(DC_UUID.configRadioMode);
            configGpsCharacteristic = dcService.getCharacteristic(DC_UUID.configGps);
            configRadarCharacteristic = dcService.getCharacteristic(DC_UUID.configRadar);
            configOperationModeCharacteristic = dcService.getCharacteristic(DC_UUID.configOperationMode);
            configLoRaAppEuiCharacteristic = dcService.getCharacteristic(DC_UUID.configLoRaAppEui);
            configLoRaAppKeyCharacteristic = dcService.getCharacteristic(DC_UUID.configLoRaAppKey);
            configLoRaDeviceEuiCharacteristic = dcService.getCharacteristic(DC_UUID.configLoRaDeviceEui);
            operationCmdCharacteristic = dcService.getCharacteristic(DC_UUID.operationCmd);
            nemeusStatusCharacteristic = dcService.getCharacteristic(DC_UUID.nemeusStatus);
            gmrStatusCharacteristic = dcService.getCharacteristic(DC_UUID.gmrStatus);
            radarStatusCharacteristic = dcService.getCharacteristic(DC_UUID.radarStatus);
            otaControlCharacteristic = otaService.getCharacteristic(DC_UUID.otaControl);

            return authCharacteristic != null &&
                    deviceIdCharacteristic != null &&
                    deviceVersionCharacteristic != null&&
                    configIdCharacteristic != null &&
                    configTransmissionIntervalCharacteristic != null &&
                    configKeepAliveIntervalCharacteristic != null &&
                    configRadioModeCharacteristic != null &&
                    configGpsCharacteristic != null &&
                    configRadarCharacteristic != null &&
                    configOperationModeCharacteristic != null &&
                    configLoRaAppEuiCharacteristic != null &&
                    configLoRaAppKeyCharacteristic != null &&
                    configLoRaDeviceEuiCharacteristic != null &&
                    operationCmdCharacteristic != null &&
                    nemeusStatusCharacteristic != null &&
                    gmrStatusCharacteristic != null &&
                    radarStatusCharacteristic != null &&
                    otaControlCharacteristic != null;
        }

        @Override
        protected Deque<Request> initGatt(BluetoothGatt gatt) {
            final LinkedList<Request> requests = new LinkedList<>();
            requests.push(Request.readRequest(deviceIdCharacteristic));
            requests.push(Request.readRequest(deviceVersionCharacteristic));
            requests.push(Request.readRequest(configIdCharacteristic));
            requests.push(Request.readRequest(configTransmissionIntervalCharacteristic));
            requests.push(Request.readRequest(configKeepAliveIntervalCharacteristic));
            requests.push(Request.readRequest(configRadioModeCharacteristic));
            requests.push(Request.readRequest(configGpsCharacteristic));
            requests.push(Request.readRequest(configRadarCharacteristic));
            requests.push(Request.readRequest(configOperationModeCharacteristic));
            requests.push(Request.readRequest(configLoRaAppEuiCharacteristic));
            requests.push(Request.readRequest(configLoRaAppKeyCharacteristic));
            requests.push(Request.readRequest(operationCmdCharacteristic));
            requests.push(Request.readRequest(configLoRaDeviceEuiCharacteristic));
            requests.push(Request.readRequest(nemeusStatusCharacteristic));
            requests.push(Request.readRequest(gmrStatusCharacteristic));
            requests.push(Request.readRequest(radarStatusCharacteristic));
            // write authentication key to characteristic
            requests.push(Request.writeRequest(authCharacteristic));
            // perform server authentication
            requests.push(Request.readRequest(authCharacteristic));
            return requests;
        }

        @Override
        protected void onDeviceDisconnected() {
            authCharacteristic = null;
            deviceIdCharacteristic = null;
            deviceVersionCharacteristic = null;
            configIdCharacteristic = null;
            configTransmissionIntervalCharacteristic = null;
            configKeepAliveIntervalCharacteristic = null;
            configRadioModeCharacteristic = null;
            configGpsCharacteristic = null;
            configRadarCharacteristic = null;
            configOperationModeCharacteristic = null;
            configLoRaAppEuiCharacteristic = null;
            configLoRaAppKeyCharacteristic = null;
            configLoRaDeviceEuiCharacteristic = null;
            nemeusStatusCharacteristic = null;
            gmrStatusCharacteristic = null;
            radarStatusCharacteristic = null;
            otaDataCharacteristic = null;
        }

        @Override
        protected void onMtuChanged(int mtu) {
            super.onMtuChanged(mtu);
            ECountBleManager.this.mtu = mtu;
        }


        @Override
        protected void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicRead(gatt, characteristic);

            if (characteristic.getUuid().equals(DC_UUID.authentication)) {
                byte encryptedData[];
                try {
                    encryptedData = BinaryUtils.encryptByteArray(characteristic.getValue());
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
                characteristic.setValue(encryptedData);
            } else if (characteristic.getUuid().equals(DC_UUID.deviceId)) {
                deviceId = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.deviceVersion)) {
                deviceVersion = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configId)) {
                configId = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configTransmissionInterval)) {
                configTransmissionInterval = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configKeepAliveInterval)) {
                configKeepAliveInterval = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configRadioMode)) {
                configRadioMode = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configGps)) {
                configGps = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configRadar)) {
                configRadar = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configOperationMode)) {
                configOperationMode = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configLoRaAppEui)) {
                configLoRaAppEui = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configLoRaAppKey)) {
                configLoRaAppKey = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.configLoRaDeviceEui)) {
                configLoRaDeviceEui = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.nemeusStatus)) {
                nemeusStatus = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.gmrStatus)) {
                gmrStatus = characteristic.getValue();
            } else if (characteristic.getUuid().equals(DC_UUID.radarStatus)) {
                radarStatus = characteristic.getValue();
            }
        }

        @Override
        protected void onCharacteristicWrite(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicWrite(gatt, characteristic);

            if (characteristic.getUuid().equals(DC_UUID.otaControl)) {
                final byte[] otaControl = characteristic.getValue();
                if (otaControl.length == 1) {
                    // OTA client initiates the update process
                    if (otaControl[0] == (byte) 0x00) {
                        // set OTA process flag
                        isOtaProcessing = true;

                        // check whether device is in OTA mode
                        if (isOtaMode.getValue()) {
                            // request MTU size
                            requestMtu(mtu);
                            // start update process,but ensure MTU size has been requested
                            handler.postDelayed(() -> uploadOta(), 2000);
                        } else {
                            // reconnect to establish OTA mode
                            isReconnectRequired = true;
                            // enforces device to reconnect
                            gatt.disconnect();
                        }
                    }

                    // OTA client finishes the update process
                    if (otaControl[0] == (byte) 0x03) {
                        if (isOtaProcessing) { // if device is in OTA mode and update process was successful
                            isOtaProcessing = false;
                            disconnect();
                        } else { // if device is in OTA mode, but update process was not established
                            // enforces device to reconnect
                            gatt.disconnect();
                        }
                    }
                }
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
        }
    };

    public byte[] getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(final byte[] value) {
        writeCharacteristic(deviceIdCharacteristic,
                BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
                value);
    }

    // Todo: implement other getters and setters

    // Here I have to get the otaAppFilePath which I discovered in OtaFragment
    public void uploadOta(final String otaAppFilePath) {
        if (otaDataCharacteristic != null) {
            otaDataCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);

            byte[] ebl = null;
            try {
                FileInputStream fileInputStream = new FileInputStream(otaAppFilePath);
                int size = fileInputStream.available();
                byte[] temp = new byte[size];
                fileInputStream.read(temp);
                fileInputStream.close();
                ebl = temp;
            } catch (Exception e) {
                Logger.e(TAG, "Couldn't open file " + e);
            }
            otaAppFileStream = ebl;
            pack = 0;

            // start update process in another thread
            Thread otaUploadThread = new Thread(() -> otaWriteDataReliable());
            otaUploadThread.start();
        }
    }

    private void writeCharacteristic(final BluetoothGattCharacteristic c,
                                     final int writeType,
                                     final byte[] value) {
        if (c == null)
            return;

        c.setWriteType(writeType);
        c.setValue(value);
        writeCharacteristic(c); // will call the underlying API of BleManager
    }

}

这段代码涵盖了基本用例,但我仍然不确定如何将特定组件彼此链接起来。

在阅读MVVM的过程中,我注意到总是有不止一种可能的解决方案/方法。我发现了以下问题:

  1. ECountBleManager是否是存储通过调用characteristics.getValue()获得的变量的正确位置?如果是,我应该将在OtaFragment中发现的变量也放在其中吗(这意味着我必须将mtu等值转发到ECountBleManager)?请考虑我必须访问在OtaFragment和其他片段中发现的变量。
  2. 我应该在ECountVieModelECountBleManager中存储OtaFragment中的变量,还是创建一个OtaViewModel(但我如何在其中访问已在ECountViewModel中创建的ECountBleManager实例?)
  3. 我如何在ECountBleManager中访问在OtaFragment中发现的mtu、priority和otaAppFile?
  4. 每个活动和片段都需要一个ViewModel吗?但是如何解决ECountBleManager实例的问题,参见问题2?
  5. ECountBleManager如何适应MVVM模式?我猜它是Model的一部分?但是哪一部分?Repository、Interactor、Controller、Mediator?

代码并不简单,所以很抱歉,但是你可以看到我真的在努力学习,希望有人能帮助我解决问题并改进我的代码。提前谢谢!


1
你能找到任何最佳实践吗?请分享。 - Shraddha Shravagi
1个回答

1

关于BLE和架构的我的5分建议:

  • 您不需要混淆应用程序架构和BLE层。
  • 只需将BLE视为数据源/-存储库(基于Rx、Coroutines)
  • 将功能拆分为几个部分:BLE扫描器、BLE连接器、BLE命令执行器。使用BLE-Fasade覆盖这些部分。
  • BLE层通常是异步的(它们太复杂了,无法100%测试)
  • BLE层在深入研究时是字节的线程安全处理。不要在主线程上处理它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接