android nordic OTA 升级
根据NORDIC官方Android-DFU-Library实现,具体步骤如下:
1、build.gradle配置
implementation 'no.nordicsemi.android:dfu:1.9.0'
2、AndroidMainfest.xml中申请BLE的相关权限、读写权限、定位等。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
3、创建DfuService,实现 getNotificationTarget()
方法, 在进行DFU时,该方法会返回一个活动类 ,
该活动通过Intent ,FLAG_ACTIVITY_NEW_TASK标志开启,下面步骤是定义的具体活动类。
public class DfuService extends DfuBaseService { public DfuService() { } @Nullable @Override protected Class<? extends Activity> getNotificationTarget() { /* * As a target activity the NotificationActivity is returned, not the MainActivity. This is because * the notification must create a new task: * * intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); * * when you press it. You can use NotificationActivity to check whether the new activity * is a root activity (that means no other activity was open earlier) or that some * other activity is already open. In the latter case the NotificationActivity will just be * closed. The system will restore the previous activity. However, if the application has been * closed during upload and you click the notification, a NotificationActivity will * be launched as a root activity. It will create and start the main activity and * terminate itself. * * This method may be used to restore the target activity in case the application * was closed or is open. It may also be used to recreate an activity history using * startActivities(...). */ return NotificationActivity.class; } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } }
4、创建NotificationActivity类,继承自Activity,该类主要用于组织APP的其他实例启动。
public class NotificationActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // If this activity is the root activity of the task, the app is not running if (isTaskRoot()) { // Start the app before finishing final Intent intent = new Intent(this, MineDfuActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtras(getIntent().getExtras()); // copy all extras startActivity(intent); } // Now finish, which will drop you to the activity at which you were at the top of the task stack finish(); } }
5、开启DFU服务
final DfuServiceInitiator starter = new DfuServiceInitiator(MineDeviceActivity.bleDevice.getMac()) .setDeviceName(MineDeviceActivity.bleDevice.getName()); // If you want to have experimental buttonless DFU feature supported call additionally:starter.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true); String fileName = urlDfuPackage.substring(urlDfuPackage.lastIndexOf("/")); String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); starter.setZip(directory + fileName); final DfuServiceController controller = starter.start(MyApplication.getContext(), DfuService.class); //以下是NORDIC DEMO分文件包的格式进行不同处理,我的直接是在固定存储路径下压缩包文件,所以就按照以上写了,以下供参考 // Init packet is required by Bootloader/DFU from SDK 7.0+ if HEX or BIN file is given above. // In case of a ZIP file, the init packet (a DAT file) must be included inside the ZIP file. if (mFileType == DfuService.TYPE_AUTO) starter.setZip(mFileStreamUri, mFilePath); else { starter.setBinOrHex(mFileType, mFileStreamUri, mFilePath).setInitFile(mInitFileStreamUri, mInitFilePath); } final DfuServiceController controller = starter.start(this, DfuService.class); // You may use the controller to pause, resume or abort the DFU process.
6、以上步骤完成了DFU过程,但是我们拿单板和APP测试也看不到DFU的过程,所以还需要创建DFU进度监听器,可以掌握DFU的动态情况,比如可以在界面显示DFU进度等。
//DFU进度监听器 private final DfuProgressListener mDfuProgressListener = new DfuProgressListenerAdapter(){ @Override public void onDeviceConnecting(final String deviceAddress) { relativeDfuProgress.setVisibility(View.VISIBLE); } @Override public void onDfuProcessStarting(final String deviceAddress) { relativeDfuProgress.setVisibility(View.VISIBLE); } @Override public void onEnablingDfuMode(final String deviceAddress) { relativeDfuProgress.setVisibility(View.VISIBLE); } @Override public void onFirmwareValidating(final String deviceAddress) { relativeDfuProgress.setVisibility(View.VISIBLE); } @Override public void onDeviceDisconnecting(final String deviceAddress) { relativeDfuProgress.setVisibility(View.VISIBLE); } @Override public void onDfuCompleted(final String deviceAddress) { Toast.makeText(MineDfuActivity.this,"固件升级成功",Toast.LENGTH_SHORT).show(); // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again. new Handler().postDelayed(new Runnable() { @Override public void run() { // onTransferCompleted(); // if this activity is still open and upload process was completed, cancel the notification final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.cancel(DfuService.NOTIFICATION_ID); } }, 200); } @Override public void onDfuAborted(final String deviceAddress) { // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again. new Handler().postDelayed(new Runnable() { @Override public void run() { // onUploadCanceled(); // if this activity is still open and upload process was completed, cancel the notification final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.cancel(DfuService.NOTIFICATION_ID); } }, 200); } @Override public void onProgressChanged(final String deviceAddress, final int percent, final float speed, final float avgSpeed, final int currentPart, final int partsTotal) { int temp = (percent * 360)/100; circleDfuProgress.update(temp,""); // if (partsTotal > 1) // mTextUploading.setText(getString(R.string.dfu_status_uploading_part, currentPart, partsTotal)); // else // mTextUploading.setText(R.string.dfu_status_uploading); } @Override public void onError(final String deviceAddress, final int error, final int errorType, final String message) { // showErrorMessage(message); // We have to wait a bit before canceling notification. This is called before DfuService creates the last notification. new Handler().postDelayed(new Runnable() { @Override public void run() { // if this activity is still open and upload process was completed, cancel the notification final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.cancel(DfuService.NOTIFICATION_ID); } }, 200); } };
7、在当前活动中重写onResume及onPause方法,分别进行监听器的注册和取消注册。
@Override protected void onResume() { super.onResume(); DfuServiceListenerHelper.registerProgressListener(this, mDfuProgressListener); } @Override protected void onPause() { super.onPause(); DfuServiceListenerHelper.unregisterProgressListener(this, mDfuProgressListener); }
【参考】
https://github.com/NordicSemiconductor/Android-DFU-Library/tree/release/documentation
根据NORDIC官方Android-DFU-Library实现,具体步骤如下:
1、build.gradle配置
implementation 'no.nordicsemi.android:dfu:1.9.0'
2、AndroidMainfest.xml中申请BLE的相关权限、读写权限、定位等。
3、创建DfuService,实现 getNotificationTarget()
方法, 在进行DFU时,该方法会返回一个活动类 ,该活动通过Intent ,FLAG_ACTIVITY_NEW_TASK标志开启,下面步骤是定义的具体活动类。
4、创建NotificationActivity类,继承自Activity,该类主要用于组织APP的其他实例启动。
5、开启DFU服务
6、以上步骤完成了DFU过程,但是我们拿单板和APP测试也看不到DFU的过程,所以还需要创建DFU进度监听器,可以掌握DFU的动态情况,比如可以在界面显示DFU进度等。
7、在当前活动中重写onResume及onPause方法,分别进行监听器的注册和取消注册。
【参考】
https://github.com/NordicSemiconductor/Android-DFU-Library/tree/release/documentation