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