最近这段时间,领导在谈北美项目,如果前期的谈判完成了,我们这边估计就要大忙了,有时候想想,到了差不多四十多岁的时候,如果能搞到像我们老大那样子,手下百十号人,完全可以基于原生的东西进行一番改造,弄出一个OS了,这样的职业生涯应该还算可以吧!当然这里所说的OS其实本质还不是真正的OS,就像华为的EMUI、小米的MIUI一样,只是对界面、控件样式进行修改的系统了。
上一次处理那个Beam分享的问题单,自己也一直没完全放下,Beam最终使用的还是蓝牙的传输功能,上一节课我们也分析了Okhttp文件上传的全过程,可以看到,在底层的实现就是通过Socket来实现的,那蓝牙底层是如何实现这整个过程的呢?今天我们就带着这个问题来看看蓝牙模块文件传输的全过程。
下面的流程图是我整理出来的,可以看到整个过程非常长,但是看看代码就会发现,其实这里的调用逻辑还是比较简单的,基本上都是一些封装。真下执行文件传输的地方是在BluetoothOppObexClientSession的内部类ClientThread的sendFile方法中完成的,流程图中也用红色标注出来了,我的流程图是用visio画的,如果大家感觉图片看不清,也可以去下载我的源文件,地址如下:
那么接下来,我们就一起来看看中间的调用过程。
关于蓝牙这块的东西,肯定还会涉及到硬件方面的东西,各种传输协议什么的,在网上查了下,下面的博客也写的非常好,推荐大家看一下:
Android bluetooth介绍(一):基本概念及硬件接口
那么在开始之前,我们还需要说一点,我们从图库中执行文件分享,传递到蓝牙模块的是一个文件的Uri,在传送前,目标图片的uri已经在BluetoothOppReceiver类的BluetoothAdapter.ACTION_STATE_CHANGED广播当中接收进来了,接收到要传送的uri数据后,调用BluetoothOppManager类的storeApplicationData方法将数据保存在了OPP_PREFERENCE_FILE文件SharedPreferences当中。大家将蓝牙模块的data目录下com.android.bluetooth完整的目录pull出来,看一下它的SharedPreferences。
目标文件的Uri就保存在OPPMGR文件当中,打开它,可以看到内容如下:
FILE_URI就是图库进程传递过来的目标文件的Uri,那么我们下面要作的事情就是根据这个Uri,把文件读取出来,解析成流,然后通过Socket发送出去。我们开始的操作是在蓝牙选择界面,会显示所有的可见的蓝牙设备,这个界面对应的是packages/apps/Settings/src/com/android/settings/bluetooth/DevicePickerFragment类,当我们点击一个设备条目后,会执行onDevicePreferenceClick方法响应点击,这个方法是调用sendDevicePickedIntent方法来发送一个BluetoothDevicePicker.ACTION_DEVICE_SELECTED广播的,sendDevicePickedIntent方法的实现如下:
private void sendDevicePickedIntent(BluetoothDevice device) {
mDeviceSelected = true;
Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
if (mLaunchPackage != null && mLaunchClass != null) {
intent.setClassName(mLaunchPackage, mLaunchClass);
}
getActivity().sendBroadcast(intent);
}
public void run() {
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
if (ActivityThread.DEBUG_BROADCAST) {
int seq = mCurIntent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
+ " seq=" + seq + " to " + mReceiver);
Slog.i(ActivityThread.TAG, " mRegistered=" + mRegistered
+ " mOrderedHint=" + ordered);
}
final IActivityManager mgr = ActivityManagerNative.getDefault();
final Intent intent = mCurIntent;
mCurIntent = null;
if (receiver == null || mForgotten) {
if (mRegistered && ordered) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing null broadcast to " + mReceiver);
sendFinished(mgr);
}
return;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
if (mRegistered && ordered) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing failed broadcast to " + mReceiver);
sendFinished(mgr);
}
if (mInstrumentation == null ||
!mInstrumentation.onException(mReceiver, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Error receiving broadcast " + intent
+ " in " + mReceiver, e);
}
}
if (receiver.getPendingResult() != null) {
finish();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
if (mRemoteDevice == null) {
Log.e(TAG, "Target bt device is null!");
return;
}
if (mIsMultiple) {
insertMultipleShare();
} else {
insertSingleShare();
}
synchronized (BluetoothOppManager.this) {
mInsertShareThreadNum--;
}
}
我们打开蓝牙目录当中的数据库,可以看到这里保存了所有我们传输过的文件信息。保存完成后,那么系统就会回调BluetoothOppService.BluetoothShareContentObserver对象的onChange方法了,通知它数据已经变化了,关于ContentProvider数据更新