1. 程式人生 > >藍芽ble實現空中升級功能

藍芽ble實現空中升級功能

這陣子比較懶也比較忙,回到家裡就不想弄程式碼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和許可權。


好了。今天就到這裡,不知道什麼問題上傳不了效果圖,不好意思。

共勉!