相关参考网站:http://wenku.baidu.com/view/d901cc2d4b73f242336c5f87.html

                  

http://wenku.baidu.com/view/518414b069dc5022aaea007b.html?from=rec&pos=2&weight=167&lastweight=111&count=5

http://wenku.baidu.com/view/dec9bddd5022aaea998f0f7f.html

测试蓝牙程序需要至少2个真机,然而Android 2.0以后才真正支持蓝牙设备编程。为了给这一讲配图,我要先去把HTC G4 刷一下机,经过2天的折腾,终于把G4刷到Android 2.2版本了…… 开始写本讲……

image

蓝牙是一种设备短距离无线通信技术,使用蓝牙你可以搜索并连接到附近的蓝牙设备,可以在两个已经进行过配对的蓝牙设备之间进行数据传输。

本讲我们先使用Android SDK自带的例子蓝牙聊天程序BluetoothChat来做演示。源代码具体位置\android-sdk-windows\samples\android-8\BluetoothChat目录下。然后再自己实现一边这个蓝牙聊天程序。

一、SDK自带例子,BluetoothChat 的运行演示

这一次我们先看一下运行效果:准备两个真机 Milestone 和 HTC G4 Tattoo,分别安装好Bluetooth程序,
先在 Milestone上点击 BluetoothChat 运行程序,

image

提示需要蓝牙使用权限

image

选择是,打开蓝牙,进入聊天界面,只是此时还未连接任何蓝牙设备。在HTC G4上进行同样的操作,也进入not connected 状态。

image

点击Menu按钮呼出菜单

image

点击 Make discoverable 菜单使,并在弹出的权限请求对话框中选择是,使手机可以被其它蓝牙设备发现,G4也做同样操作

image

然后选择connect a device,在弹出的对话框中选择 Scan for devices 查找设备,会发现 HTC Tattoo 设备

image

image

image

image

全部选择匹配,然后就连接成功了。

image

image

这样就可以聊天了……

MainFest xml

Xhtml代码 复制代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!-- Copyright (C) 2009 The Android Open Source Project   
  3.      Licensed under the Apache License, Version 2.0 (the "License");   
  4.      you may not use this file except in compliance with the License.   
  5.      You may obtain a copy of the License at   
  6.           http://www.apache.org/licenses/LICENSE-2.0   
  7.      Unless required by applicable law or agreed to in writing, software   
  8.      distributed under the License is distributed on an "AS IS" BASIS,   
  9.      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   
  10.      See the License for the specific language governing permissions and   
  11.      limitations under the License.   
  12. -->  
  13. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  14.       package="com.example.android.BluetoothChat"  
  15.       android:versionCode="1"  
  16.       android:versionName="1.0">  
  17.     <uses-sdk minSdkVersion="6" />  
  18.     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />  
  19.     <uses-permission android:name="android.permission.BLUETOOTH" />  
  20.     <!-- 获取权限BlueTooth-->  
  21.     <application android:label="@string/app_name"  
  22.                  android:icon="@drawable/app_icon" >  
  23.         <activity android:name=".BluetoothChat"  
  24.                   android:label="@string/app_name"  
  25.                   android:configChanges="orientation|keyboardHidden">  
  26.             <intent-filter>  
  27.                 <action android:name="android.intent.action.MAIN" />  
  28.                 <category android:name="android.intent.category.LAUNCHER" />  
  29.             </intent-filter>  
  30.         </activity>  
  31.         <activity android:name=".DeviceListActivity"  
  32.                   android:label="@string/select_device"  
  33.                   android:theme="@android:style/Theme.Dialog"                 
  34.                   android:configChanges="orientation|keyboardHidden" />  
  35.                       <!-- android:theme="@android:style/Theme.Dialog" 设置界面主题:弹出框主题 -->  
  36.     </application>  
  37. </manifest>  

 

Java代码 复制代码
  1. /*  
  2.  * Copyright (C) 2009 The Android Open Source Project  
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License");  
  5.  * you may not use this file except in compliance with the License.  
  6.  * You may obtain a copy of the License at  
  7.  *  
  8.  *      http://www.apache.org/licenses/LICENSE-2.0  
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software  
  11.  * distributed under the License is distributed on an "AS IS" BASIS,  
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.  * See the License for the specific language governing permissions and  
  14.  * limitations under the License.  
  15.  */  
  16. package com.example.android.BluetoothChat;   
  17. import android.app.Activity;   
  18. import android.bluetooth.BluetoothAdapter;   
  19. import android.bluetooth.BluetoothDevice;   
  20. import android.content.Intent;   
  21. import android.os.Bundle;   
  22. import android.os.Handler;   
  23. import android.os.Message;   
  24. import android.util.Log;   
  25. import android.view.KeyEvent;   
  26. import android.view.Menu;   
  27. import android.view.MenuInflater;   
  28. import android.view.MenuItem;   
  29. import android.view.View;   
  30. import android.view.Window;   
  31. import android.view.View.OnClickListener;   
  32. import android.view.inputmethod.EditorInfo;   
  33. import android.widget.ArrayAdapter;   
  34. import android.widget.Button;   
  35. import android.widget.EditText;   
  36. import android.widget.ListView;   
  37. import android.widget.TextView;   
  38. import android.widget.Toast;   
  39. /**  
  40.  * This is the main Activity that displays the current chat session.  
  41.  */  
  42. public class BluetoothChat extends Activity {   
  43.     // Debugging   
  44.     private static final String TAG = "BluetoothChat";   
  45.     private static final boolean D = true;   
  46.     // Message types sent from the BluetoothChatService Handler   
  47.     public static final int MESSAGE_STATE_CHANGE = 1;   
  48.     public static final int MESSAGE_READ = 2;   
  49.     public static final int MESSAGE_WRITE = 3;   
  50.     public static final int MESSAGE_DEVICE_NAME = 4;   
  51.     public static final int MESSAGE_TOAST = 5;   
  52.     // Key names received from the BluetoothChatService Handler   
  53.     public static final String DEVICE_NAME = "device_name";   
  54.     public static final String TOAST = "toast";   
  55.     // Intent request codes   
  56.     private static final int REQUEST_CONNECT_DEVICE = 1;   
  57.     private static final int REQUEST_ENABLE_BT = 2;   
  58.     // Layout Views   
  59.     private TextView mTitle;   
  60.     private ListView mConversationView;   
  61.     private EditText mOutEditText;   
  62.     private Button mSendButton;   
  63.     // Name of the connected device   
  64.     private String mConnectedDeviceName = null;   
  65.     // Array adapter for the conversation thread   
  66.     private ArrayAdapter<String> mConversationArrayAdapter;   
  67.     // String buffer for outgoing messages   
  68.     private StringBuffer mOutStringBuffer;   
  69.     // Local Bluetooth adapter   
  70.     private BluetoothAdapter mBluetoothAdapter = null;   
  71.     // Member object for the chat services   
  72.     private BluetoothChatService mChatService = null;   
  73.   
  74.     @Override  
  75.     public void onCreate(Bundle savedInstanceState) {   
  76.         super.onCreate(savedInstanceState);   
  77.         if(D) Log.e(TAG, "+++ ON CREATE +++");   
  78.         // Set up the window layout 设置Activity的标题栏格式Window.FEATURE_CUSTOM_TITLE普通、Window.FEATURE_NO_TITLE无标题栏、Window.FEATURE_INDETERMINATE_PROGRESS进度条   
  79.         requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);   
  80.         setContentView(R.layout.main);   
  81.         //设置标题栏的布局资源   
  82.         getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);   
  83.            
  84.         // Set up the custom title   
  85.         mTitle = (TextView) findViewById(R.id.title_left_text);   
  86.         mTitle.setText(R.string.app_name);   
  87.         mTitle = (TextView) findViewById(R.id.title_right_text);   
  88.         // Get local Bluetooth adapter   
  89.        //从根本上讲,这是你的出发点,所有的蓝牙行动。一旦你的本地适配器,getBondedDevices()所有配对设备对象设置;   
  90.         //与startDiscovery()启动设备可被发现,或建立一个BluetoothServerSocket监听与listenUsingRfcommWithServiceRecord(字符串,UUID的)传入的连接请求。   
  91.         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();   
  92.         // If the adapter is null, then Bluetooth is not supported   
  93.         if (mBluetoothAdapter == null) {   
  94.             Toast.makeText(this"Bluetooth is not available", Toast.LENGTH_LONG).show();   
  95.             finish();   
  96.             return;   
  97.         }   
  98.     }   
  99.     @Override  
  100.     public void onStart() {   
  101.         super.onStart();   
  102.         if(D) Log.e(TAG, "++ ON START ++");   
  103.         // If BT is not on, request that it be enabled.   
  104.         // setupChat() will then be called during onActivityResult   
  105.         //判断蓝牙是否开启,否则请求开启   
  106.         if (!mBluetoothAdapter.isEnabled()) {   
  107.             Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);   
  108.             startActivityForResult(enableIntent, REQUEST_ENABLE_BT);   
  109.         // Otherwise, setup the chat session   
  110.         } else {   
  111.             if (mChatService == null) setupChat();   
  112.         }   
  113.     }   
  114.     @Override  
  115.     public synchronized void onResume() {   
  116.         super.onResume();   
  117.         if(D) Log.e(TAG, "+ ON RESUME +");   
  118.         // Performing this check in onResume() covers the case in which BT was   
  119.         // not enabled during onStart(), so we were paused to enable it...   
  120.         // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.   
  121.         if (mChatService != null) {   
  122.             // Only if the state is STATE_NONE, do we know that we haven't started already   
  123.             if (mChatService.getState() == BluetoothChatService.STATE_NONE) { //STATE_NONE及刚创建为启动状态,如果是从后台切换到前台应为STATE_LISTEN,无需再次启动   
  124.               // Start the Bluetooth chat services    
  125.               mChatService.start();   
  126.             }   
  127.         }   
  128.     }   
  129.     private void setupChat() {   
  130.         Log.d(TAG, "setupChat()");   
  131.         // Initialize the array adapter for the conversation thread    
  132.         mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);   
  133.         mConversationView = (ListView) findViewById(R.id.in);   
  134.         mConversationView.setAdapter(mConversationArrayAdapter);   
  135.         // Initialize the compose field with a listener for the return key   
  136.         mOutEditText = (EditText) findViewById(R.id.edit_text_out);   
  137.         mOutEditText.setOnEditorActionListener(mWriteListener);////注册编辑状态监听   
  138.         // Initialize the send button with a listener that for click events   
  139.         mSendButton = (Button) findViewById(R.id.button_send);   
  140.         mSendButton.setOnClickListener(new OnClickListener() {   
  141.             public void onClick(View v) {   
  142.                 // Send a message using content of the edit text widget   
  143.                 TextView view = (TextView) findViewById(R.id.edit_text_out);   
  144.                 String message = view.getText().toString();   
  145.                 sendMessage(message); //点击发送则向蓝牙发送消息   
  146.             }   
  147.         });   
  148.         // Initialize the BluetoothChatService to perform bluetooth connections   
  149.         mChatService = new BluetoothChatService(this, mHandler);   
  150.         // Initialize the buffer for outgoing messages   
  151.         mOutStringBuffer = new StringBuffer("");   
  152.     }   
  153.     @Override  
  154.     public synchronized void onPause() {   
  155.         super.onPause();   
  156.         if(D) Log.e(TAG, "- ON PAUSE -");   
  157.     }   
  158.     @Override  
  159.     public void onStop() {   
  160.         super.onStop();   
  161.         if(D) Log.e(TAG, "-- ON STOP --");   
  162.     }   
  163.     @Override  
  164.     public void onDestroy() {   
  165.         super.onDestroy();   
  166.         // Stop the Bluetooth chat services   
  167.         if (mChatService != null) mChatService.stop(); //UI关闭时,蓝牙服务应也停止   
  168.         if(D) Log.e(TAG, "--- ON DESTROY ---");   
  169.     }   
  170.     private void ensureDiscoverable() {   
  171.         if(D) Log.d(TAG, "ensure discoverable");   
  172.         if (mBluetoothAdapter.getScanMode() !=   
  173.             BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { //如果蓝牙状态为不可被发现则请求开启为可被发现,时间300秒内   
  174.             Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);   
  175.             discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);   
  176.             startActivity(discoverableIntent);   
  177.         }   
  178.     }   
  179.     /**  
  180.      * Sends a message.  
  181.      * @param message  A string of text to send.  
  182.      */  
  183.     private void sendMessage(String message) {   
  184.         // Check that we're actually connected before trying anything   
  185.         if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {   
  186.             Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();   
  187.             return;   
  188.         }   
  189.         // Check that there's actually something to send   
  190.         if (message.length() > 0) {   
  191.             // Get the message bytes and tell the BluetoothChatService to write   
  192.             byte[] send = message.getBytes();   
  193.             mChatService.write(send);   
  194.             // Reset out string buffer to zero and clear the edit text field   
  195.             mOutStringBuffer.setLength(0); //mOutStringBuffer 一直未使用,何意?   
  196.             mOutEditText.setText(mOutStringBuffer);//清空编辑框 窃以为使用mOutEditText.setText("");即可   
  197.         }   
  198.     }   
  199.     // The action listener for the EditText widget, to listen for the return key   
  200.     private TextView.OnEditorActionListener mWriteListener =   
  201.         new TextView.OnEditorActionListener() {   
  202.         public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {   
  203.             // If the action is a key-up event on the return key, send the message    
  204.             //经测试应为点击回车键后放开时发送消息   
  205.             if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {   
  206.                 String message = view.getText().toString();   
  207.                 sendMessage(message);   
  208.             }   
  209.             if(D) Log.i(TAG, "END onEditorAction");   
  210.             return true;   
  211.         }   
  212.     };   
  213.     // The Handler that gets information back from the BluetoothChatService   
  214.     //更新标题栏右边状态和读写状态的Handler   
  215.     private final Handler mHandler = new Handler() {   
  216.         @Override  
  217.         public void handleMessage(Message msg) {   
  218.             switch (msg.what) {   
  219.             case MESSAGE_STATE_CHANGE:   
  220.                 if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);   
  221.                 switch (msg.arg1) {   
  222.                 case BluetoothChatService.STATE_CONNECTED:   
  223.                     mTitle.setText(R.string.title_connected_to);   
  224.                     mTitle.append(mConnectedDeviceName);   
  225.                     mConversationArrayAdapter.clear(); //清空消息   
  226.                     break;   
  227.                 case BluetoothChatService.STATE_CONNECTING:   
  228.                     mTitle.setText(R.string.title_connecting);   
  229.                     break;   
  230.                 case BluetoothChatService.STATE_LISTEN:   
  231.                 case BluetoothChatService.STATE_NONE:   
  232.                     mTitle.setText(R.string.title_not_connected);   
  233.                     break;   
  234.                 }   
  235.                 break;   
  236.             case MESSAGE_WRITE:   
  237.                 byte[] writeBuf = (byte[]) msg.obj;   
  238.                 // construct a string from the buffer   
  239.                 String writeMessage = new String(writeBuf);   
  240.                 mConversationArrayAdapter.add("Me:  " + writeMessage);   
  241.                 break;   
  242.             case MESSAGE_READ:   
  243.                 byte[] readBuf = (byte[]) msg.obj;   
  244.                 // construct a string from the valid bytes in the buffer   
  245.                 String readMessage = new String(readBuf, 0, msg.arg1);   
  246.                 mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);   
  247.                 break;   
  248.             case MESSAGE_DEVICE_NAME:   
  249.                 // save the connected device's name   
  250.                 mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);   
  251.                 Toast.makeText(getApplicationContext(), "Connected to "  
  252.                                + mConnectedDeviceName, Toast.LENGTH_SHORT).show();   
  253.                 break;   
  254.             case MESSAGE_TOAST:   
  255.                 Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),   
  256.                                Toast.LENGTH_SHORT).show();   
  257.                 break;   
  258.             }   
  259.         }   
  260.     };   
  261.     public void onActivityResult(int requestCode, int resultCode, Intent data) {   
  262.         if(D) Log.d(TAG, "onActivityResult " + resultCode);   
  263.         switch (requestCode) {   
  264.         case REQUEST_CONNECT_DEVICE:   
  265.             // When DeviceListActivity returns with a device to connect   
  266.             if (resultCode == Activity.RESULT_OK) {   
  267.                 // Get the device MAC address   
  268.                 String address = data.getExtras()   
  269.                                      .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);   
  270.                 // Get the BLuetoothDevice object   
  271.                 if(address==null){   
  272.                     break;   
  273.                 }   
  274.                 //获取设备进行连接   
  275.                 BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);   
  276.                 // Attempt to connect to the device   
  277.                 mChatService.connect(device);   
  278.             }   
  279.             break;   
  280.         case REQUEST_ENABLE_BT:   
  281.             // When the request to enable Bluetooth returns   
  282.             if (resultCode == Activity.RESULT_OK) {   
  283.                 // Bluetooth is now enabled, so set up a chat session   
  284.                 //蓝牙开启成功,则继续初始化UI   
  285.                 setupChat();   
  286.             } else {   
  287.                 // User did not enable Bluetooth or an error occured   
  288.                 Log.d(TAG, "BT not enabled");   
  289.                 Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();   
  290.                 finish();   
  291.             }   
  292.         }   
  293.     }   
  294.     @Override  
  295.     public boolean onCreateOptionsMenu(Menu menu) {   
  296.         //从Menu资源中初始化Menu 返回true则弹出默认menu,false则不弹出Menu,须自定义   
  297.         MenuInflater inflater = getMenuInflater();   
  298.         inflater.inflate(R.menu.option_menu, menu);   
  299.         return true;    
  300.     }   
  301.     @Override  
  302.     public boolean onOptionsItemSelected(MenuItem item) {   
  303.         switch (item.getItemId()) {   
  304.         case R.id.scan:   
  305.             // Launch the DeviceListActivity to see devices and do scan   
  306.             //启动DeviceListActivity,并等待返回   
  307.             Intent serverIntent = new Intent(this, DeviceListActivity.class);   
  308.             startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);   
  309.             return true;   
  310.         case R.id.discoverable:   
  311.             // Ensure this device is discoverable by others   
  312.             ensureDiscoverable();   
  313.             return true;   
  314.         }   
  315.         return false;   
  316.     }   
  317. }  

 

Java代码 复制代码
  1. /*  
  2.  * Copyright (C) 2009 The Android Open Source Project  
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License");  
  5.  * you may not use this file except in compliance with the License.  
  6.  * You may obtain a copy of the License at  
  7.  *  
  8.  *      http://www.apache.org/licenses/LICENSE-2.0  
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software  
  11.  * distributed under the License is distributed on an "AS IS" BASIS,  
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.  * See the License for the specific language governing permissions and  
  14.  * limitations under the License.  
  15.  */  
  16. package com.example.android.BluetoothChat;   
  17. import java.util.Set;   
  18. import android.app.Activity;   
  19. import android.bluetooth.BluetoothAdapter;   
  20. import android.bluetooth.BluetoothDevice;   
  21. import android.content.BroadcastReceiver;   
  22. import android.content.Context;   
  23. import android.content.Intent;   
  24. import android.content.IntentFilter;   
  25. import android.os.Bundle;   
  26. import android.util.Log;   
  27. import android.view.View;   
  28. import android.view.Window;   
  29. import android.view.View.OnClickListener;   
  30. import android.widget.AdapterView;   
  31. import android.widget.ArrayAdapter;   
  32. import android.widget.Button;   
  33. import android.widget.ListView;   
  34. import android.widget.TextView;   
  35. import android.widget.AdapterView.OnItemClickListener;   
  36. /**  
  37.  * This Activity appears as a dialog. It lists any paired devices and  
  38.  * devices detected in the area after discovery. When a device is chosen  
  39.  * by the user, the MAC address of the device is sent back to the parent  
  40.  * Activity in the result Intent.  
  41.  */  
  42. public class DeviceListActivity extends Activity {   
  43.     // Debugging   
  44.     private static final String TAG = "DeviceListActivity";   
  45.     private static final boolean D = true;   
  46.     // Return Intent extra   
  47.     public static String EXTRA_DEVICE_ADDRESS = "device_address";   
  48.     // Member fields   
  49.     private BluetoothAdapter mBtAdapter;   
  50.     private ArrayAdapter<String> mPairedDevicesArrayAdapter;   
  51.     private ArrayAdapter<String> mNewDevicesArrayAdapter;   
  52.     @Override  
  53.     protected void onCreate(Bundle savedInstanceState) {   
  54.         super.onCreate(savedInstanceState);   
  55.         // Setup the window   
  56.         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);   
  57.         setContentView(R.layout.device_list);   
  58.         // Set result CANCELED incase the user backs out   
  59.         //先设置为Canceled 防止点击回退键时BluetoothChat认为是Result_OK状态   
  60.         setResult(Activity.RESULT_CANCELED);   
  61.         // Initialize the button to perform device discovery   
  62.         Button scanButton = (Button) findViewById(R.id.button_scan);   
  63.         scanButton.setOnClickListener(new OnClickListener() {   
  64.             public void onClick(View v) {   
  65.                 doDiscovery();   
  66.                 v.setVisibility(View.GONE);   
  67.             }   
  68.         });   
  69.         // Initialize array adapters. One for already paired devices and   
  70.         // one for newly discovered devices   
  71.         mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);   
  72.         mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);   
  73.         // Find and set up the ListView for paired devices   
  74.         ListView pairedListView = (ListView) findViewById(R.id.paired_devices);   
  75.         pairedListView.setAdapter(mPairedDevicesArrayAdapter);   
  76.         pairedListView.setOnItemClickListener(mDeviceClickListener);   
  77.         // Find and set up the ListView for newly discovered devices   
  78.         ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);   
  79.         newDevicesListView.setAdapter(mNewDevicesArrayAdapter);   
  80.         newDevicesListView.setOnItemClickListener(mDeviceClickListener);   
  81.         // Register for broadcasts when a device is discovered 发现设备时监听过滤   
  82.         IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);   
  83.         this.registerReceiver(mReceiver, filter);   
  84.         // Register for broadcasts when discovery has finished发现设备程序运行结束时监听过滤   
  85.         filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);   
  86.         this.registerReceiver(mReceiver, filter);   
  87.         // Get the local Bluetooth adapter   
  88.         mBtAdapter = BluetoothAdapter.getDefaultAdapter();   
  89.         // Get a set of currently paired devices 获取当前已经互联过的设备   
  90.         Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();   
  91.         // If there are paired devices, add each one to the ArrayAdapter   
  92.         if (pairedDevices.size() > 0) {   
  93.             findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);    
  94.             for (BluetoothDevice device : pairedDevices) {   
  95.                 mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());   
  96.             }   
  97.         } else {   
  98.             String noDevices = getResources().getText(R.string.none_paired).toString();   
  99.             mPairedDevicesArrayAdapter.add(noDevices);   
  100.         }   
  101.     }   
  102.     @Override  
  103.     protected void onDestroy() {   
  104.         super.onDestroy();   
  105.         // Make sure we're not doing discovery anymore   
  106.         if (mBtAdapter != null) {   
  107.             mBtAdapter.cancelDiscovery(); //BluetoothAdapter 不受UI管理受系统管理,所以要即使cancelDiscovery   
  108.         }   
  109.         // Unregister broadcast listeners   
  110.         this.unregisterReceiver(mReceiver); //取消监听当UI销毁时   
  111.     }   
  112.     /**  
  113.      * Start device discover with the BluetoothAdapter  
  114.      */  
  115.     private void doDiscovery() {   
  116.         if (D) Log.d(TAG, "doDiscovery()");   
  117.         // Indicate scanning in the title 设置标题加载loading条可见   
  118.         setProgressBarIndeterminateVisibility(true);    
  119.         setTitle(R.string.scanning); //设置标题   
  120.         // Turn on sub-title for new devices   
  121.         findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);   
  122.         // If we're already discovering, stop it   
  123.         if (mBtAdapter.isDiscovering()) {   
  124.             mBtAdapter.cancelDiscovery();    
  125.         }   
  126.         // Request discover from BluetoothAdapter   
  127.         mBtAdapter.startDiscovery();   
  128.     }   
  129.     // The on-click listener for all devices in the ListViews 点击蓝牙列表的监听   
  130.     private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {   
  131.         public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {   
  132.             // Cancel discovery because it's costly and we're about to connect   
  133.             mBtAdapter.cancelDiscovery();    
  134.             // Get the device MAC address, which is the last 17 chars in the View IP地址17位   
  135.             String info = ((TextView) v).getText().toString();   
  136.             String address = info.substring(info.length() - 17);   
  137.             // Create the result Intent and include the MAC address   
  138.             Intent intent = new Intent();   
  139.             intent.putExtra(EXTRA_DEVICE_ADDRESS, address);   
  140.             // Set result and finish this Activity   
  141.             setResult(Activity.RESULT_OK, intent);   
  142.             finish();   
  143.         }   
  144.     };   
  145.     // The BroadcastReceiver that listens for discovered devices and   
  146.     // changes the title when discovery is finished   
  147.     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {   
  148.         @Override  
  149.         public void onReceive(Context context, Intent intent) {   
  150.             String action = intent.getAction();   
  151.             // When discovery finds a device 搜索到设备   
  152.             if (BluetoothDevice.ACTION_FOUND.equals(action)) {   
  153.                 // Get the BluetoothDevice object from the Intent   
  154.                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);   
  155.                 // If it's already paired, skip it, because it's been listed already   
  156.                 if (device.getBondState() != BluetoothDevice.BOND_BONDED) {   
  157.                     mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());   
  158.                 }   
  159.             // When discovery is finished, change the Activity title   
  160.             } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {    
  161.                 setProgressBarIndeterminateVisibility(false);//搜索完毕,loading条不可见   
  162.                 setTitle(R.string.select_device);   
  163.                 if (mNewDevicesArrayAdapter.getCount() == 0) {   
  164.                     String noDevices = getResources().getText(R.string.none_found).toString();   
  165.                     mNewDevicesArrayAdapter.add(noDevices);   
  166.                 }   
  167.             }   
  168.         }   
  169.     };   
  170. }  

 

Java代码 复制代码
  1. /*  
  2.  * Copyright (C) 2009 The Android Open Source Project  
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License");  
  5.  * you may not use this file except in compliance with the License.  
  6.  * You may obtain a copy of the License at  
  7.  *  
  8.  *      http://www.apache.org/licenses/LICENSE-2.0  
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software  
  11.  * distributed under the License is distributed on an "AS IS" BASIS,  
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.  * See the License for the specific language governing permissions and  
  14.  * limitations under the License.  
  15.  */  
  16. package com.example.android.BluetoothChat;   
  17. import java.io.IOException;   
  18. import java.io.InputStream;   
  19. import java.io.OutputStream;   
  20. import java.util.UUID;   
  21. import android.bluetooth.BluetoothAdapter;   
  22. import android.bluetooth.BluetoothDevice;   
  23. import android.bluetooth.BluetoothServerSocket;   
  24. import android.bluetooth.BluetoothSocket;   
  25. import android.content.Context;   
  26. import android.os.Bundle;   
  27. import android.os.Handler;   
  28. import android.os.Message;   
  29. import android.util.Log;   
  30. /**  
  31.  * This class does all the work for setting up and managing Bluetooth  
  32.  * connections with other devices. It has a thread that listens for  
  33.  * incoming connections, a thread for connecting with a device, and a  
  34.  * thread for performing data transmissions when connected.  
  35.  */  
  36. public class BluetoothChatService {   
  37.     // Debugging   
  38.     private static final String TAG = "BluetoothChatService";   
  39.     private static final boolean D = true;   
  40.     // Name for the SDP record when creating server socket   
  41.     private static final String NAME = "BluetoothChat";   
  42.     // Unique UUID for this application   
  43.     private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");   
  44.     // Member fields   
  45.     private final BluetoothAdapter mAdapter;   
  46.     private final Handler mHandler;   
  47.     private AcceptThread mAcceptThread;   
  48.     private ConnectThread mConnectThread;   
  49.     private ConnectedThread mConnectedThread;   
  50.     private int mState;   
  51.     // Constants that indicate the current connection state   
  52.     public static final int STATE_NONE = 0;       // we're doing nothing   
  53.     public static final int STATE_LISTEN = 1;     // now listening for incoming connections   
  54.     public static final int STATE_CONNECTING = 2// now initiating an outgoing connection   
  55.     public static final int STATE_CONNECTED = 3;  // now connected to a remote device   
  56.     /**  
  57.      * Constructor. Prepares a new BluetoothChat session.  
  58.      * @param context  The UI Activity Context  
  59.      * @param handler  A Handler to send messages back to the UI Activity  
  60.      */  
  61.     public BluetoothChatService(Context context, Handler handler) {   
  62.         mAdapter = BluetoothAdapter.getDefaultAdapter();   
  63.         mState = STATE_NONE;   
  64.         mHandler = handler;   
  65.     }   
  66.     /**  
  67.      * Set the current state of the chat connection  
  68.      * @param state  An integer defining the current connection state  
  69.      */  
  70.     private synchronized void setState(int state) {   
  71.         if (D) Log.d(TAG, "setState() " + mState + " -> " + state);   
  72.         mState = state;   
  73.         // Give the new state to the Handler so the UI Activity can update 更新状态,使用Handler向UI发送消息提示   
  74.         mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();   
  75.     }   
  76.     /**  
  77.      * Return the current connection state. */  
  78.     public synchronized int getState() {   
  79.         return mState;   
  80.     }   
  81.     /**  
  82.      * Start the chat service. Specifically start AcceptThread to begin a  
  83.      * session in listening (server) mode. Called by the Activity onResume() */  
  84.     public synchronized void start() {   
  85.         if (D) Log.d(TAG, "start");   
  86.         // Cancel any thread attempting to make a connection   
  87.         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}   
  88.         // Cancel any thread currently running a connection   
  89.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}   
  90.         // Start the thread to listen on a BluetoothServerSocket   
  91.         if (mAcceptThread == null) {   
  92.             mAcceptThread = new AcceptThread();   
  93.             mAcceptThread.start();   
  94.         }   
  95.         setState(STATE_LISTEN);   
  96.     }   
  97.     /**  
  98.      * Start the ConnectThread to initiate a connection to a remote device.  
  99.      * @param device  The BluetoothDevice to connect  
  100.      */  
  101.     public synchronized void connect(BluetoothDevice device) {   
  102.         if (D) Log.d(TAG, "connect to: " + device);   
  103.         // Cancel any thread attempting to make a connection   
  104.         if (mState == STATE_CONNECTING) {   
  105.             if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}   
  106.         }   
  107.         // Cancel any thread currently running a connection   
  108.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}   
  109.         // Start the thread to connect with the given device 设置正在状态为正在连接   
  110.         mConnectThread = new ConnectThread(device);   
  111.         mConnectThread.start();   
  112.         setState(STATE_CONNECTING);   
  113.     }   
  114.     /**  
  115.      * Start the ConnectedThread to begin managing a Bluetooth connection  
  116.      * @param socket  The BluetoothSocket on which the connection was made  
  117.      * @param device  The BluetoothDevice that has been connected  
  118.      */  
  119.     public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {   
  120.         if (D) Log.d(TAG, "connected");   
  121.         // Cancel the thread that completed the connection   
  122.         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}   
  123.         // Cancel any thread currently running a connection   
  124.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}   
  125.         // Cancel the accept thread because we only want to connect to one device   
  126.         if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}   
  127.         // Start the thread to manage the connection and perform transmissions   
  128.         mConnectedThread = new ConnectedThread(socket);   
  129.         mConnectedThread.start();   
  130.         // Send the name of the connected device back to the UI Activity   
  131.         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);   
  132.         Bundle bundle = new Bundle();   
  133.         bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());   
  134.         msg.setData(bundle);   
  135.         mHandler.sendMessage(msg);   
  136.         setState(STATE_CONNECTED);   
  137.     }   
  138.     /**  
  139.      * Stop all threads  
  140.      */  
  141.     public synchronized void stop() {   
  142.         if (D) Log.d(TAG, "stop");   
  143.         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}   
  144.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}   
  145.         if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}   
  146.         setState(STATE_NONE);   
  147.     }   
  148.     /**  
  149.      * Write to the ConnectedThread in an unsynchronized manner  
  150.      * @param out The bytes to write  
  151.      * @see ConnectedThread#write(byte[])  
  152.      */  
  153.     public void write(byte[] out) {   
  154.         // Create temporary object   
  155.         ConnectedThread r;   
  156.         // Synchronize a copy of the ConnectedThread   
  157.         synchronized (this) {   
  158.             if (mState != STATE_CONNECTED) return;   
  159.             r = mConnectedThread;   
  160.         }   
  161.         // Perform the write unsynchronized   
  162.         r.write(out);   
  163.     }   
  164.     /**  
  165.      * Indicate that the connection attempt failed and notify the UI Activity.  
  166.      */  
  167.     private void connectionFailed() {   
  168.         setState(STATE_LISTEN);   
  169.         // Send a failure message back to the Activity更新状态,使用Handler向UI发送消息提示   
  170.         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);   
  171.         Bundle bundle = new Bundle();   
  172.         bundle.putString(BluetoothChat.TOAST, "Unable to connect device");   
  173.         msg.setData(bundle);   
  174.         mHandler.sendMessage(msg);   
  175.     }   
  176.     /**  
  177.      * Indicate that the connection was lost and notify the UI Activity.  
  178.      */  
  179.     private void connectionLost() {   
  180.         setState(STATE_LISTEN);   
  181.         // Send a failure message back to the Activity更新状态,使用Handler向UI发送消息提示   
  182.         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);   
  183.         Bundle bundle = new Bundle();   
  184.         bundle.putString(BluetoothChat.TOAST, "Device connection was lost");   
  185.         msg.setData(bundle);   
  186.         mHandler.sendMessage(msg);   
  187.     }   
  188.     /**  
  189.      * This thread runs while listening for incoming connections. It behaves  
  190.      * like a server-side client. It runs until a connection is accepted  
  191.      * (or until cancelled).  
  192.      */  
  193.     private class AcceptThread extends Thread {   
  194.         // The local server socket   
  195.         private final BluetoothServerSocket mmServerSocket;   
  196.         public AcceptThread() {   
  197.             BluetoothServerSocket tmp = null;   
  198.             // Create a new listening server socket   
  199.             try {   
  200.                 tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);   
  201.             } catch (IOException e) {   
  202.                 Log.e(TAG, "listen() failed", e);   
  203.             }   
  204.             mmServerSocket = tmp;   
  205.         }   
  206.         public void run() {   
  207.             if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);   
  208.             setName("AcceptThread");   
  209.             BluetoothSocket socket = null;   
  210.             // Listen to the server socket if we're not connected 不断监听是否有蓝牙连接请求   
  211.             while (mState != STATE_CONNECTED) {    
  212.                 try {   
  213.                     // This is a blocking call and will only return on a   
  214.                     // successful connection or an exception   
  215.                     socket = mmServerSocket.accept();   
  216.                 } catch (IOException e) {   
  217.                     Log.e(TAG, "accept() failed", e);   
  218.                     break;   
  219.                 }   
  220.                 // If a connection was accepted   
  221.                 if (socket != null) {   
  222.                     synchronized (BluetoothChatService.this) {   
  223.                         switch (mState) {   
  224.                         case STATE_LISTEN:   
  225.                         case STATE_CONNECTING:   
  226.                             // Situation normal. Start the connected thread. 当有连接请求且状态是正在连接时   
  227.                             connected(socket, socket.getRemoteDevice());   
  228.                             break;   
  229.                         case STATE_NONE:   
  230.                         case STATE_CONNECTED:   
  231.                             // Either not ready or already connected. Terminate new socket.   
  232.                             try {   
  233.                                 socket.close();   
  234.                             } catch (IOException e) {   
  235.                                 Log.e(TAG, "Could not close unwanted socket", e);   
  236.                             }   
  237.                             break;   
  238.                         }   
  239.                     }   
  240.                 }   
  241.             }   
  242.             if (D) Log.i(TAG, "END mAcceptThread");   
  243.         }   
  244.         public void cancel() {   
  245.             if (D) Log.d(TAG, "cancel " + this);   
  246.             try {   
  247.                 mmServerSocket.close();   
  248.             } catch (IOException e) {   
  249.                 Log.e(TAG, "close() of server failed", e);   
  250.             }   
  251.         }   
  252.     }   
  253.   
  254.     /**  
  255.      * This thread runs while attempting to make an outgoing connection  
  256.      * with a device. It runs straight through; the connection either  
  257.      * succeeds or fails.  
  258.      */  
  259.     private class ConnectThread extends Thread {   
  260.         private final BluetoothSocket mmSocket;   
  261.         private final BluetoothDevice mmDevice;   
  262.         public ConnectThread(BluetoothDevice device) {   
  263.             mmDevice = device;   
  264.             BluetoothSocket tmp = null;   
  265.             // Get a BluetoothSocket for a connection with the   
  266.             // given BluetoothDevice   
  267.             try {   
  268.                 tmp = device.createRfcommSocketToServiceRecord(MY_UUID);   
  269.             } catch (IOException e) {   
  270.                 Log.e(TAG, "create() failed", e);   
  271.             }   
  272.             mmSocket = tmp;   
  273.         }   
  274.         public void run() {   
  275.             Log.i(TAG, "BEGIN mConnectThread");   
  276.             setName("ConnectThread");   
  277.             // Always cancel discovery because it will slow down a connection   
  278.             mAdapter.cancelDiscovery();   
  279.             // Make a connection to the BluetoothSocket   
  280.             try {   
  281.                 // This is a blocking call and will only return on a   
  282.                 // successful connection or an exception   
  283.                 mmSocket.connect();   
  284.             } catch (IOException e) {   
  285.                 connectionFailed();   
  286.                 // Close the socket   
  287.                 try {   
  288.                     mmSocket.close();   
  289.                 } catch (IOException e2) {   
  290.                     Log.e(TAG, "unable to close() socket during connection failure", e2);   
  291.                 }   
  292.                 // Start the service over to restart listening mode   
  293.                 BluetoothChatService.this.start();   
  294.                 return;   
  295.             }   
  296.             // Reset the ConnectThread because we're done   
  297.             synchronized (BluetoothChatService.this) {   
  298.                 mConnectThread = null;   
  299.             }   
  300.             // Start the connected thread   
  301.             connected(mmSocket, mmDevice);   
  302.         }   
  303.         public void cancel() {   
  304.             try {   
  305.                 mmSocket.close();   
  306.             } catch (IOException e) {   
  307.                 Log.e(TAG, "close() of connect socket failed", e);   
  308.             }   
  309.         }   
  310.     }   
  311.     /**  
  312.      * This thread runs during a connection with a remote device.  
  313.      * It handles all incoming and outgoing transmissions.  
  314.      */  
  315.     private class ConnectedThread extends Thread {   
  316.         private final BluetoothSocket mmSocket;   
  317.         private final InputStream mmInStream;   
  318.         private final OutputStream mmOutStream;   
  319.         public ConnectedThread(BluetoothSocket socket) {   
  320.             Log.d(TAG, "create ConnectedThread");   
  321.             mmSocket = socket;   
  322.             InputStream tmpIn = null;   
  323.             OutputStream tmpOut = null;   
  324.             // Get the BluetoothSocket input and output streams   
  325.             try {   
  326.                 tmpIn = socket.getInputStream();   
  327.                 tmpOut = socket.getOutputStream();   
  328.             } catch (IOException e) {   
  329.                 Log.e(TAG, "temp sockets not created", e);   
  330.             }   
  331.             mmInStream = tmpIn;   
  332.             mmOutStream = tmpOut;   
  333.         }   
  334.         public void run() {   
  335.             Log.i(TAG, "BEGIN mConnectedThread");   
  336.             byte[] buffer = new byte[1024];   
  337.             int bytes;   
  338.             // Keep listening to the InputStream while connected   
  339.             while (true) {   
  340.                 try {   
  341.                     // Read from the InputStream    
  342.                     bytes = mmInStream.read(buffer);   
  343.                     // Send the obtained bytes to the UI Activity   
  344.                     mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)   
  345.                             .sendToTarget();   
  346.                 } catch (IOException e) {   
  347.                     Log.e(TAG, "disconnected", e);   
  348.                     connectionLost();   
  349.                     break;   
  350.                 }   
  351.             }   
  352.         }   
  353.         /**  
  354.          * Write to the connected OutStream.  
  355.          * @param buffer  The bytes to write  
  356.          */  
  357.         public void write(byte[] buffer) {   
  358.             try {   
  359.                 mmOutStream.write(buffer); //向对方写数据   
  360.                 // Share the sent message back to the UI Activity   
  361.                 mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)   
  362.                         .sendToTarget();   
  363.             } catch (IOException e) {   
  364.                 Log.e(TAG, "Exception during write", e);   
  365.             }   
  366.         }   
  367.         public void cancel() {   
  368.             try {   
  369.                 mmSocket.close();   
  370.             } catch (IOException e) {   
  371.                 Log.e(TAG, "close() of connect socket failed", e);   
  372.             }   
  373.         }   
  374.     }   
  375. }  

里面的难点就是BluetoothChatService.java这个类中几个线程的理解;

ConnectThread这个线程就是连接socket的线程.如果断开就执行 BluetoothChatService.this.start();这段句代码来重新获取socket

那么AcceptThread这个就很容易理解了.就是获取socket的线程.

ConnectedThread这个线程就是一直运行着来接受输入输出数据的线程.当ConnectThread连接之后就开始执行他.

其他的都比较好理解吧

 

posted on 2012-06-18 16:48  清沁  阅读(5833)  评论(0编辑  收藏  举报