NRF52832空中升级安卓源码

本文档详细介绍了使用NRF52832蓝牙模块进行空中升级(DFU)的Android源码实现过程,包括导入官方SDK、配置AndroidManifest.xml、创建DfuService、处理文件路径以及实现DFU进度监听器。代码示例展示了如何在选择文件后开始升级,并跟踪升级进度直至完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NRF52832空中升级安卓源码

话不多说,上源码

导入官方包

//NRF52*** DFU空中升级SDK
implementation 'no.nordicsemi.android:dfu:1.9.0'

//在AndroidMainfest.xml添加   **是你当前DfuService的路径
<!-- 使用DFU时必须添加否则无法进行 -->
<service android:name="com.**.**.DFU.DfuService"/>

添加DFU文件:

public class DfuService extends DfuBaseService {
    /*
    创建DfuService,实现 getNotificationTarget() 方法,
    在进行DFU时,该方法会返回一个活动类 ,
    该活动通过Intent ,FLAG_ACTIVITY_NEW_TASK标志开启
     */
    @Override
    protected Class<? extends Activity> getNotificationTarget() {
        return NotificationActivity.class;
    }

    @Override
    protected boolean isDebug() {
        return BuildConfig.DEBUG;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return null;
    }
}
//用于组织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, UpdateActivity.class);//UpdateActivity更新页面
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            Bundle bundle = getIntent().getExtras();
            if(bundle != null) {
                intent.putExtras(bundle); // 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();
    }
}

最关键的一步来了

//NRF52832 蓝牙模块空中升级
public class UpdateActivity extends AppCompatActivity{
    private static final String LOG_TAG = UpdateActivity.class.getSimpleName();
    //----------------------------------------------------------------------------------------------
    @BindView(R.id.pb_tTraffic)
    ProgressBar pbBar;
    @BindView(R.id.tv_test)
    TextView tvTest;
    //private BluetoothService mBluetoothService;
    private String path;
    private String dfu_macAddress;
    private String dfu_device_name;
    private MyBluetooth mBleController;//蓝牙工具类
    private BluetoothDevice bluetoothDevice;
    //----------------------------------------------------------------------------------------------
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_update);
        ButterKnife.bind(this);//这个是必须的,否则会出现点击中断不了

        mBleController = MyBluetooth.getInstance();
        bluetoothDevice = mBleController.getBluetoothDevice();
        if(bluetoothDevice != null){
            dfu_device_name = bluetoothDevice.getName();//蓝牙设备名
            dfu_macAddress = bluetoothDevice.getAddress();//蓝牙地址
            //messageShow(dfu_device_name+" " + dfu_macAddress);
        }else{
            messageShow("bluetoothDevice");
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        DfuServiceListenerHelper.registerProgressListener(this, dfuProgressListener);
    }

    @Override
    protected void onPause() {
        super.onPause();
        DfuServiceListenerHelper.unregisterProgressListener(this, dfuProgressListener);
    }

    //点击事件
    @OnClick({R.id.btn_update,R.id.btn_cBack})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_update:{//跳转到工作模式
                //选择文件更新
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

                //intent.setType("image/*");//选择图片
                //intent.setType("audio/*"); //选择音频
                //intent.setType("video/*"); //选择视频 (mp4 3gp 是android支持的视频格式)
                //intent.setType("video/*;image/*");//同时选择视频和图片

                intent.setType("*/*");//无类型限制
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                startActivityForResult(intent, 1);

            }break;
            case R.id.btn_cBack:{
                this.finish();
            }break;
        }
    }
    //----------------------------------------------------------------------------------------------
    public boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
    public boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
    public String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    public boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
    //专为Android4.4设计的从Uri获取文件绝对路径,以前的方法已不好使
    @SuppressLint("NewApi")
    public String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{split[1]};
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }
    //4.4以下下系统调用方法
    public String getRealPathFromURI(Uri contentUri) {
        String res = null;
        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
        if (null != cursor && cursor.moveToFirst()) {
            ;
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            res = cursor.getString(column_index);
            cursor.close();
        }
        return res;
    }
    //获取文件路径
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (resultCode == RESULT_OK) {
            Uri uri = data.getData();
            if (uri == null)
                return;
            //无法获取到SD卡的绝对路径
            if ("file".equalsIgnoreCase(uri.getScheme())) {//使用第三方应用打开
                path = uri.getPath();
            } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {//4.4以后
                path = getPath(this, uri);
            } else {//4.4以下下系统调用方法
                path = getRealPathFromURI(uri);
            }
            //messageShow(path);
            try {//开始进行更新
                if (requestCode == 1) {
                    //progressBar.setVisibility(View.VISIBLE);
                    final DfuServiceInitiator starter = new DfuServiceInitiator(dfu_macAddress)
                            .setDeviceName(dfu_device_name)
                            .setKeepBond(true);
                    starter.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true);

                    starter.setZip(null, path);//null
                    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){
                        starter.setForeground(false);
                        starter.setDisableNotification(true);
                    }
                    starter.start(this, DfuService.class);
                    final DfuServiceController controller = starter.start(this, DfuService.class);
                }
            }catch (Exception e){
                Log.e(LOG_TAG,e.toString());
            }
        }
    }
    //DFU进度监听器
    private final DfuProgressListener dfuProgressListener = new DfuProgressListenerAdapter(){
        @Override
        public void onDeviceConnecting(final String deviceAddress) {
            //relativeDfuProgress.setVisibility(View.VISIBLE);
            Log.e(LOG_TAG,deviceAddress);
        }

        @Override
        public void onDfuProcessStarting(final String deviceAddress) {
            //relativeDfuProgress.setVisibility(View.VISIBLE);
            Log.e(LOG_TAG,deviceAddress);
        }

        @Override
        public void onEnablingDfuMode(final String deviceAddress) {
            //relativeDfuProgress.setVisibility(View.VISIBLE);
            Log.e(LOG_TAG,deviceAddress);
        }

        @Override
        public void onFirmwareValidating(final String deviceAddress) {
            //relativeDfuProgress.setVisibility(View.VISIBLE);
            Log.e(LOG_TAG,deviceAddress);
        }

        @Override
        public void onDeviceDisconnecting(final String deviceAddress) {
            //relativeDfuProgress.setVisibility(View.VISIBLE);
            Log.e(LOG_TAG,deviceAddress);
        }

        @Override
        public void onDfuCompleted(final String deviceAddress) {
            Log.e(LOG_TAG,deviceAddress);
            //Toast.makeText(UpdateActivity.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);
            tvTest.setText("固件升级成功!");
        }

        @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) {

            Log.e("下载中", "onProgressChanged: " + deviceAddress + "百分比" + percent + ",speed "
                    + speed + ",avgSpeed " + avgSpeed + ",currentPart " + currentPart
                    + ",partTotal " + partsTotal);
            //tvBar.setText("升级进度:" + percent + "%");
            pbBar.setProgress(percent);//显示进度条
        }

        @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);
        }
    };

    //反射的方式访问蓝牙设备的隐藏接口removebond
    public void removeBondStatus(BluetoothDevice btDevices){
        boolean result = false;
        try {
            final Method removeBondStatus = btDevices.getClass().getMethod("removeBond");
            if (removeBondStatus != null) {
                result = (Boolean) removeBondStatus.invoke(btDevices);

                while (btDevices.getBondState() != BluetoothDevice.BOND_NONE){
                    Thread.sleep(20);
                }
            }
        } catch (final Exception e) {
            Log.w(LOG_TAG, "清除蓝牙配对失败", e);
        }
    }


    private void messageShow(String str){
        Toast.makeText(this,str,Toast.LENGTH_LONG).show();
    }

就这么简单!!!后续更新如何在工作状态进入BootLoader DFU

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值