dfu升級適用於nordic nRF51 nRF52 的系統,github上提供了相關升級的庫 https://github.com/NordicSemiconductor/Android-DFU-Library 支持 Android 4.3+
gradle配置如下:
implementation 'no.nordicsemi.android:dfu:1.8.1'
在使用前請先配置好BLE的相關權限以及動態權限 讀寫權限 定位等可以使用EasyPermission配置
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
第一步 我們創建一個service集成DfuBaseService
public class DfuService extends DfuBaseService { public DfuService() { } @Override protected Class<? extends Activity> getNotificationTarget() { return NotificationActivity.class; } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } }
第二步 創建一個NotificationActivity
public class NotificationActivity extends Activity { @Override protected void onCreate(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, UpdateZipActivity.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(); } }
其中UpdateZipActivity即為處理升級業務的界面
在這里要根據你的業務需求來,可以把升級包打包在apk里,也可以做成下載后升級的情況
如果是打包在apk里可以按下面做,如果你是下載的情況只需要改starter.setZip(uri, path); 傳入文件uri和路徑即可
private DfuServiceInitiator starter; starter = new DfuServiceInitiator(mac).setDeviceName("Smart Lock").setKeepBond(false).setPacketsReceiptNotificationsEnabled(true).setPacketsReceiptNotificationsValue(10); // If you want to have experimental buttonless DFU feature supported call additionally: starter.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true); starter.setZip(R.raw.update24); //setDeviceName為設備過濾條件 setzip傳入raw文件的路徑即可
@Override
protected void onResume() {
DfuServiceListenerHelper.registerProgressListener(this, mDfuProgressListener); //監聽升級進度
starter.start(this, DfuService.class); //啟動升級服務
}
/**
* dfu升級監聽
*/
private final DfuProgressListener mDfuProgressListener = new DfuProgressListener() {
@Override
public void onDeviceConnecting(String deviceAddress) {
Log.i("dfu", "onDeviceConnecting");
proBar.setIndeterminate(true);
airUpgradeTv.setText(R.string.dfu_status_connecting);
}
@Override
public void onDeviceConnected(String deviceAddress) {
}
@Override
public void onDfuProcessStarting(String deviceAddress) {
proBar.setIndeterminate(true);
airUpgradeTv.setText(R.string.dfu_status_starting);
}
@Override
public void onDfuProcessStarted(String deviceAddress) {
Log.i("dfu", "onDfuProcessStarted");
}
@Override
public void onEnablingDfuMode(String deviceAddress) {
Log.i("dfu", "onEnablingDfuMode");
proBar.setIndeterminate(true);
airUpgradeTv.setText(R.string.dfu_status_switching_to_dfu);
}
@Override
public void onProgressChanged(String deviceAddress, int percent, float speed, float avgSpeed, int currentPart, int partsTotal) {
Log.i("dfu", "onProgressChanged");
Log.i("dfu", "onProgressChanged" + percent);
proBar.setProgress(percent);
proBar.setIndeterminate(false);
airUpgradeTv.setText(percent + "%");
}
@Override
public void onFirmwareValidating(String deviceAddress) {
Log.i("dfu", "onFirmwareValidating");
proBar.setIndeterminate(true);
airUpgradeTv.setText(R.string.dfu_status_validating);
}
@Override
public void onDeviceDisconnecting(String deviceAddress) {
Log.i("dfu", "onDeviceDisconnecting");
}
@Override
public void onDeviceDisconnected(String deviceAddress) {
Log.i("dfu", "onDeviceDisconnected");
proBar.setIndeterminate(true);
airUpgradeTv.setText(R.string.dfu_status_disconnecting);
}
@Override
public void onDfuCompleted(String deviceAddress) {
airUpgradeTv.setText(R.string.dfu_status_completed);
proBar.setProgress(100);
proBar.setIndeterminate(false);
//升級成功
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);
}
@Override
public void onDfuAborted(String deviceAddress) {
Log.i("dfu", "onDfuAborted");
//升級流產,失敗
airUpgradeTv.setText(R.string.dfu_status_aborted);
// 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() {
Toast.makeText(UpdateZipActivity.this, "升級出錯", Toast.LENGTH_SHORT).show();
// 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 onError(String deviceAddress, int error, int errorType, String message) {
Log.i("dfu", "onError");
airUpgradeTv.setText(getString(R.string.dfu_status_aborted));
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);
}
@Override
protected void onPause() {
//取消監聽
DfuServiceListenerHelper.unregisterProgressListener(this, mDfuProgressListener);
super.onPause();
}
