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的相关权限、读写权限、定位等。

  1.  
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  2.  
    <uses-permission android:name="android.permission.CAMERA" />
  3.  
    <uses-permission android:name="android.permission.BLUETOOTH" />
  4.  
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  5.  
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  6.  
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  7.  
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

3、创建DfuService,实现 getNotificationTarget() 方法, 在进行DFU时,该方法会返回一个活动类 ,该活动通过Intent ,FLAG_ACTIVITY_NEW_TASK标志开启,下面步骤是定义的具体活动类。

  1.  
    public class DfuService extends DfuBaseService {
  2.  
     
  3.  
     
  4.  
    public DfuService() {
  5.  
    }
  6.  
     
  7.  
    @Nullable
  8.  
    @Override
  9.  
    protected Class<? extends Activity> getNotificationTarget() {
  10.  
    /*
  11.  
    * As a target activity the NotificationActivity is returned, not the MainActivity. This is because
  12.  
    * the notification must create a new task:
  13.  
    *
  14.  
    * intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  15.  
    *
  16.  
    * when you press it. You can use NotificationActivity to check whether the new activity
  17.  
    * is a root activity (that means no other activity was open earlier) or that some
  18.  
    * other activity is already open. In the latter case the NotificationActivity will just be
  19.  
    * closed. The system will restore the previous activity. However, if the application has been
  20.  
    * closed during upload and you click the notification, a NotificationActivity will
  21.  
    * be launched as a root activity. It will create and start the main activity and
  22.  
    * terminate itself.
  23.  
    *
  24.  
    * This method may be used to restore the target activity in case the application
  25.  
    * was closed or is open. It may also be used to recreate an activity history using
  26.  
    * startActivities(...).
  27.  
    */
  28.  
    return NotificationActivity.class;
  29.  
    }
  30.  
     
  31.  
    @Override
  32.  
    public IBinder onBind(Intent intent) {
  33.  
    // TODO: Return the communication channel to the service.
  34.  
    throw new UnsupportedOperationException("Not yet implemented");
  35.  
    }
  36.  
    }

4、创建NotificationActivity类,继承自Activity,该类主要用于组织APP的其他实例启动。

  1.  
    public class NotificationActivity extends Activity {
  2.  
    @Override
  3.  
    protected void onCreate(@Nullable Bundle savedInstanceState) {
  4.  
    super.onCreate(savedInstanceState);
  5.  
     
  6.  
    // If this activity is the root activity of the task, the app is not running
  7.  
    if (isTaskRoot()) {
  8.  
    // Start the app before finishing
  9.  
    final Intent intent = new Intent(this, MineDfuActivity.class);
  10.  
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  11.  
    intent.putExtras(getIntent().getExtras()); // copy all extras
  12.  
    startActivity(intent);
  13.  
    }
  14.  
     
  15.  
    // Now finish, which will drop you to the activity at which you were at the top of the task stack
  16.  
    finish();
  17.  
     
  18.  
     
  19.  
    }
  20.  
    }

5、开启DFU服务

  1.  
    final DfuServiceInitiator starter = new DfuServiceInitiator(MineDeviceActivity.bleDevice.getMac())
  2.  
    .setDeviceName(MineDeviceActivity.bleDevice.getName());
  3.  
    // If you want to have experimental buttonless DFU feature supported call additionally:starter.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true);
  4.  
    String fileName = urlDfuPackage.substring(urlDfuPackage.lastIndexOf("/"));
  5.  
    String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
  6.  
     
  7.  
    starter.setZip(directory + fileName);
  8.  
    final DfuServiceController controller = starter.start(MyApplication.getContext(), DfuService.class);
  9.  
     
  10.  
    //以下是NORDIC DEMO分文件包的格式进行不同处理,我的直接是在固定存储路径下压缩包文件,所以就按照以上写了,以下供参考
  11.  
    // Init packet is required by Bootloader/DFU from SDK 7.0+ if HEX or BIN file is given above.
  12.  
    // In case of a ZIP file, the init packet (a DAT file) must be included inside the ZIP file.
  13.  
    if (mFileType == DfuService.TYPE_AUTO)
  14.  
    starter.setZip(mFileStreamUri, mFilePath);
  15.  
    else {
  16.  
    starter.setBinOrHex(mFileType, mFileStreamUri, mFilePath).setInitFile(mInitFileStreamUri, mInitFilePath);
  17.  
    }
  18.  
    final DfuServiceController controller = starter.start(this, DfuService.class);
  19.  
    // You may use the controller to pause, resume or abort the DFU process.

6、以上步骤完成了DFU过程,但是我们拿单板和APP测试也看不到DFU的过程,所以还需要创建DFU进度监听器,可以掌握DFU的动态情况,比如可以在界面显示DFU进度等。

  1.  
    //DFU进度监听器
  2.  
    private final DfuProgressListener mDfuProgressListener = new DfuProgressListenerAdapter(){
  3.  
     
  4.  
    @Override
  5.  
    public void onDeviceConnecting(final String deviceAddress) {
  6.  
    relativeDfuProgress.setVisibility(View.VISIBLE);
  7.  
     
  8.  
    }
  9.  
     
  10.  
    @Override
  11.  
    public void onDfuProcessStarting(final String deviceAddress) {
  12.  
    relativeDfuProgress.setVisibility(View.VISIBLE);
  13.  
    }
  14.  
     
  15.  
    @Override
  16.  
    public void onEnablingDfuMode(final String deviceAddress) {
  17.  
    relativeDfuProgress.setVisibility(View.VISIBLE);
  18.  
     
  19.  
    }
  20.  
     
  21.  
    @Override
  22.  
    public void onFirmwareValidating(final String deviceAddress) {
  23.  
    relativeDfuProgress.setVisibility(View.VISIBLE);
  24.  
     
  25.  
    }
  26.  
     
  27.  
    @Override
  28.  
    public void onDeviceDisconnecting(final String deviceAddress) {
  29.  
    relativeDfuProgress.setVisibility(View.VISIBLE);
  30.  
     
  31.  
    }
  32.  
     
  33.  
    @Override
  34.  
    public void onDfuCompleted(final String deviceAddress) {
  35.  
    Toast.makeText(MineDfuActivity.this,"固件升级成功",Toast.LENGTH_SHORT).show();
  36.  
    // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again.
  37.  
    new Handler().postDelayed(new Runnable() {
  38.  
    @Override
  39.  
    public void run() {
  40.  
    // onTransferCompleted();
  41.  
     
  42.  
    // if this activity is still open and upload process was completed, cancel the notification
  43.  
    final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  44.  
    manager.cancel(DfuService.NOTIFICATION_ID);
  45.  
    }
  46.  
    }, 200);
  47.  
    }
  48.  
     
  49.  
    @Override
  50.  
    public void onDfuAborted(final String deviceAddress) {
  51.  
    // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again.
  52.  
    new Handler().postDelayed(new Runnable() {
  53.  
    @Override
  54.  
    public void run() {
  55.  
    // onUploadCanceled();
  56.  
     
  57.  
    // if this activity is still open and upload process was completed, cancel the notification
  58.  
    final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  59.  
    manager.cancel(DfuService.NOTIFICATION_ID);
  60.  
    }
  61.  
    }, 200);
  62.  
    }
  63.  
     
  64.  
    @Override
  65.  
    public void onProgressChanged(final String deviceAddress, final int percent, final float speed, final float avgSpeed, final int currentPart, final int partsTotal) {
  66.  
     
  67.  
    int temp = (percent * 360)/100;
  68.  
    circleDfuProgress.update(temp,"");
  69.  
     
  70.  
    // if (partsTotal > 1)
  71.  
    // mTextUploading.setText(getString(R.string.dfu_status_uploading_part, currentPart, partsTotal));
  72.  
    // else
  73.  
    // mTextUploading.setText(R.string.dfu_status_uploading);
  74.  
    }
  75.  
     
  76.  
    @Override
  77.  
    public void onError(final String deviceAddress, final int error, final int errorType, final String message) {
  78.  
    // showErrorMessage(message);
  79.  
     
  80.  
    // We have to wait a bit before canceling notification. This is called before DfuService creates the last notification.
  81.  
    new Handler().postDelayed(new Runnable() {
  82.  
    @Override
  83.  
    public void run() {
  84.  
    // if this activity is still open and upload process was completed, cancel the notification
  85.  
    final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  86.  
    manager.cancel(DfuService.NOTIFICATION_ID);
  87.  
    }
  88.  
    }, 200);
  89.  
    }
  90.  
    };

7、在当前活动中重写onResume及onPause方法,分别进行监听器的注册和取消注册。

  1.  
    @Override
  2.  
    protected void onResume() {
  3.  
    super.onResume();
  4.  
    DfuServiceListenerHelper.registerProgressListener(this, mDfuProgressListener);
  5.  
     
  6.  
    }
  7.  
     
  8.  
    @Override
  9.  
    protected void onPause() {
  10.  
    super.onPause();
  11.  
    DfuServiceListenerHelper.unregisterProgressListener(this, mDfuProgressListener);
  12.  
     
  13.  
    }

【参考】

https://github.com/NordicSemiconductor/Android-DFU-Library/tree/release/documentation

posted @ 2021-01-08 17:53  RockSmall  阅读(637)  评论(0编辑  收藏  举报