运行的时候,会报错:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.ActionBar.setSubtitle(int)' on a null object reference

解决办法:

发现时是 actionBar出错,解决方法如下:

1、在这个onCreate函数中天加 getWindow().requestFeature(Window.FEATURE_ACTION_BAR);

2、将actionBar.setSubtitle(resId)注释掉,这样就不会使用到actionbar了。

这个Demo最重要的是3个类:BluetoothChat;DeviceListActivity;BluetoothChatService

BluetoothChat源码:

/** 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.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Build;
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.*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class BluetoothChat extends Activity {// Debuggingprivate static final String TAG = "BluetoothChat";private static final boolean D = true;// Message types sent from the BluetoothChatService Handlerpublic 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 Handlerpublic static final String DEVICE_NAME = "device_name";public static final String TOAST = "toast";// Intent request codesprivate static final int REQUEST_CONNECT_DEVICE_SECURE = 1;private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;private static final int REQUEST_ENABLE_BT = 3;// Layout Viewsprivate ListView mConversationView;private EditText mOutEditText;private Button mSendButton;// Name of the connected deviceprivate String mConnectedDeviceName = null;// Array adapter for the conversation threadprivate ArrayAdapter<String> mConversationArrayAdapter;// String buffer for outgoing messagesprivate StringBuffer mOutStringBuffer;// Local Bluetooth adapterprivate BluetoothAdapter mBluetoothAdapter = null;// Member object for the chat servicesprivate BluetoothChatService mChatService = null;@Overridepublic void onCreate(Bundle savedInstanceState) {getWindow().requestFeature(Window.FEATURE_ACTION_BAR);super.onCreate(savedInstanceState);if(D) Log.e(TAG, "+++ ON CREATE +++");// Set up the window layout
        setContentView(R.layout.main);// Get local Bluetooth adaptermBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();// If the adapter is null, then Bluetooth is not supportedif (mBluetoothAdapter == null) {Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();finish();return;}}@Overridepublic 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 onActivityResultif (!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();}}@Overridepublic 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 alreadyif (mChatService.getState() == BluetoothChatService.STATE_NONE) {// Start the Bluetooth chat services
              mChatService.start();}}}private void setupChat() {Log.d(TAG, "setupChat()");// Initialize the array adapter for the conversation threadmConversationArrayAdapter = 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 keymOutEditText = (EditText) findViewById(R.id.edit_text_out);mOutEditText.setOnEditorActionListener(mWriteListener);// Initialize the send button with a listener that for click eventsmSendButton = (Button) findViewById(R.id.button_send);mSendButton.setOnClickListener(new OnClickListener() {public void onClick(View v) {// Send a message using content of the edit text widgetTextView view = (TextView) findViewById(R.id.edit_text_out);String message = view.getText().toString();sendMessage(message);}});// Initialize the BluetoothChatService to perform bluetooth connectionsmChatService = new BluetoothChatService(this, mHandler);// Initialize the buffer for outgoing messagesmOutStringBuffer = new StringBuffer("");}@Overridepublic synchronized void onPause() {super.onPause();if(D) Log.e(TAG, "- ON PAUSE -");}@Overridepublic void onStop() {super.onStop();if(D) Log.e(TAG, "-- ON STOP --");}@Overridepublic void onDestroy() {super.onDestroy();// Stop the Bluetooth chat servicesif (mChatService != null) mChatService.stop();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) {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 anythingif (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();return;}// Check that there's actually something to sendif (message.length() > 0) {// Get the message bytes and tell the BluetoothChatService to writebyte[] send = message.getBytes();mChatService.write(send);// Reset out string buffer to zero and clear the edit text fieldmOutStringBuffer.setLength(0);mOutEditText.setText(mOutStringBuffer);}}// The action listener for the EditText widget, to listen for the return keyprivate 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 messageif (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;}};private final void setStatus(int resId) {final ActionBar actionBar = getActionBar();actionBar.setSubtitle(resId);}private final void setStatus(CharSequence subTitle) {final ActionBar actionBar = getActionBar();actionBar.setSubtitle(subTitle);}// The Handler that gets information back from the BluetoothChatServiceprivate final Handler mHandler = new Handler() {@Overridepublic 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:setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));mConversationArrayAdapter.clear();break;case BluetoothChatService.STATE_CONNECTING:setStatus(R.string.title_connecting);break;case BluetoothChatService.STATE_LISTEN:case BluetoothChatService.STATE_NONE:setStatus(R.string.title_not_connected);break;}break;case MESSAGE_WRITE:byte[] writeBuf = (byte[]) msg.obj;// construct a string from the bufferString 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 bufferString readMessage = new String(readBuf, 0, msg.arg1);mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);break;case MESSAGE_DEVICE_NAME:// save the connected device's namemConnectedDeviceName = 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_SECURE:// When DeviceListActivity returns with a device to connectif (resultCode == Activity.RESULT_OK) {connectDevice(data, true);}break;case REQUEST_CONNECT_DEVICE_INSECURE:// When DeviceListActivity returns with a device to connectif (resultCode == Activity.RESULT_OK) {connectDevice(data, false);}break;case REQUEST_ENABLE_BT:// When the request to enable Bluetooth returnsif (resultCode == Activity.RESULT_OK) {// Bluetooth is now enabled, so set up a chat session
                setupChat();} else {// User did not enable Bluetooth or an error occurredLog.d(TAG, "BT not enabled");Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();finish();}}}private void connectDevice(Intent data, boolean secure) {// Get the device MAC addressString address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);// Get the BluetoothDevice objectBluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);// Attempt to connect to the device
        mChatService.connect(device, secure);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.option_menu, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {Intent serverIntent = null;switch (item.getItemId()) {case R.id.secure_connect_scan:// Launch the DeviceListActivity to see devices and do scanserverIntent = new Intent(this, DeviceListActivity.class);startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);return true;case R.id.insecure_connect_scan:// Launch the DeviceListActivity to see devices and do scanserverIntent = new Intent(this, DeviceListActivity.class);startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);return true;case R.id.discoverable:// Ensure this device is discoverable by others
            ensureDiscoverable();return true;}return false;}}

View Code

其中,先执行了onCreate(),onStart(),onResume()方法

    @Overridepublic void onCreate(Bundle savedInstanceState) {getWindow().requestFeature(Window.FEATURE_ACTION_BAR);super.onCreate(savedInstanceState);if(D) Log.e(TAG, "+++ ON CREATE +++");// Set up the window layout
        setContentView(R.layout.main);// Get local Bluetooth adaptermBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();// If the adapter is null, then Bluetooth is not supportedif (mBluetoothAdapter == null) {Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();finish();return;}}@Overridepublic 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 onActivityResultif (!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();}}@Overridepublic 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 alreadyif (mChatService.getState() == BluetoothChatService.STATE_NONE) {// Start the Bluetooth chat services
              mChatService.start();}}}

View Code

常量:

// 调试用的日志标志TAG与是否打印日志的标志D
private static final String TAG = "BluetoothChat";
private static final boolean D = true;  // 从BluetoothChatService传回来交给Handler处理的消息类型
public static final int MESSAGE_STATE_CHANGE = 1; // 蓝牙socket状态改变,居然有4个状态监听、正在连接、已连接、无。具体可以看Handler的handMessage方法
public static final int MESSAGE_READ = 2; // 这个消息类型被发送回来是因为蓝牙服务socket在读别的设备发来的内容
public static final int MESSAGE_WRITE = 3; // 这个消息类型被发送回来是因为蓝牙socket在写要发送的内容
public static final int MESSAGE_DEVICE_NAME = 4; // 这个消息发送回来是因为连接到了一个设备,并且获得了对方的名字,好像是手机的型号
public static final int MESSAGE_TOAST = 5; // 这个消息发送回来是有要用Toast控件广播的内容  // 从BluetoothChatService的发送回来消息内容里的键值
public static final String DEVICE_NAME = "device_name"; // 在发回设备名(MESSAGE_DEVICE_NAME)消息时,获取设备名时的键值
public static final String TOAST = "toast"; // 同样是键值,指向的是要Toast控件广播的内容   // Intent的请求值,在startActivityForResult时使用
private static final int REQUEST_CONNECT_DEVICE = 1; // 在要请求去搜索设备的时候使用到
private static final int REQUEST_ENABLE_BT = 2; // 在请求要使蓝牙可用的时候用到

主要是Menu菜单触发事件:

   @Overridepublic boolean onCreateOptionsMenu(Menu menu) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.option_menu, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {Intent serverIntent = null;switch (item.getItemId()) {case R.id.secure_connect_scan:// Launch the DeviceListActivity to see devices and do scanserverIntent = new Intent(this, DeviceListActivity.class);startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);return true;case R.id.insecure_connect_scan:// Launch the DeviceListActivity to see devices and do scanserverIntent = new Intent(this, DeviceListActivity.class);startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);return true;case R.id.discoverable:// Ensure this device is discoverable by others
            ensureDiscoverable();return true;}return false;}

View Code

额外:

然后是ListView从底下开始显示:需要XML里一句:

android:stackFromBottom="true" 

学到了EditText注册了一个监听器,然后实现了软键盘按回车return键发送消息(一般我们在EditText里按return键是换行)

// 用于监听EditText的一个return键事件
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;  }
};  

DeviceListActivity源码:

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.

这个Aty以对话框的形式显示。显示任何已经配对的设备和查找的设备。当用户选择一个设备的时候,将会返回MAC地址到上一个Aty。

/** 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 {// Debuggingprivate static final String TAG = "DeviceListActivity";private static final boolean D = true;// Return Intent extrapublic static String EXTRA_DEVICE_ADDRESS = "device_address";// Member fieldsprivate BluetoothAdapter mBtAdapter;private ArrayAdapter<String> mPairedDevicesArrayAdapter;private ArrayAdapter<String> mNewDevicesArrayAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Setup the window
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);setContentView(R.layout.device_list);// Set result CANCELED in case the user backs out
        setResult(Activity.RESULT_CANCELED);// Initialize the button to perform device discoveryButton 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 devicesmPairedDevicesArrayAdapter = 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 devicesListView pairedListView = (ListView) findViewById(R.id.paired_devices);pairedListView.setAdapter(mPairedDevicesArrayAdapter);pairedListView.setOnItemClickListener(mDeviceClickListener);// Find and set up the ListView for newly discovered devicesListView newDevicesListView = (ListView) findViewById(R.id.new_devices);newDevicesListView.setAdapter(mNewDevicesArrayAdapter);newDevicesListView.setOnItemClickListener(mDeviceClickListener);// Register for broadcasts when a device is discoveredIntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);this.registerReceiver(mReceiver, filter);// Register for broadcasts when discovery has finishedfilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);this.registerReceiver(mReceiver, filter);// Get the local Bluetooth adaptermBtAdapter = BluetoothAdapter.getDefaultAdapter();// Get a set of currently paired devicesSet<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();// If there are paired devices, add each one to the ArrayAdapterif (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);}}@Overrideprotected void onDestroy() {super.onDestroy();// Make sure we're not doing discovery anymoreif (mBtAdapter != null) {mBtAdapter.cancelDiscovery();}// Unregister broadcast listenersthis.unregisterReceiver(mReceiver);}/*** Start device discover with the BluetoothAdapter*/private void doDiscovery() {if (D) Log.d(TAG, "doDiscovery()");// Indicate scanning in the titlesetProgressBarIndeterminateVisibility(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 itif (mBtAdapter.isDiscovering()) {mBtAdapter.cancelDiscovery();}// Request discover from BluetoothAdapter
        mBtAdapter.startDiscovery();}// The on-click listener for all devices in the ListViewsprivate 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 ViewString info = ((TextView) v).getText().toString();String address = info.substring(info.length() - 17);// Create the result Intent and include the MAC addressIntent 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 finishedprivate final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();// When discovery finds a deviceif (BluetoothDevice.ACTION_FOUND.equals(action)) {// Get the BluetoothDevice object from the IntentBluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// If it's already paired, skip it, because it's been listed alreadyif (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);setTitle(R.string.select_device);if (mNewDevicesArrayAdapter.getCount() == 0) {String noDevices = getResources().getText(R.string.none_found).toString();mNewDevicesArrayAdapter.add(noDevices);}}}};}

View Code

这边,学习到了3点:

第一点:

在onCreate()方法中,设置:如果用户按下返回键,设置CANCELED

// Set result CANCELED in case the user backs out
setResult(Activity.RESULT_CANCELED);

第二点:

将aty以dialog形式显示:需要在AndroidManifest.xml配置

<activity android:name=".DeviceListActivity"android:label="@string/select_device"android:theme="@android:style/Theme.Holo.Dialog"android:configChanges="orientation|keyboardHidden" />

第三点:

将result设置成public static的。

// Return Intent extra
public static String EXTRA_DEVICE_ADDRESS = "device_address";

 // Create the result Intent and include the MAC addressIntent intent = new Intent();intent.putExtra(EXTRA_DEVICE_ADDRESS, address);

前一个aty:

// Get the device MAC addressString address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);

BluetoothChatService源码:

/** 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 {// Debuggingprivate static final String TAG = "BluetoothChatService";private static final boolean D = true;// Name for the SDP record when creating server socketprivate static final String NAME_SECURE = "BluetoothChatSecure";private static final String NAME_INSECURE = "BluetoothChatInsecure";// Unique UUID for this applicationprivate static final UUID MY_UUID_SECURE =UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");private static final UUID MY_UUID_INSECURE =UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");// Member fieldsprivate final BluetoothAdapter mAdapter;private final Handler mHandler;private AcceptThread mSecureAcceptThread;private AcceptThread mInsecureAcceptThread;private ConnectThread mConnectThread;private ConnectedThread mConnectedThread;private int mState;// Constants that indicate the current connection statepublic static final int STATE_NONE = 0;       // we're doing nothingpublic static final int STATE_LISTEN = 1;     // now listening for incoming connectionspublic static final int STATE_CONNECTING = 2; // now initiating an outgoing connectionpublic 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 updatemHandler.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 connectionif (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}// Cancel any thread currently running a connectionif (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}setState(STATE_LISTEN);// Start the thread to listen on a BluetoothServerSocketif (mSecureAcceptThread == null) {mSecureAcceptThread = new AcceptThread(true);mSecureAcceptThread.start();}if (mInsecureAcceptThread == null) {mInsecureAcceptThread = new AcceptThread(false);mInsecureAcceptThread.start();}}/*** Start the ConnectThread to initiate a connection to a remote device.* @param device  The BluetoothDevice to connect* @param secure Socket Security type - Secure (true) , Insecure (false)*/public synchronized void connect(BluetoothDevice device, boolean secure) {if (D) Log.d(TAG, "connect to: " + device);// Cancel any thread attempting to make a connectionif (mState == STATE_CONNECTING) {if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}}// Cancel any thread currently running a connectionif (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}// Start the thread to connect with the given devicemConnectThread = new ConnectThread(device, secure);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, BluetoothDevicedevice, final String socketType) {if (D) Log.d(TAG, "connected, Socket Type:" + socketType);// Cancel the thread that completed the connectionif (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}// Cancel any thread currently running a connectionif (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}// Cancel the accept thread because we only want to connect to one deviceif (mSecureAcceptThread != null) {mSecureAcceptThread.cancel();mSecureAcceptThread = null;}if (mInsecureAcceptThread != null) {mInsecureAcceptThread.cancel();mInsecureAcceptThread = null;}// Start the thread to manage the connection and perform transmissionsmConnectedThread = new ConnectedThread(socket, socketType);mConnectedThread.start();// Send the name of the connected device back to the UI ActivityMessage 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 (mSecureAcceptThread != null) {mSecureAcceptThread.cancel();mSecureAcceptThread = null;}if (mInsecureAcceptThread != null) {mInsecureAcceptThread.cancel();mInsecureAcceptThread = 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 ConnectedThreadsynchronized (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() {// Send a failure message back to the ActivityMessage msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);Bundle bundle = new Bundle();bundle.putString(BluetoothChat.TOAST, "Unable to connect device");msg.setData(bundle);mHandler.sendMessage(msg);// Start the service over to restart listening modeBluetoothChatService.this.start();}/*** Indicate that the connection was lost and notify the UI Activity.*/private void connectionLost() {// Send a failure message back to the ActivityMessage msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);Bundle bundle = new Bundle();bundle.putString(BluetoothChat.TOAST, "Device connection was lost");msg.setData(bundle);mHandler.sendMessage(msg);// Start the service over to restart listening modeBluetoothChatService.this.start();}/*** 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 socketprivate final BluetoothServerSocket mmServerSocket;private String mSocketType;public AcceptThread(boolean secure) {BluetoothServerSocket tmp = null;mSocketType = secure ? "Secure":"Insecure";// Create a new listening server sockettry {if (secure) {tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,MY_UUID_SECURE);} else {tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, MY_UUID_INSECURE);}} catch (IOException e) {Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);}mmServerSocket = tmp;}public void run() {if (D) Log.d(TAG, "Socket Type: " + mSocketType +"BEGIN mAcceptThread" + this);setName("AcceptThread" + mSocketType);BluetoothSocket socket = null;// Listen to the server socket if we're not connectedwhile (mState != STATE_CONNECTED) {try {// This is a blocking call and will only return on a// successful connection or an exceptionsocket = mmServerSocket.accept();} catch (IOException e) {Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);break;}// If a connection was acceptedif (socket != null) {synchronized (BluetoothChatService.this) {switch (mState) {case STATE_LISTEN:case STATE_CONNECTING:// Situation normal. Start the connected thread.
                            connected(socket, socket.getRemoteDevice(),mSocketType);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, socket Type: " + mSocketType);}public void cancel() {if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);try {mmServerSocket.close();} catch (IOException e) {Log.e(TAG, "Socket Type" + mSocketType + "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;private String mSocketType;public ConnectThread(BluetoothDevice device, boolean secure) {mmDevice = device;BluetoothSocket tmp = null;mSocketType = secure ? "Secure" : "Insecure";// Get a BluetoothSocket for a connection with the// given BluetoothDevicetry {if (secure) {tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);} else {tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);}} catch (IOException e) {Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);}mmSocket = tmp;}public void run() {Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);setName("ConnectThread" + mSocketType);// Always cancel discovery because it will slow down a connection
            mAdapter.cancelDiscovery();// Make a connection to the BluetoothSockettry {// This is a blocking call and will only return on a// successful connection or an exception
                mmSocket.connect();} catch (IOException e) {// Close the sockettry {mmSocket.close();} catch (IOException e2) {Log.e(TAG, "unable to close() " + mSocketType +" socket during connection failure", e2);}connectionFailed();return;}// Reset the ConnectThread because we're donesynchronized (BluetoothChatService.this) {mConnectThread = null;}// Start the connected thread
            connected(mmSocket, mmDevice, mSocketType);}public void cancel() {try {mmSocket.close();} catch (IOException e) {Log.e(TAG, "close() of connect " + mSocketType + " 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, String socketType) {Log.d(TAG, "create ConnectedThread: " + socketType);mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;// Get the BluetoothSocket input and output streamstry {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 connectedwhile (true) {try {// Read from the InputStreambytes = mmInStream.read(buffer);// Send the obtained bytes to the UI ActivitymHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer).sendToTarget();} catch (IOException e) {Log.e(TAG, "disconnected", e);connectionLost();// Start the service over to restart listening modeBluetoothChatService.this.start();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 ActivitymHandler.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);}}}
}

View Code

AcceptThread:蓝牙服务端socket监听线程.:

    private class AcceptThread extends Thread {// 本地的服务端socketprivate final BluetoothServerSocket mmServerSocket;public AcceptThread() {BluetoothServerSocket tmp = null;// 创建一个用于监听的服务端socket,通过下面这个方法,NAME参数没关系,MY_UUID是确定唯一通道的标示符,用于连接的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;// 不断的监听,直到状态被改变成已连接状态while (mState != STATE_CONNECTED) {try {// 这是一个会产生阻塞的方法accept,要不就是成功地建立一个连接,要不就是返回一个异常socket = mmServerSocket.accept();} catch (IOException e) {Log.e(TAG, "accept() failed", e);break;}// 连接已经建立成功if (socket != null) {synchronized (BluetoothChatService.this) { // 同步块,同一时间,只有一个线程可以访问该区域switch (mState) {case STATE_LISTEN:case STATE_CONNECTING:// 状态正常,开始进行线程通信,实际就是开启通信线程ConnectedThread
                            connected(socket, socket.getRemoteDevice());break;case STATE_NONE:case STATE_CONNECTED:// 未准备或已连接状态. 关闭新建的这个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() { //关闭服务端的socketif (D) Log.d(TAG, "cancel " + this);try {mmServerSocket.close();} catch (IOException e) {Log.e(TAG, "close() of server failed", e);}}}

View Code

ConnectThread:蓝牙socket连接线程:

    private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device) {mmDevice = device;BluetoothSocket tmp = null;// 通过远程设备以及唯一的UUID创建一个用于连接的sockettry {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");// 一定要停止扫描,不然会减慢连接速度
            mAdapter.cancelDiscovery();// 连接到服务端的sockettry {// connect方法也会造成阻塞,直到成功连接,或返回一个异常
                mmSocket.connect();} catch (IOException e) {connectionFailed(); //连接失败发送要Toast的消息// 关闭sockettry {mmSocket.close();} catch (IOException e2) {Log.e(TAG, "unable to close() socket during connection failure", e2);}// 连接失败了,把软件变成监听模式,可以让别的设备来连接BluetoothChatService.this.start();return;}// 重置连接线程,因为我们已经完成了synchronized (BluetoothChatService.this) {mConnectThread = null;}// 开始进行线程通信,实际就是开启通信线程ConnectedThread
            connected(mmSocket, mmDevice);}public void cancel() { // 关闭连接用的sockettry {mmSocket.close();} catch (IOException e) {Log.e(TAG, "close() of connect socket failed", e);}}}

View Code

ConnectedThread:连接后的通信线程:

    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; // 这个是之前的用于连接的socketInputStream tmpIn = null;OutputStream tmpOut = null;// 从连接的socket里获取InputStream和OutputStreamtry {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;// 已经连接上以后持续从通道中监听输入流的情况while (true) {try {// 从通道的输入流InputStream中读取数据到buffer数组中bytes = mmInStream.read(buffer);// 将获取到数据的消息发送到UI界面,同时也把内容buffer发过去显示mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer).sendToTarget();} catch (IOException e) {Log.e(TAG, "disconnected", e);connectionLost(); // 连接异常断开的时候发送一个需要Toast的消息,让软件进行Toastbreak;}}}/*** Write to the connected OutStream.* @param buffer  The bytes to write*/public void write(byte[] buffer) { // 这个方法用于把发送内容写到通道的OutputStream中,会在发信息是被调用try {mmOutStream.write(buffer); //将buffer内容写进通道// 用于将自己发送给对方的内容也在UI界面显示mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();} catch (IOException e) {Log.e(TAG, "Exception during write", e);}}public void cancel() { //关闭socket,即关闭通道try {mmSocket.close();} catch (IOException e) {Log.e(TAG, "close() of connect socket failed", e);}}}

View Code

参考:

http://www.aichengxu.com/view/4418179

http://blog.csdn.net/wyzxk888/article/details/7543364

转载于:https://www.cnblogs.com/H-BolinBlog/p/5548433.html

Bluetooth篇 开发实例之十 官网的Bluetooth Chat sample app.相关推荐

  1. Bluetooth篇 开发实例之六 蓝牙RSSI计算距离

    计算公式: d = 10^((abs(RSSI) - A) / (10 * n)) 其中: d - 计算所得距离 RSSI - 接收信号强度(负值) A - 发射端和接收端相隔1米时的信号强度 n - ...

  2. tornado web高级开发项目之抽屉官网的页面登陆验证、form验证、点赞、评论、文章分页处理、发送邮箱验证码、登陆验证码、注册、发布文章、上传图片...

    本博文将一步步带领你实现抽屉官网的各种功能:包括登陆.注册.发送邮箱验证码.登陆验证码.页面登陆验证.发布文章.上传图片.form验证.点赞.评论.文章分页处理以及基于tornado的后端和ajax的 ...

  3. 快速开发像vue elementui官网一样的api查询网站

    推荐vuepress 附官网VuePress 中文文档 | VuePress 中文网 前言 一直想做公司的api封装查询网站,百度寻找工具返回结果大多是 gitbook 于是开始各种查询GitBook ...

  4. javaEE开发如何在oracle官网下载安装jdk?(java SE 8u5 JDK 和 Java EE 7 SDK with JDK 7 U45的区别 )

    做javaEE开发,想到oracle官网上下载JDK使用,但是到底下载那个呢? 一.java SE 8u5 JDK ,如图 二. Java EE 7 SDK with JDK 7 U45,如图: 本人 ...

  5. 使用Django1.7开发熙鱿记官网

    Django升级1.7了,根据业务的需求,我们也准备开发新版的网站,新加入会员管理,订单查看等新功能,界面也会重新调整.官网: http://www.youyutiao.com 熙鱿记是一个什么样的站 ...

  6. 初学Web前端会用到开发工具(附官网下载地址)

    目前市面上比较流行的前端开发工具主要有WebStorm.Vscode.Sublime.HBuilder.notepad++.EditPlus.记事本等,今天介绍一下这些开发工具,并且给出了下载地址. ...

  7. 软件推荐:Web前端初学者会用到开发工具(附官网下载地址)

    目前市面上比较流行的前端开发工具主要有WebStorm.Vscode.Sublime.HBuilder.notepad++.EditPlus.记事本等,今天介绍一下这些开发工具,并且给出了下载地址,下 ...

  8. hbuilder前端需要的插件_初学Web前端会用到开发工具(附官网下载地址)

    目前市面上比较流行的前端开发工具主要有WebStorm.Vscode.Sublime.HBuilder.notepad++.EditPlus.记事本等,今天介绍一下这些开发工具,并且给出了下载地址. ...

  9. CSS弹出二级多列菜单和DIV布局实例 - 仿IBM官网首页

    图 顶层CSS菜单,弹出二级菜单,二级菜单多列: 主体部分三个DIV布局: 这个是IBM官网布局的样子: 代码,拿去花吧:我的奶酪很多: 有不清楚的地方可以问我,qq 513979805 <!D ...

最新文章

  1. 泛型(Generic)-反射泛形-Dao
  2. matlab计算每个细胞面积,手把手教你用 Imaris 计算细胞面积
  3. firedac连接mysql,FireDAC连接数据库
  4. myeclipse springboot 运行内存溢出_springboot学习心得 - aowumao
  5. 保留数据给硬盘增加分区
  6. 不使用任何路由协议使3台路由器通信
  7. 谷歌浏览器有哪些好看的主题_Kibou 简洁的Typecho主题
  8. 在线CSS3压缩美化格式化
  9. javaEE常用开源框架的认识及概述,带你深入探索Java开发世界
  10. 内网穿透软件NPS--客户端NPC SDK交叉编译ARM64位库
  11. Linux平台开源浏览器
  12. (实战3)tasklist(查看进程)和taskkill(结束进程)的使用
  13. Binder机制之Service Manager(大内总管)
  14. 小程序用户行为数据监测与分析以及案例分享
  15. integrate函数python_python – Sympy:integrate()奇怪的输出
  16. 【Comet OJ - 2019国庆欢乐赛 F】 高速公路
  17. 【php毕业设计】基于php+mysql+mvc的网上留言管理系统设计与实现(毕业论文+程序源码)——网上留言管理系统
  18. echarts-箱线图(盒须图)
  19. 水果分割论文、代码和数据集汇总
  20. 每日一学 | 2021-05-19 | Power BI 学习笔记03、04

热门文章

  1. 传说中的程序员十层楼
  2. 【医学图像处理】MRI T1, T2 PD-加权成像
  3. 基于FPGA的PCI接口电路设计
  4. web前端基础——定位
  5. window.print() 前端页面打印与预览PDF
  6. 什么是EDI许可证办理所需条件
  7. php 6 下载图片,[独家全程图解]ThinkPHP6框架的下载与安装
  8. mysql 数据库生命周期_MySQL生命周期
  9. WordPress PHP版本:2023年用于WordPress的最佳PHP版本
  10. 女孩子可以做什么副业,适合女孩做的副业推荐