这阵子比较懒也比较忙,回到家里就不想弄代码0.0 博客也好久没更新。实在抱歉。这阵子公司App需要实现空中升级的功能。在网上也找了好久毫无头绪。偶然看到了nRF 工具箱的源码。翻了翻,终于把它实现了。今天就来说说空中升级的实现。
首先我们需要往项目的build.gradle文件中导入我们空中升级所需要的第三方包:
加入如下代码即可compile “no.nordicsemi.android:dfu:0.6.2”
然后我们先看下布局
这里的话是点击开始升级,然后发指令给手环,之后手环返回确认升级的指令,收到之后才开始空中升级,这里的话收到手环返回的指令我是用广播来弄的。
具体看下面代码:
/**
* 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();
}
}
好了。今天就到这里,不知道什么问题上传不了效果图,不好意思。
共勉!