相关参考网站:http://wenku.baidu.com/view/d901cc2d4b73f242336c5f87.html
http://wenku.baidu.com/view/dec9bddd5022aaea998f0f7f.html
测试蓝牙程序需要至少2个真机,然而Android 2.0以后才真正支持蓝牙设备编程。为了给这一讲配图,我要先去把HTC G4 刷一下机,经过2天的折腾,终于把G4刷到Android 2.2版本了…… 开始写本讲……
蓝牙是一种设备短距离无线通信技术,使用蓝牙你可以搜索并连接到附近的蓝牙设备,可以在两个已经进行过配对的蓝牙设备之间进行数据传输。
本讲我们先使用Android SDK自带的例子蓝牙聊天程序BluetoothChat来做演示。源代码具体位置\android-sdk-windows\samples\android-8\BluetoothChat目录下。然后再自己实现一边这个蓝牙聊天程序。
一、SDK自带例子,BluetoothChat 的运行演示
这一次我们先看一下运行效果:准备两个真机 Milestone 和 HTC G4 Tattoo,分别安装好Bluetooth程序,
先在 Milestone上点击 BluetoothChat 运行程序,
提示需要蓝牙使用权限
选择是,打开蓝牙,进入聊天界面,只是此时还未连接任何蓝牙设备。在HTC G4上进行同样的操作,也进入not connected 状态。
点击Menu按钮呼出菜单
点击 Make discoverable 菜单使,并在弹出的权限请求对话框中选择是,使手机可以被其它蓝牙设备发现,G4也做同样操作
然后选择connect a device,在弹出的对话框中选择 Scan for devices 查找设备,会发现 HTC Tattoo 设备
全部选择匹配,然后就连接成功了。
这样就可以聊天了……
MainFest xml
- <?xml version="1.0" encoding="utf-8"?>
- <!-- Copyright (C) 2009 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.android.BluetoothChat"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk minSdkVersion="6" />
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <!-- 获取权限BlueTooth-->
- <application android:label="@string/app_name"
- android:icon="@drawable/app_icon" >
- <activity android:name=".BluetoothChat"
- android:label="@string/app_name"
- android:configChanges="orientation|keyboardHidden">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <activity android:name=".DeviceListActivity"
- android:label="@string/select_device"
- android:theme="@android:style/Theme.Dialog"
- android:configChanges="orientation|keyboardHidden" />
- <!-- android:theme="@android:style/Theme.Dialog" 设置界面主题:弹出框主题 -->
- </application>
- </manifest>
- /*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.example.android.BluetoothChat;
- import android.app.Activity;
- import android.bluetooth.BluetoothAdapter;
- import android.bluetooth.BluetoothDevice;
- import android.content.Intent;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.util.Log;
- import android.view.KeyEvent;
- import android.view.Menu;
- import android.view.MenuInflater;
- import android.view.MenuItem;
- import android.view.View;
- import android.view.Window;
- import android.view.View.OnClickListener;
- import android.view.inputmethod.EditorInfo;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListView;
- import android.widget.TextView;
- import android.widget.Toast;
- /**
- * This is the main Activity that displays the current chat session.
- */
- public class BluetoothChat extends Activity {
- // Debugging
- private static final String TAG = "BluetoothChat";
- private static final boolean D = true;
- // Message types sent from the BluetoothChatService Handler
- public static final int MESSAGE_STATE_CHANGE = 1;
- public static final int MESSAGE_READ = 2;
- public static final int MESSAGE_WRITE = 3;
- public static final int MESSAGE_DEVICE_NAME = 4;
- public static final int MESSAGE_TOAST = 5;
- // Key names received from the BluetoothChatService Handler
- public static final String DEVICE_NAME = "device_name";
- public static final String TOAST = "toast";
- // Intent request codes
- private static final int REQUEST_CONNECT_DEVICE = 1;
- private static final int REQUEST_ENABLE_BT = 2;
- // Layout Views
- private TextView mTitle;
- private ListView mConversationView;
- private EditText mOutEditText;
- private Button mSendButton;
- // Name of the connected device
- private String mConnectedDeviceName = null;
- // Array adapter for the conversation thread
- private ArrayAdapter<String> mConversationArrayAdapter;
- // String buffer for outgoing messages
- private StringBuffer mOutStringBuffer;
- // Local Bluetooth adapter
- private BluetoothAdapter mBluetoothAdapter = null;
- // Member object for the chat services
- private BluetoothChatService mChatService = null;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if(D) Log.e(TAG, "+++ ON CREATE +++");
- // Set up the window layout 设置Activity的标题栏格式Window.FEATURE_CUSTOM_TITLE普通、Window.FEATURE_NO_TITLE无标题栏、Window.FEATURE_INDETERMINATE_PROGRESS进度条
- requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
- setContentView(R.layout.main);
- //设置标题栏的布局资源
- getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
- // Set up the custom title
- mTitle = (TextView) findViewById(R.id.title_left_text);
- mTitle.setText(R.string.app_name);
- mTitle = (TextView) findViewById(R.id.title_right_text);
- // Get local Bluetooth adapter
- //从根本上讲,这是你的出发点,所有的蓝牙行动。一旦你的本地适配器,getBondedDevices()所有配对设备对象设置;
- //与startDiscovery()启动设备可被发现,或建立一个BluetoothServerSocket监听与listenUsingRfcommWithServiceRecord(字符串,UUID的)传入的连接请求。
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- // If the adapter is null, then Bluetooth is not supported
- if (mBluetoothAdapter == null) {
- Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
- finish();
- return;
- }
- }
- @Override
- public void onStart() {
- super.onStart();
- if(D) Log.e(TAG, "++ ON START ++");
- // If BT is not on, request that it be enabled.
- // setupChat() will then be called during onActivityResult
- //判断蓝牙是否开启,否则请求开启
- if (!mBluetoothAdapter.isEnabled()) {
- Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
- // Otherwise, setup the chat session
- } else {
- if (mChatService == null) setupChat();
- }
- }
- @Override
- public synchronized void onResume() {
- super.onResume();
- if(D) Log.e(TAG, "+ ON RESUME +");
- // Performing this check in onResume() covers the case in which BT was
- // not enabled during onStart(), so we were paused to enable it...
- // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
- if (mChatService != null) {
- // Only if the state is STATE_NONE, do we know that we haven't started already
- if (mChatService.getState() == BluetoothChatService.STATE_NONE) { //STATE_NONE及刚创建为启动状态,如果是从后台切换到前台应为STATE_LISTEN,无需再次启动
- // Start the Bluetooth chat services
- mChatService.start();
- }
- }
- }
- private void setupChat() {
- Log.d(TAG, "setupChat()");
- // Initialize the array adapter for the conversation thread
- mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
- mConversationView = (ListView) findViewById(R.id.in);
- mConversationView.setAdapter(mConversationArrayAdapter);
- // Initialize the compose field with a listener for the return key
- mOutEditText = (EditText) findViewById(R.id.edit_text_out);
- mOutEditText.setOnEditorActionListener(mWriteListener);////注册编辑状态监听
- // Initialize the send button with a listener that for click events
- mSendButton = (Button) findViewById(R.id.button_send);
- mSendButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- // Send a message using content of the edit text widget
- TextView view = (TextView) findViewById(R.id.edit_text_out);
- String message = view.getText().toString();
- sendMessage(message); //点击发送则向蓝牙发送消息
- }
- });
- // Initialize the BluetoothChatService to perform bluetooth connections
- mChatService = new BluetoothChatService(this, mHandler);
- // Initialize the buffer for outgoing messages
- mOutStringBuffer = new StringBuffer("");
- }
- @Override
- public synchronized void onPause() {
- super.onPause();
- if(D) Log.e(TAG, "- ON PAUSE -");
- }
- @Override
- public void onStop() {
- super.onStop();
- if(D) Log.e(TAG, "-- ON STOP --");
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- // Stop the Bluetooth chat services
- if (mChatService != null) mChatService.stop(); //UI关闭时,蓝牙服务应也停止
- if(D) Log.e(TAG, "--- ON DESTROY ---");
- }
- private void ensureDiscoverable() {
- if(D) Log.d(TAG, "ensure discoverable");
- if (mBluetoothAdapter.getScanMode() !=
- BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { //如果蓝牙状态为不可被发现则请求开启为可被发现,时间300秒内
- Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
- startActivity(discoverableIntent);
- }
- }
- /**
- * Sends a message.
- * @param message A string of text to send.
- */
- private void sendMessage(String message) {
- // Check that we're actually connected before trying anything
- if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
- Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
- return;
- }
- // Check that there's actually something to send
- if (message.length() > 0) {
- // Get the message bytes and tell the BluetoothChatService to write
- byte[] send = message.getBytes();
- mChatService.write(send);
- // Reset out string buffer to zero and clear the edit text field
- mOutStringBuffer.setLength(0); //mOutStringBuffer 一直未使用,何意?
- mOutEditText.setText(mOutStringBuffer);//清空编辑框 窃以为使用mOutEditText.setText("");即可
- }
- }
- // The action listener for the EditText widget, to listen for the return key
- private TextView.OnEditorActionListener mWriteListener =
- new TextView.OnEditorActionListener() {
- public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
- // If the action is a key-up event on the return key, send the message
- //经测试应为点击回车键后放开时发送消息
- if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
- String message = view.getText().toString();
- sendMessage(message);
- }
- if(D) Log.i(TAG, "END onEditorAction");
- return true;
- }
- };
- // The Handler that gets information back from the BluetoothChatService
- //更新标题栏右边状态和读写状态的Handler
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_STATE_CHANGE:
- if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
- switch (msg.arg1) {
- case BluetoothChatService.STATE_CONNECTED:
- mTitle.setText(R.string.title_connected_to);
- mTitle.append(mConnectedDeviceName);
- mConversationArrayAdapter.clear(); //清空消息
- break;
- case BluetoothChatService.STATE_CONNECTING:
- mTitle.setText(R.string.title_connecting);
- break;
- case BluetoothChatService.STATE_LISTEN:
- case BluetoothChatService.STATE_NONE:
- mTitle.setText(R.string.title_not_connected);
- break;
- }
- break;
- case MESSAGE_WRITE:
- byte[] writeBuf = (byte[]) msg.obj;
- // construct a string from the buffer
- String writeMessage = new String(writeBuf);
- mConversationArrayAdapter.add("Me: " + writeMessage);
- break;
- case MESSAGE_READ:
- byte[] readBuf = (byte[]) msg.obj;
- // construct a string from the valid bytes in the buffer
- String readMessage = new String(readBuf, 0, msg.arg1);
- mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
- break;
- case MESSAGE_DEVICE_NAME:
- // save the connected device's name
- mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
- Toast.makeText(getApplicationContext(), "Connected to "
- + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
- break;
- case MESSAGE_TOAST:
- Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
- Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if(D) Log.d(TAG, "onActivityResult " + resultCode);
- switch (requestCode) {
- case REQUEST_CONNECT_DEVICE:
- // When DeviceListActivity returns with a device to connect
- if (resultCode == Activity.RESULT_OK) {
- // Get the device MAC address
- String address = data.getExtras()
- .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
- // Get the BLuetoothDevice object
- if(address==null){
- break;
- }
- //获取设备进行连接
- BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
- // Attempt to connect to the device
- mChatService.connect(device);
- }
- break;
- case REQUEST_ENABLE_BT:
- // When the request to enable Bluetooth returns
- if (resultCode == Activity.RESULT_OK) {
- // Bluetooth is now enabled, so set up a chat session
- //蓝牙开启成功,则继续初始化UI
- setupChat();
- } else {
- // User did not enable Bluetooth or an error occured
- Log.d(TAG, "BT not enabled");
- Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
- finish();
- }
- }
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- //从Menu资源中初始化Menu 返回true则弹出默认menu,false则不弹出Menu,须自定义
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.option_menu, menu);
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.scan:
- // Launch the DeviceListActivity to see devices and do scan
- //启动DeviceListActivity,并等待返回
- Intent serverIntent = new Intent(this, DeviceListActivity.class);
- startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
- return true;
- case R.id.discoverable:
- // Ensure this device is discoverable by others
- ensureDiscoverable();
- return true;
- }
- return false;
- }
- }
- /*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.example.android.BluetoothChat;
- import java.util.Set;
- import android.app.Activity;
- import android.bluetooth.BluetoothAdapter;
- import android.bluetooth.BluetoothDevice;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.Window;
- import android.view.View.OnClickListener;
- import android.widget.AdapterView;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.ListView;
- import android.widget.TextView;
- import android.widget.AdapterView.OnItemClickListener;
- /**
- * This Activity appears as a dialog. It lists any paired devices and
- * devices detected in the area after discovery. When a device is chosen
- * by the user, the MAC address of the device is sent back to the parent
- * Activity in the result Intent.
- */
- public class DeviceListActivity extends Activity {
- // Debugging
- private static final String TAG = "DeviceListActivity";
- private static final boolean D = true;
- // Return Intent extra
- public static String EXTRA_DEVICE_ADDRESS = "device_address";
- // Member fields
- private BluetoothAdapter mBtAdapter;
- private ArrayAdapter<String> mPairedDevicesArrayAdapter;
- private ArrayAdapter<String> mNewDevicesArrayAdapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Setup the window
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.device_list);
- // Set result CANCELED incase the user backs out
- //先设置为Canceled 防止点击回退键时BluetoothChat认为是Result_OK状态
- setResult(Activity.RESULT_CANCELED);
- // Initialize the button to perform device discovery
- Button scanButton = (Button) findViewById(R.id.button_scan);
- scanButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- doDiscovery();
- v.setVisibility(View.GONE);
- }
- });
- // Initialize array adapters. One for already paired devices and
- // one for newly discovered devices
- mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
- mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
- // Find and set up the ListView for paired devices
- ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
- pairedListView.setAdapter(mPairedDevicesArrayAdapter);
- pairedListView.setOnItemClickListener(mDeviceClickListener);
- // Find and set up the ListView for newly discovered devices
- ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
- newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
- newDevicesListView.setOnItemClickListener(mDeviceClickListener);
- // Register for broadcasts when a device is discovered 发现设备时监听过滤
- IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
- this.registerReceiver(mReceiver, filter);
- // Register for broadcasts when discovery has finished发现设备程序运行结束时监听过滤
- filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
- this.registerReceiver(mReceiver, filter);
- // Get the local Bluetooth adapter
- mBtAdapter = BluetoothAdapter.getDefaultAdapter();
- // Get a set of currently paired devices 获取当前已经互联过的设备
- Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
- // If there are paired devices, add each one to the ArrayAdapter
- if (pairedDevices.size() > 0) {
- findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
- for (BluetoothDevice device : pairedDevices) {
- mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }
- } else {
- String noDevices = getResources().getText(R.string.none_paired).toString();
- mPairedDevicesArrayAdapter.add(noDevices);
- }
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- // Make sure we're not doing discovery anymore
- if (mBtAdapter != null) {
- mBtAdapter.cancelDiscovery(); //BluetoothAdapter 不受UI管理受系统管理,所以要即使cancelDiscovery
- }
- // Unregister broadcast listeners
- this.unregisterReceiver(mReceiver); //取消监听当UI销毁时
- }
- /**
- * Start device discover with the BluetoothAdapter
- */
- private void doDiscovery() {
- if (D) Log.d(TAG, "doDiscovery()");
- // Indicate scanning in the title 设置标题加载loading条可见
- setProgressBarIndeterminateVisibility(true);
- setTitle(R.string.scanning); //设置标题
- // Turn on sub-title for new devices
- findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
- // If we're already discovering, stop it
- if (mBtAdapter.isDiscovering()) {
- mBtAdapter.cancelDiscovery();
- }
- // Request discover from BluetoothAdapter
- mBtAdapter.startDiscovery();
- }
- // The on-click listener for all devices in the ListViews 点击蓝牙列表的监听
- private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
- public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
- // Cancel discovery because it's costly and we're about to connect
- mBtAdapter.cancelDiscovery();
- // Get the device MAC address, which is the last 17 chars in the View IP地址17位
- String info = ((TextView) v).getText().toString();
- String address = info.substring(info.length() - 17);
- // Create the result Intent and include the MAC address
- Intent intent = new Intent();
- intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
- // Set result and finish this Activity
- setResult(Activity.RESULT_OK, intent);
- finish();
- }
- };
- // The BroadcastReceiver that listens for discovered devices and
- // changes the title when discovery is finished
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- // When discovery finds a device 搜索到设备
- if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- // Get the BluetoothDevice object from the Intent
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // If it's already paired, skip it, because it's been listed already
- if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
- mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }
- // When discovery is finished, change the Activity title
- } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
- setProgressBarIndeterminateVisibility(false);//搜索完毕,loading条不可见
- setTitle(R.string.select_device);
- if (mNewDevicesArrayAdapter.getCount() == 0) {
- String noDevices = getResources().getText(R.string.none_found).toString();
- mNewDevicesArrayAdapter.add(noDevices);
- }
- }
- }
- };
- }
- /*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.example.android.BluetoothChat;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.UUID;
- import android.bluetooth.BluetoothAdapter;
- import android.bluetooth.BluetoothDevice;
- import android.bluetooth.BluetoothServerSocket;
- import android.bluetooth.BluetoothSocket;
- import android.content.Context;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.util.Log;
- /**
- * This class does all the work for setting up and managing Bluetooth
- * connections with other devices. It has a thread that listens for
- * incoming connections, a thread for connecting with a device, and a
- * thread for performing data transmissions when connected.
- */
- public class BluetoothChatService {
- // Debugging
- private static final String TAG = "BluetoothChatService";
- private static final boolean D = true;
- // Name for the SDP record when creating server socket
- private static final String NAME = "BluetoothChat";
- // Unique UUID for this application
- private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
- // Member fields
- private final BluetoothAdapter mAdapter;
- private final Handler mHandler;
- private AcceptThread mAcceptThread;
- private ConnectThread mConnectThread;
- private ConnectedThread mConnectedThread;
- private int mState;
- // Constants that indicate the current connection state
- public static final int STATE_NONE = 0; // we're doing nothing
- public static final int STATE_LISTEN = 1; // now listening for incoming connections
- public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
- public static final int STATE_CONNECTED = 3; // now connected to a remote device
- /**
- * Constructor. Prepares a new BluetoothChat session.
- * @param context The UI Activity Context
- * @param handler A Handler to send messages back to the UI Activity
- */
- public BluetoothChatService(Context context, Handler handler) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mState = STATE_NONE;
- mHandler = handler;
- }
- /**
- * Set the current state of the chat connection
- * @param state An integer defining the current connection state
- */
- private synchronized void setState(int state) {
- if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
- mState = state;
- // Give the new state to the Handler so the UI Activity can update 更新状态,使用Handler向UI发送消息提示
- mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
- }
- /**
- * Return the current connection state. */
- public synchronized int getState() {
- return mState;
- }
- /**
- * Start the chat service. Specifically start AcceptThread to begin a
- * session in listening (server) mode. Called by the Activity onResume() */
- public synchronized void start() {
- if (D) Log.d(TAG, "start");
- // Cancel any thread attempting to make a connection
- if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
- // Cancel any thread currently running a connection
- if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
- // Start the thread to listen on a BluetoothServerSocket
- if (mAcceptThread == null) {
- mAcceptThread = new AcceptThread();
- mAcceptThread.start();
- }
- setState(STATE_LISTEN);
- }
- /**
- * Start the ConnectThread to initiate a connection to a remote device.
- * @param device The BluetoothDevice to connect
- */
- public synchronized void connect(BluetoothDevice device) {
- if (D) Log.d(TAG, "connect to: " + device);
- // Cancel any thread attempting to make a connection
- if (mState == STATE_CONNECTING) {
- if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
- }
- // Cancel any thread currently running a connection
- if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
- // Start the thread to connect with the given device 设置正在状态为正在连接
- mConnectThread = new ConnectThread(device);
- mConnectThread.start();
- setState(STATE_CONNECTING);
- }
- /**
- * Start the ConnectedThread to begin managing a Bluetooth connection
- * @param socket The BluetoothSocket on which the connection was made
- * @param device The BluetoothDevice that has been connected
- */
- public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
- if (D) Log.d(TAG, "connected");
- // Cancel the thread that completed the connection
- if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
- // Cancel any thread currently running a connection
- if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
- // Cancel the accept thread because we only want to connect to one device
- if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}
- // Start the thread to manage the connection and perform transmissions
- mConnectedThread = new ConnectedThread(socket);
- mConnectedThread.start();
- // Send the name of the connected device back to the UI Activity
- Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);
- Bundle bundle = new Bundle();
- bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());
- msg.setData(bundle);
- mHandler.sendMessage(msg);
- setState(STATE_CONNECTED);
- }
- /**
- * Stop all threads
- */
- public synchronized void stop() {
- if (D) Log.d(TAG, "stop");
- if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
- if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
- if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}
- setState(STATE_NONE);
- }
- /**
- * Write to the ConnectedThread in an unsynchronized manner
- * @param out The bytes to write
- * @see ConnectedThread#write(byte[])
- */
- public void write(byte[] out) {
- // Create temporary object
- ConnectedThread r;
- // Synchronize a copy of the ConnectedThread
- synchronized (this) {
- if (mState != STATE_CONNECTED) return;
- r = mConnectedThread;
- }
- // Perform the write unsynchronized
- r.write(out);
- }
- /**
- * Indicate that the connection attempt failed and notify the UI Activity.
- */
- private void connectionFailed() {
- setState(STATE_LISTEN);
- // Send a failure message back to the Activity更新状态,使用Handler向UI发送消息提示
- Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);
- Bundle bundle = new Bundle();
- bundle.putString(BluetoothChat.TOAST, "Unable to connect device");
- msg.setData(bundle);
- mHandler.sendMessage(msg);
- }
- /**
- * Indicate that the connection was lost and notify the UI Activity.
- */
- private void connectionLost() {
- setState(STATE_LISTEN);
- // Send a failure message back to the Activity更新状态,使用Handler向UI发送消息提示
- Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);
- Bundle bundle = new Bundle();
- bundle.putString(BluetoothChat.TOAST, "Device connection was lost");
- msg.setData(bundle);
- mHandler.sendMessage(msg);
- }
- /**
- * This thread runs while listening for incoming connections. It behaves
- * like a server-side client. It runs until a connection is accepted
- * (or until cancelled).
- */
- private class AcceptThread extends Thread {
- // The local server socket
- private final BluetoothServerSocket mmServerSocket;
- public AcceptThread() {
- BluetoothServerSocket tmp = null;
- // Create a new listening server socket
- try {
- tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
- } catch (IOException e) {
- Log.e(TAG, "listen() failed", e);
- }
- mmServerSocket = tmp;
- }
- public void run() {
- if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
- setName("AcceptThread");
- BluetoothSocket socket = null;
- // Listen to the server socket if we're not connected 不断监听是否有蓝牙连接请求
- while (mState != STATE_CONNECTED) {
- try {
- // This is a blocking call and will only return on a
- // successful connection or an exception
- socket = mmServerSocket.accept();
- } catch (IOException e) {
- Log.e(TAG, "accept() failed", e);
- break;
- }
- // If a connection was accepted
- if (socket != null) {
- synchronized (BluetoothChatService.this) {
- switch (mState) {
- case STATE_LISTEN:
- case STATE_CONNECTING:
- // Situation normal. Start the connected thread. 当有连接请求且状态是正在连接时
- connected(socket, socket.getRemoteDevice());
- break;
- case STATE_NONE:
- case STATE_CONNECTED:
- // Either not ready or already connected. Terminate new socket.
- try {
- socket.close();
- } catch (IOException e) {
- Log.e(TAG, "Could not close unwanted socket", e);
- }
- break;
- }
- }
- }
- }
- if (D) Log.i(TAG, "END mAcceptThread");
- }
- public void cancel() {
- if (D) Log.d(TAG, "cancel " + this);
- try {
- mmServerSocket.close();
- } catch (IOException e) {
- Log.e(TAG, "close() of server failed", e);
- }
- }
- }
- /**
- * This thread runs while attempting to make an outgoing connection
- * with a device. It runs straight through; the connection either
- * succeeds or fails.
- */
- private class ConnectThread extends Thread {
- private final BluetoothSocket mmSocket;
- private final BluetoothDevice mmDevice;
- public ConnectThread(BluetoothDevice device) {
- mmDevice = device;
- BluetoothSocket tmp = null;
- // Get a BluetoothSocket for a connection with the
- // given BluetoothDevice
- try {
- tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
- } catch (IOException e) {
- Log.e(TAG, "create() failed", e);
- }
- mmSocket = tmp;
- }
- public void run() {
- Log.i(TAG, "BEGIN mConnectThread");
- setName("ConnectThread");
- // Always cancel discovery because it will slow down a connection
- mAdapter.cancelDiscovery();
- // Make a connection to the BluetoothSocket
- try {
- // This is a blocking call and will only return on a
- // successful connection or an exception
- mmSocket.connect();
- } catch (IOException e) {
- connectionFailed();
- // Close the socket
- try {
- mmSocket.close();
- } catch (IOException e2) {
- Log.e(TAG, "unable to close() socket during connection failure", e2);
- }
- // Start the service over to restart listening mode
- BluetoothChatService.this.start();
- return;
- }
- // Reset the ConnectThread because we're done
- synchronized (BluetoothChatService.this) {
- mConnectThread = null;
- }
- // Start the connected thread
- connected(mmSocket, mmDevice);
- }
- public void cancel() {
- try {
- mmSocket.close();
- } catch (IOException e) {
- Log.e(TAG, "close() of connect socket failed", e);
- }
- }
- }
- /**
- * This thread runs during a connection with a remote device.
- * It handles all incoming and outgoing transmissions.
- */
- private class ConnectedThread extends Thread {
- private final BluetoothSocket mmSocket;
- private final InputStream mmInStream;
- private final OutputStream mmOutStream;
- public ConnectedThread(BluetoothSocket socket) {
- Log.d(TAG, "create ConnectedThread");
- mmSocket = socket;
- InputStream tmpIn = null;
- OutputStream tmpOut = null;
- // Get the BluetoothSocket input and output streams
- try {
- tmpIn = socket.getInputStream();
- tmpOut = socket.getOutputStream();
- } catch (IOException e) {
- Log.e(TAG, "temp sockets not created", e);
- }
- mmInStream = tmpIn;
- mmOutStream = tmpOut;
- }
- public void run() {
- Log.i(TAG, "BEGIN mConnectedThread");
- byte[] buffer = new byte[1024];
- int bytes;
- // Keep listening to the InputStream while connected
- while (true) {
- try {
- // Read from the InputStream
- bytes = mmInStream.read(buffer);
- // Send the obtained bytes to the UI Activity
- mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
- .sendToTarget();
- } catch (IOException e) {
- Log.e(TAG, "disconnected", e);
- connectionLost();
- break;
- }
- }
- }
- /**
- * Write to the connected OutStream.
- * @param buffer The bytes to write
- */
- public void write(byte[] buffer) {
- try {
- mmOutStream.write(buffer); //向对方写数据
- // Share the sent message back to the UI Activity
- mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
- .sendToTarget();
- } catch (IOException e) {
- Log.e(TAG, "Exception during write", e);
- }
- }
- public void cancel() {
- try {
- mmSocket.close();
- } catch (IOException e) {
- Log.e(TAG, "close() of connect socket failed", e);
- }
- }
- }
- }
里面的难点就是BluetoothChatService.java这个类中几个线程的理解;
ConnectThread这个线程就是连接socket的线程.如果断开就执行 BluetoothChatService.this.start();这段句代码来重新获取socket
那么AcceptThread这个就很容易理解了.就是获取socket的线程.
ConnectedThread这个线程就是一直运行着来接受输入输出数据的线程.当ConnectThread连接之后就开始执行他.
其他的都比较好理解吧