android4.2 蓝牙 bluetooth第4节:OPP文件传输

  在前面 android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录下的代码,作为蓝牙最基本的一个功能,这部分的代码在之前的版本中就已经有了,新旧版本代码对比很多类名都是一样的,这一部分新东西不多,写在这里帮助大家梳理下流程吧。

        有没有这种感觉,智能手机的普及让我们提高了一点对蓝牙的关注,手机间使用蓝牙互传文件应该是最常用的应用之一,手机与电脑也可以通过蓝牙做同样的事情,大部分笔记本都支持蓝牙功能,本本上蓝牙芯片多数是broadcom的,也有其它厂商(比如东芝)不过数量不多,毕竟broadcom在BT这方面是老大。不过本本上蓝牙一般只支持蓝牙耳机听歌,并没实现对opp的支持,如果体验下手机与电脑的蓝牙文件传输怎么办呢,安装一个叫bluesoleil(中文名好像是千月)软件就可以了,这个软件对蓝牙功能的支持还是比较全的。可能需要卸载本本自带蓝牙驱动。扯淡结束,本文还是要关注手机间蓝牙opp的代码流程,这段的废话也许能帮助你提高下对蓝牙的体验。

        蓝牙发送文件时发送端先来到这里packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java,一个没有界面只是提取下文件信息的中转站,源码的注释写的很清楚了,两个分支action.equals(Intent.ACTION_SEND)和action.equals(Intent.ACTION_SEND_MULTIPLE)

  1.  if (action.equals(Intent.ACTION_SEND) || action.equals(Intent.ACTION_SEND_MULTIPLE)) {  
  2.             //Check if Bluetooth is available in the beginning instead of at the end   
  3.             if (!isBluetoothAllowed()) {  
  4.                 Intent in = new Intent(this, BluetoothOppBtErrorActivity.class);  
  5.                 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  6.                 in.putExtra("title"this.getString(R.string.airplane_error_title));  
  7.                 in.putExtra("content"this.getString(R.string.airplane_error_msg));  
  8.                 startActivity(in);  
  9.                 finish();  
  10.                 return;  
  11.             }  
  12.             if (action.equals(Intent.ACTION_SEND)) {  
  13.                .......   
  14.                Thread t = new Thread(new Runnable() {  
  15.                             public void run() {  
  16.                                 BluetoothOppManager.getInstance(BluetoothOppLauncherActivity.this)  
  17.                                     .saveSendingFileInfo(type,fileUri.toString(), false);  
  18.                                 //Done getting file info..Launch device picker   
  19.                                 //and finish this activity   
  20.                                 launchDevicePicker();  
  21.                                 finish();  
  22.                             }  
  23.                         });  ......           
  24.             } else if (action.equals(Intent.ACTION_SEND_MULTIPLE)) {  
  25.               .......                     
  26.             }  
 if (action.equals(Intent.ACTION_SEND) || action.equals(Intent.ACTION_SEND_MULTIPLE)) {
            //Check if Bluetooth is available in the beginning instead of at the end
            if (!isBluetoothAllowed()) {
                Intent in = new Intent(this, BluetoothOppBtErrorActivity.class);
                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                in.putExtra("title", this.getString(R.string.airplane_error_title));
                in.putExtra("content", this.getString(R.string.airplane_error_msg));
                startActivity(in);
                finish();
                return;
            }
            if (action.equals(Intent.ACTION_SEND)) {
               ....... 
               Thread t = new Thread(new Runnable() {
                            public void run() {
                                BluetoothOppManager.getInstance(BluetoothOppLauncherActivity.this)
                                    .saveSendingFileInfo(type,fileUri.toString(), false);
                                //Done getting file info..Launch device picker
                                //and finish this activity
                                launchDevicePicker();
                                finish();
                            }
                        });  ......         
            } else if (action.equals(Intent.ACTION_SEND_MULTIPLE)) {
              .......                   
            }

           最前面那个isBluetoothAllowed()会判断是否处于飞行模式,如果是会禁止发送的。在launchDevicePicker()里还会判断蓝牙是否已经打开,就是下面这个条件语句(!BluetoothOppManager.getInstance(this).isEnabled())。如果已经打开了蓝牙,如果蓝牙打开了就进入设备选择界面DeviceListPreferenceFragment(DevicePickerFragment)选择设备,这个跳转过程简单说明下,注意这个new Intent(BluetoothDevicePicker.ACTION_LAUNCH)里字符串,完整定义public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH";路径frameworks/base/core/java/android/bluetooth/BluetoothDevicePicker.java,你会在setting应用的manifest.xml里发现

  1. <activity android:name=".bluetooth.DevicePickerActivity"  
  2.                 android:theme="@android:style/Theme.Holo.DialogWhenLarge"  
  3.                 android:label="@string/device_picker"  
  4.                 android:clearTaskOnLaunch="true">  
  5.             <intent-filter>  
  6.                 <action android:name="android.bluetooth.devicepicker.action.LAUNCH" />  
  7.                 <category android:name="android.intent.category.DEFAULT" />  
  8.             </intent-filter>  
  9.         </activity>  
<activity android:name=".bluetooth.DevicePickerActivity"
                android:theme="@android:style/Theme.Holo.DialogWhenLarge"
                android:label="@string/device_picker"
                android:clearTaskOnLaunch="true">
            <intent-filter>
                <action android:name="android.bluetooth.devicepicker.action.LAUNCH" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
          这样目标就指向了DevicePickerActivity,注意此时它的代码路径是packages/apps/Settings/src/com/android/settings/bluetooth/DevicePickerActivity.java,这个类代码很简单,只有一个onCreate并只在里加载了一个布局文件bluetooth_device_picker.xml,就是这个布局文件指明下一站在哪,看下面就知道怎么来到DevicePickerFragment了

  1. <fragment  
  2.         android:id="@+id/bluetooth_device_picker_fragment"  
  3.         android:name="com.android.settings.bluetooth.DevicePickerFragment"  
  4.         android:layout_width="match_parent"  
  5.         android:layout_height="0dip"  
  6.         android:layout_weight="1" />  
<fragment
        android:id="@+id/bluetooth_device_picker_fragment"
        android:name="com.android.settings.bluetooth.DevicePickerFragment"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />
          到了这里,已经可看到配对过的蓝牙列表了,选择其中一个点击会来到这里,里面那个sendDevicePickedIntent是我们关心的,又发了一个广播,去找谁收了广播就好了

  1.     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {  
  2.         mLocalAdapter.stopScanning();  
  3.         LocalBluetoothPreferences.persistSelectedDeviceInPicker(  
  4.                 getActivity(), mSelectedDevice.getAddress());  
  5.         if ((btPreference.getCachedDevice().getBondState() ==  
  6.                 BluetoothDevice.BOND_BONDED) || !mNeedAuth) {  
  7.             sendDevicePickedIntent(mSelectedDevice);  
  8.             finish();  
  9.         } else {  
  10.             super.onDevicePreferenceClick(btPreference);  
  11.         }  
  12.     }<DIV>    public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH";  
  13.          private void sendDevicePickedIntent(BluetoothDevice device) {  
  14.          Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);  
  15.          intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);  
  16.          if (mLaunchPackage != null && mLaunchClass != null) {  
  17.              intent.setClassName(mLaunchPackage, mLaunchClass);  
  18.          }  
  19.         getActivity().sendBroadcast(intent);}  
  20. </DIV>  
    void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
        mLocalAdapter.stopScanning();
        LocalBluetoothPreferences.persistSelectedDeviceInPicker(
                getActivity(), mSelectedDevice.getAddress());
        if ((btPreference.getCachedDevice().getBondState() ==
                BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
            sendDevicePickedIntent(mSelectedDevice);
            finish();
        } else {
            super.onDevicePreferenceClick(btPreference);
        }
    }
  
  
public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH";     private void sendDevicePickedIntent(BluetoothDevice device) {     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);}
        通过BluetoothDevicePicker.ACTION_DEVICE_SELECTED查找,会在/packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppReceiver.java这个找到对该广播的处理,也就是下面的代码:

  1. else if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {  
  2.           BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(context);           
  3.           BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);   
  4.            
  5.           // Insert transfer session record to database   
  6.           mOppManager.startTransfer(remoteDevice);  
  7.            
  8.           // Display toast message   
  9.           String deviceName = mOppManager.getDeviceName(remoteDevice);  
  10.           .......  
  11. }  
else if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {
          BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(context);         
          BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 
         
          // Insert transfer session record to database
          mOppManager.startTransfer(remoteDevice);
         
          // Display toast message
          String deviceName = mOppManager.getDeviceName(remoteDevice);
          .......
}
        看来关键代码是mOppManager.startTransfer(remoteDevice),在packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppManager.java,里面开启线程执行发送动作,既然是开启线程,直接去看run方法就是了,方法里面依旧区分单个和多个文件的发送,看一个就可以。

  1.    public void startTransfer(BluetoothDevice device) {  
  2.         if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum);  
  3.         InsertShareInfoThread insertThread;  
  4.         synchronized (BluetoothOppManager.this) {  
  5.             if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) {  
  6.                 ...........  
  7.                 return;  
  8.             }  
  9.             insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile,  
  10.                     mUriOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles,  
  11.                     mIsHandoverInitiated);  
  12.             if (mMultipleFlag) {  
  13.                 mfileNumInBatch = mUrisOfSendingFiles.size();  
  14.             }  
  15.         }  
  16.         insertThread.start();  
  17.     }             
  18.     public void run() {  
  19.             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  20.             ..........  
  21.             if (mIsMultiple) {  
  22.                 insertMultipleShare();  
  23.             } else {  
  24.                 insertSingleShare();  
  25.             }  
  26.             .......... }  
   public void startTransfer(BluetoothDevice device) {
        if (V) Log.v(TAG, "Active InsertShareThread number is : " + mInsertShareThreadNum);
        InsertShareInfoThread insertThread;
        synchronized (BluetoothOppManager.this) {
            if (mInsertShareThreadNum > ALLOWED_INSERT_SHARE_THREAD_NUMBER) {
                ...........
                return;
            }
            insertThread = new InsertShareInfoThread(device, mMultipleFlag, mMimeTypeOfSendingFile,
                    mUriOfSendingFile, mMimeTypeOfSendingFiles, mUrisOfSendingFiles,
                    mIsHandoverInitiated);
            if (mMultipleFlag) {
                mfileNumInBatch = mUrisOfSendingFiles.size();
            }
        }
        insertThread.start();
    }           
    public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            ..........
            if (mIsMultiple) {
                insertMultipleShare();
            } else {
                insertSingleShare();
            }
            .......... }

       以insertSingleShare() 为例,在它的实现会看到mContext.getContentResolver().insert,不多想了,要去provider里找到insert()函数了,

对应的代码在BluetoothOppProvider.java (bluetooth\src\com\android\bluetooth\opp),insert的函数实现如下,里面又拉起BluetoothOppService,开始还以为只是针对数据库的操作,差点错过了风景。路径/packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppService.java

  1. public Uri insert(Uri uri, ContentValues values) {  
  2.  if (rowID != -1) {  
  3.      context.startService(new Intent(context, BluetoothOppService.class));  
  4.       ret = Uri.parse(BluetoothShare.CONTENT_URI + "/" + rowID);  
  5.      context.getContentResolver().notifyChange(uri, null);  
  6.  } else {  
  7.       if (D) Log.d(TAG, "couldn't insert into btopp database");  
  8.  }  
public Uri insert(Uri uri, ContentValues values) {
 if (rowID != -1) {
     context.startService(new Intent(context, BluetoothOppService.class));
      ret = Uri.parse(BluetoothShare.CONTENT_URI + "/" + rowID);
     context.getContentResolver().notifyChange(uri, null);
 } else {
      if (D) Log.d(TAG, "couldn't insert into btopp database");
 }

       在BluetoothOppService的onStartCommand方法中会看到updateFromProvider(),这里又开启了一个线程UpdateThread,后续代码当然是看它的run方法了,这里面内容不少,好在这部分代码注释比较多,理解起来不难。先暂时只关心发送的动作insertShare方法,代码也不少,只贴出了告诉我们接下来去哪里的代码和有关的逻辑注释,在下面的代码我们可以看到 BluetoothOppTransfer.java的对象,下一站就是它了。

  1. private void insertShare(Cursor cursor, int arrayPos) {  
  2.     .........  
  3.     /* 
  4.      * Add info into a batch. The logic is 
  5.      * 1) Only add valid and readyToStart info 
  6.      * 2) If there is no batch, create a batch and insert this transfer into batch, 
  7.      * then run the batch 
  8.      * 3) If there is existing batch and timestamp match, insert transfer into batch 
  9.      * 4) If there is existing batch and timestamp does not match, create a new batch and 
  10.      * put in queue 
  11.      */  
  12.     if (info.isReadyToStart()) {  
  13.         .............  
  14.         if (mBatchs.size() == 0) {  
  15.            ........  
  16.             mBatchs.add(newBatch);  
  17.             if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {  
  18.                  mTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch);  
  19.             } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {  
  20.                 mServerTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch,  
  21.                         mServerSession);  
  22.             }  
  23.   
  24.             if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND && mTransfer != null) {  
  25.                 mTransfer.start();  
  26.             } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND  
  27.                     && mServerTransfer != null) {  
  28.                 mServerTransfer.start();  
  29.             }  
  30.   
  31.         } else {  
  32.             .........  
  33.     }}  
    private void insertShare(Cursor cursor, int arrayPos) {
        .........
        /*
         * Add info into a batch. The logic is
         * 1) Only add valid and readyToStart info
         * 2) If there is no batch, create a batch and insert this transfer into batch,
         * then run the batch
         * 3) If there is existing batch and timestamp match, insert transfer into batch
         * 4) If there is existing batch and timestamp does not match, create a new batch and
         * put in queue
         */
        if (info.isReadyToStart()) {
            .............
            if (mBatchs.size() == 0) {
               ........
                mBatchs.add(newBatch);
                if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
                     mTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch);
                } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {
                    mServerTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch,
                            mServerSession);
                }

                if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND && mTransfer != null) {
                    mTransfer.start();
                } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND
                        && mServerTransfer != null) {
                    mServerTransfer.start();
                }

            } else {
                .........
        }}
        虽然名字是start(),可实际并不是什么线程的,就是一普通方法的,路径是/packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppTransfer.java

  1. public void start() {  
  2.       ....这里省略未贴的代码是检查蓝牙是否打开,一个很谨慎的判断。看似无用,不过还是安全第一。  
  3.   
  4.       if (mHandlerThread == null) {  
  5.           ........  
  6.           if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {  
  7.               /* for outbound transfer, we do connect first */  
  8.               startConnectSession();  
  9.           } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {  
  10.               /* 
  11.                * for inbound transfer, it's already connected, so we start 
  12.                * OBEX session directly 
  13.                */  
  14.               startObexSession();  
  15.           }  
  16.       }  
  17.   }  
  public void start() {
        ....这里省略未贴的代码是检查蓝牙是否打开,一个很谨慎的判断。看似无用,不过还是安全第一。

        if (mHandlerThread == null) {
            ........
            if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
                /* for outbound transfer, we do connect first */
                startConnectSession();
            } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
                /*
                 * for inbound transfer, it's already connected, so we start
                 * OBEX session directly
                 */
                startObexSession();
            }
        }
    }

        上面的代码是分发送文件和接收文件的,看下这两行代码就很清楚了,如果分享给别人是OUTBOUND,先执行startConnectSession(),这个函数最后还是要跑到startObexSession()这里的,如果收文件直接startObexSession,所以后面就只看startObexSession方法了

  1. // This transfer is outbound, e.g. share file to other device.   
  2.  public static final int DIRECTION_OUTBOUND = 0;  
  3.  // This transfer is inbound, e.g. receive file from other device.   
  4.  public static final int DIRECTION_INBOUND = 1;  
   // This transfer is outbound, e.g. share file to other device.
    public static final int DIRECTION_OUTBOUND = 0;
    // This transfer is inbound, e.g. receive file from other device.
    public static final int DIRECTION_INBOUND = 1;
       还是在同一个类里,发送流程快结束了,同样区分是传入还是传出,发文件看OUTBOUND,去BluetoothOppObexClientSession.java

  1. private void startObexSession() {       
  2.       if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {  
  3.           if (V) Log.v(TAG, "Create Client session with transport " + mTransport.toString());  
  4.           mSession = new BluetoothOppObexClientSession(mContext, mTransport);  
  5.       } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {           
  6.           if (mSession == null) {  
  7.                markBatchFailed();  
  8.               mBatch.mStatus = Constants.BATCH_STATUS_FAILED;  
  9.               return;  
  10.           }  
  11.           if (V) Log.v(TAG, "Transfer has Server session" + mSession.toString());  
  12.       }  
  13.       mSession.start(mSessionHandler);  
  14.       processCurrentShare();  
  15.   }  
  private void startObexSession() {     
        if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
            if (V) Log.v(TAG, "Create Client session with transport " + mTransport.toString());
            mSession = new BluetoothOppObexClientSession(mContext, mTransport);
        } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {         
            if (mSession == null) {
                 markBatchFailed();
                mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
                return;
            }
            if (V) Log.v(TAG, "Transfer has Server session" + mSession.toString());
        }
        mSession.start(mSessionHandler);
        processCurrentShare();
    }
       同样名字是start,实际只是一个普通方法而已,会看又是一个线程  mThread = new ClientThread( mContext, mTransport),这时的start才是线程的start(),还是看run方法,一些线程状态的判断,看到doSend() 就是了,直正的发送在这里packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java,

  1. private void doSend() {  
  2.   
  3.      int status = BluetoothShare.STATUS_SUCCESS;  
  4.      ........关于status值的判断  
  5.      if (status == BluetoothShare.STATUS_SUCCESS) {  
  6.          /* do real send */ //看到这个注释了没,它才是真家伙sendFile   
  7.          if (mFileInfo.mFileName != null) {  
  8.              status = sendFile(mFileInfo);  
  9.          } else {  
  10.              /* this is invalid request */  
  11.              status = mFileInfo.mStatus;  
  12.          }  
  13.          waitingForShare = true;  
  14.      } else {  
  15.          Constants.updateShareStatus(mContext1, mInfo.mId, status);  
  16.      }  
  17.   
  18.      if (status == BluetoothShare.STATUS_SUCCESS) {  
  19.          Message msg = Message.obtain(mCallback);  
  20.          msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE;  
  21.          msg.obj = mInfo;  
  22.          msg.sendToTarget();  
  23.      } else {  
  24.          Message msg = Message.obtain(mCallback);  
  25.          msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR;  
  26.          mInfo.mStatus = status;  
  27.          msg.obj = mInfo;  
  28.          msg.sendToTarget();  
  29.      }  
  30.  }  
       private void doSend() {

            int status = BluetoothShare.STATUS_SUCCESS;
            ........关于status值的判断
            if (status == BluetoothShare.STATUS_SUCCESS) {
                /* do real send */ //看到这个注释了没,它才是真家伙sendFile
                if (mFileInfo.mFileName != null) {
                    status = sendFile(mFileInfo);
                } else {
                    /* this is invalid request */
                    status = mFileInfo.mStatus;
                }
                waitingForShare = true;
            } else {
                Constants.updateShareStatus(mContext1, mInfo.mId, status);
            }

            if (status == BluetoothShare.STATUS_SUCCESS) {
                Message msg = Message.obtain(mCallback);
                msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE;
                msg.obj = mInfo;
                msg.sendToTarget();
            } else {
                Message msg = Message.obtain(mCallback);
                msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR;
                mInfo.mStatus = status;
                msg.obj = mInfo;
                msg.sendToTarget();
            }
        }
        sendFile是真正干活的,执行完sendFile会把分享成功或失败的消息传回去,sendFile里会执行打包的过程,对于字段的含义要看Headset.java,

代码路径在frameworks/base/obex/javax/obex/HeaderSet.java。这个sendFile方法行数虽然多,不过逻辑还是比较清晰的,在这里就不贴了。到这蓝牙发送文件流程也就此结束。由于发送文件时长肯定是不确定,所以在这个流程我们看到了很多开启线程代码也是很正常的,对于这线程,直接看对应的run方法就是了。

        对于蓝牙接收文件时会收到MSG_INCOMING_BTOPP_CONNECTION消息,收到这个消息是由于在蓝牙打开,即蓝牙状态是 BluetoothAdapter.STATE_ON时会执行

startSocketListener(),在这个函数开启了监听程序,看下面贴在一起的代码就明白了,

  1. if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {  
  2.     switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {  
  3.         case BluetoothAdapter.STATE_ON:  
  4.             if (V) Log.v(TAG,"Receiver BLUETOOTH_STATE_CHANGED_ACTION, BLUETOOTH_STATE_ON");  
  5.             startSocketListener();  
  6.             break;  
  7.                           
  8. private void startSocketListener() {  
  9.         if (V) Log.v(TAG, "start RfcommListener");  
  10.         mSocketListener.start(mHandler);  
  11.         if (V) Log.v(TAG, "RfcommListener started");  
  12. }  
  13. mSocketListener.start(mHandler);这个的实现在这里,比较长,没有贴上来     
  14. /packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java  
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
    switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
        case BluetoothAdapter.STATE_ON:
            if (V) Log.v(TAG,"Receiver BLUETOOTH_STATE_CHANGED_ACTION, BLUETOOTH_STATE_ON");
            startSocketListener();
            break;
                        
private void startSocketListener() {
        if (V) Log.v(TAG, "start RfcommListener");
        mSocketListener.start(mHandler);
        if (V) Log.v(TAG, "RfcommListener started");
}
mSocketListener.start(mHandler);这个的实现在这里,比较长,没有贴上来   
/packages/apps/Bluetooth/src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java
        回到上面处理消息,在BluetoothOppService.java的handlemessage中这个分支 case BluetoothOppRfcommListener.MSG_INCOMING_BTOPP_CONNECTION, 创建一个 createServerSession(transport); 最后走/frameworks/base/obex/javax/obex/ServerSession.java的run方法中接收数据

  1. private void createServerSession(ObexTransport transport) {  
  2.      mServerSession = new BluetoothOppObexServerSession(this, transport);  
  3.      mServerSession.preStart();       
  4.  }  
   private void createServerSession(ObexTransport transport) {
        mServerSession = new BluetoothOppObexServerSession(this, transport);
        mServerSession.preStart();     
    }
        对于蓝牙接收文件部分的流程还没有细致的跟踪,暂时只看到这里,对于了解基本流程这此应该够用了,同时如果想更好理解蓝牙OPP文件传输,了解是OBEX基础协议也是有必要的,网上资料还是有不少的,多数是论文形式的。对于蓝牙OPP部分,本文只是描述android代码中的流程,旨在帮你快速的理清流程,本文对OPP本身并没有深入,相关的知识需要进一步学习才行,有同道先行的童鞋还望赐教一二,谢谢。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值