藍芽ble實現空中升級功能
阿新 • • 發佈:2019-01-23
這陣子比較懶也比較忙,回到家裡就不想弄程式碼0.0 部落格也好久沒更新。實在抱歉。這陣子公司App需要實現空中升級的功能。在網上也找了好久毫無頭緒。偶然看到了nRF 工具箱的原始碼。翻了翻,終於把它實現了。今天就來說說空中升級的實現。
首先我們需要往專案的build.gradle檔案中匯入我們空中升級所需要的第三方包:
加入如下程式碼即可compile “no.nordicsemi.android:dfu:0.6.2”
然後我們先看下佈局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <RelativeLayout android:id="@+id/title" android:layout_width="match_parent" android:layout_height="@dimen/columnheight" android:background="@color/mainColor" > <RelativeLayout android:id="@+id/rl_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" > <ImageView android:id="@+id/imageview_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginTop="10dp" android:background="@drawable/back" android:layout_centerVertical="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="30dp" android:paddingBottom="5dp" android:paddingTop="5dp" android:layout_centerVertical="true" android:text="@string/account_title" android:textColor="@color/main_text_white" /> </RelativeLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/airUpgrade" android:textColor="@color/white" android:layout_centerInParent="true" android:textSize="@dimen/accountfragmentwidth" /> </RelativeLayout> <LinearLayout android:id="@+id/nameLayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_above="@+id/sizeLayout" android:layout_centerHorizontal="true"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/fileName"/> <TextView android:id="@+id/fileName" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:id="@+id/sizeLayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_above="@+id/proBar" android:layout_centerHorizontal="true" android:layout_marginBottom="10dp" android:layout_marginTop="10dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/fileSize"/> <TextView android:id="@+id/fileSize" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <ProgressBar android:id="@+id/proBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:layout_marginLeft="10dp" android:layout_centerInParent="true" style="@style/Widget.AppCompat.ProgressBar.Horizontal"/> <TextView android:id="@+id/airUpgradeTv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15sp" android:layout_below="@+id/proBar" android:layout_marginTop="15dp" android:text="@string/airUpgrade" android:layout_centerHorizontal="true"/> </RelativeLayout>
這裡只是一個顯示檔名和檔案大小的TextView 和一個progress進度條和一個開始空中升級的TextView。
這裡的話是點選開始升級,然後發指令給手環,之後手環返回確認升級的指令,收到之後才開始空中升級,這裡的話收到手環返回的指令我是用廣播來弄的。
具體看下面程式碼:
/** * Created by Administrator on 2016/11/15. */ public class AirUpgradeActivity extends BaseActivity { private static final String TAG = "AirUpgradeActivity"; private TextView fileName,fileSize,airUpgrade; private ProgressBar proBar; private String filePath; private String address,deviceName; private MySharePreference sp; private AirUpgradeReceiver receiver; //空中升級時的監聽 private final DfuProgressListener dfuProgressListener = new DfuProgressListener() { @Override public void onDeviceConnecting(String deviceAddress) { proBar.setIndeterminate(true); } //裝置開始連線 @Override public void onDeviceConnected(String deviceAddress) { proBar.setIndeterminate(true); } //升級準備開始的時候呼叫 @Override public void onDfuProcessStarting(String deviceAddress) { proBar.setIndeterminate(true); } //裝置開始升級 @Override public void onDfuProcessStarted(String deviceAddress) { proBar.setIndeterminate(true); } @Override public void onEnablingDfuMode(String deviceAddress) { proBar.setIndeterminate(true); } //升級過程中的回撥 @Override public void onProgressChanged(String deviceAddress, int percent, float speed, float avgSpeed, int currentPart, int partsTotal) { proBar.setIndeterminate(false); proBar.setProgress(percent); } //韌體驗證 @Override public void onFirmwareValidating(String deviceAddress) { } //裝置正在斷開 @Override public void onDeviceDisconnecting(String deviceAddress) { } //裝置已經斷開 @Override public void onDeviceDisconnected(String deviceAddress) { } //升級完成 @Override public void onDfuCompleted(String deviceAddress) { Toast.makeText(AirUpgradeActivity.this,getString(R.string.upgradesuccess),Toast.LENGTH_SHORT).show(); proBar.setIndeterminate(false); } @Override public void onDfuAborted(String deviceAddress) { proBar.setIndeterminate(false); } //升級失敗 @Override public void onError(String deviceAddress, int error, int errorType, String message) { Log.e(TAG,"ERROR"+error+"message"+message); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.air_upgrade_activity); getActionBar().hide(); //註冊空中升級的監聽 DfuServiceListenerHelper.registerProgressListener(this, dfuProgressListener); initView(); receiver = new AirUpgradeReceiver();//這裡是收到手環確認升級時的廣播監聽 IntentFilter filter = new IntentFilter(); filter.addAction("com.airupgrade.action"); registerReceiver(receiver, filter); sp = new MySharePreference(this); address = sp.getDeviceAddress(); deviceName = sp.getDeviceName(); try { copyBigDataToSD("/mnt/sdcard/KeeFit/IR16506084.zip");//把assets資料夾的韌體複製到手機記憶體 } catch (IOException e) { e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { try { int zipLength = getZipLength(); fileSize.setText(zipLength + "");//設定檔案的大小 } catch (IOException e) { e.printStackTrace(); } } }).start(); fileName.setText("IR16506084.zip"); } private void initView() { fileName = ((TextView) findViewById(R.id.fileName)); fileSize = ((TextView) findViewById(R.id.fileSize)); airUpgrade = ((TextView) findViewById(R.id.airUpgradeTv)); proBar = ((ProgressBar) findViewById(R.id.proBar)); airUpgrade.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e(TAG, "XXXXXXXX"); MyApplication.getInstance().mBlueToothService.setAirUpgrade(); } }); } /** * 得到韌體的檔案大小 * @return * @throws IOException */ public int getZipLength() throws IOException { int length ; InputStream is = getAssets().open("IR16506084.zip"); length = is.available(); Log.e(TAG,"is:"+is+"length"+length); return length; } //開始空中升級 class AirUpgradeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "開始空中升級" + address +" "+deviceName +""); final DfuServiceInitiator starter = new DfuServiceInitiator(address)//裝置地址 .setDeviceName(deviceName)//裝置名 .setKeepBond(true); starter.setZip("/mnt/sdcard/KeeFit/IR16506084.zip");//設定韌體 starter.start(AirUpgradeActivity.this, DfuService.class);//開始升級 } } //把zip韌體複製到手機儲存,之後再升級 private void copyBigDataToSD(String strOutFileName) throws IOException { Log.e(TAG,"copyBigDataToSD"); InputStream myInput; OutputStream myOutput = new FileOutputStream(strOutFileName); myInput = this.getAssets().open("IR16506084.zip"); byte[] buffer = new byte[1024]; int length = myInput.read(buffer); while(length > 0) { myOutput.write(buffer, 0, length); length = myInput.read(buffer); } myOutput.flush(); myInput.close(); myOutput.close(); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver);//取消註冊 } @Override protected void onPause() { super.onPause(); DfuServiceListenerHelper.unregisterProgressListener(this, dfuProgressListener);//取消監聽升級回撥 } }
具體的程式碼就是這樣,我這裡遇到一個問題就是無法取到assets資料夾下檔案的絕對路徑,而升級的時候需要的是絕對路徑,我嘗試過許多方法還是不行,最後只能複製到手機儲存然後再得到地址。
這裡開始升級的時候需要個DfuService。這個主要是升級時在介面上顯示個Notification通知進度條。下面我也貼下它們兩個的程式碼:
首先是DfuService:
然後是public class DfuService extends DfuBaseService { @Override protected Class<? extends Activity> getNotificationTarget() { return NotificationActivity.class; } }
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 parentIntent = new Intent(this, AirUpgradeActivity.class);
parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Intent startAppIntent = new Intent(this, AirUpgradeActivity.class);
startAppIntent.putExtras(getIntent().getExtras());
startActivities(new Intent[] { parentIntent, startAppIntent });
}
// Now finish, which will drop the user in to the activity that was at the top
// of the task stack
finish();
}
}
最後,別忘了在清單檔案配置DfuService和許可權。
好了。今天就到這裡,不知道什麼問題上傳不了效果圖,不好意思。
共勉!