• Bluetooth篇 开发实例之十 官网的Bluetooth Chat sample app.


    运行的时候,会报错:

    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 {
        // 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_SECURE = 1;
        private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;
        private static final int REQUEST_ENABLE_BT = 3;
    
        // Layout Views
        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) {
            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 adapter
            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) {
                  // 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();
            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 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);
                mOutEditText.setText(mOutStringBuffer);
            }
        }
    
        // 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;
            }
        };
    
        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 BluetoothChatService
        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:
                        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 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_SECURE:
                // When DeviceListActivity returns with a device to connect
                if (resultCode == Activity.RESULT_OK) {
                    connectDevice(data, true);
                }
                break;
            case REQUEST_CONNECT_DEVICE_INSECURE:
                // When DeviceListActivity returns with a device to connect
                if (resultCode == Activity.RESULT_OK) {
                    connectDevice(data, false);
                }
                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
                    setupChat();
                } else {
                    // User did not enable Bluetooth or an error occurred
                    Log.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 address
            String address = data.getExtras()
                .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
            // Get the BluetoothDevice object
            BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
            // Attempt to connect to the device
            mChatService.connect(device, secure);
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.option_menu, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            Intent serverIntent = null;
            switch (item.getItemId()) {
            case R.id.secure_connect_scan:
                // Launch the DeviceListActivity to see devices and do scan
                serverIntent = 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 scan
                serverIntent = 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()方法

        @Override
        public 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 adapter
            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) {
                  // 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菜单触发事件:

       @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.option_menu, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            Intent serverIntent = null;
            switch (item.getItemId()) {
            case R.id.secure_connect_scan:
                // Launch the DeviceListActivity to see devices and do scan
                serverIntent = 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 scan
                serverIntent = 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 {
        // 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 in case the user backs out
            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() + "
    " + 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();
            }
    
            // Unregister broadcast listeners
            this.unregisterReceiver(mReceiver);
        }
    
        /**
         * Start device discover with the BluetoothAdapter
         */
        private void doDiscovery() {
            if (D) Log.d(TAG, "doDiscovery()");
    
            // Indicate scanning in the title
            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
                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() + "
    " + 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 address
     Intent intent = new Intent();
     intent.putExtra(EXTRA_DEVICE_ADDRESS, address);

    前一个aty:

    // Get the device MAC address
      String 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 {
        // 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_SECURE = "BluetoothChatSecure";
        private static final String NAME_INSECURE = "BluetoothChatInsecure";
    
        // Unique UUID for this application
        private 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 fields
        private 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 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
            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;}
    
            setState(STATE_LISTEN);
    
            // Start the thread to listen on a BluetoothServerSocket
            if (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 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, 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, BluetoothDevice
                device, final String socketType) {
            if (D) Log.d(TAG, "connected, Socket Type:" + socketType);
    
            // 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 (mSecureAcceptThread != null) {
                mSecureAcceptThread.cancel();
                mSecureAcceptThread = null;
            }
            if (mInsecureAcceptThread != null) {
                mInsecureAcceptThread.cancel();
                mInsecureAcceptThread = null;
            }
    
            // Start the thread to manage the connection and perform transmissions
            mConnectedThread = new ConnectedThread(socket, socketType);
            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 (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 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() {
            // Send a failure message back to the Activity
            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);
    
            // Start the service over to restart listening mode
            BluetoothChatService.this.start();
        }
    
        /**
         * Indicate that the connection was lost and notify the UI Activity.
         */
        private void connectionLost() {
            // Send a failure message back to the Activity
            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);
    
            // Start the service over to restart listening mode
            BluetoothChatService.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 socket
            private final BluetoothServerSocket mmServerSocket;
            private String mSocketType;
    
            public AcceptThread(boolean secure) {
                BluetoothServerSocket tmp = null;
                mSocketType = secure ? "Secure":"Insecure";
    
                // Create a new listening server socket
                try {
                    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 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, "Socket Type: " + mSocketType + "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(),
                                        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 BluetoothDevice
                try {
                    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 BluetoothSocket
                try {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    mmSocket.connect();
                } catch (IOException e) {
                    // Close the socket
                    try {
                        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 done
                synchronized (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 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();
                        // Start the service over to restart listening mode
                        BluetoothChatService.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 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);
                }
            }
        }
    }
    View Code

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

        private class AcceptThread extends Thread {
            // 本地的服务端socket
            private 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() { //关闭服务端的socket
                if (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创建一个用于连接的socket
                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");
    
                // 一定要停止扫描,不然会减慢连接速度
                mAdapter.cancelDiscovery();
    
                // 连接到服务端的socket
                try {
                    // connect方法也会造成阻塞,直到成功连接,或返回一个异常
                    mmSocket.connect();
                } catch (IOException e) {
                    connectionFailed(); //连接失败发送要Toast的消息
                    // 关闭socket
                    try {
                        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() { // 关闭连接用的socket
                try {
                    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; // 这个是之前的用于连接的socket
                InputStream tmpIn = null;
                OutputStream tmpOut = null;
    
                // 从连接的socket里获取InputStream和OutputStream
                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;
    
                // 已经连接上以后持续从通道中监听输入流的情况
                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的消息,让软件进行Toast
                        break;
                    }
                }
            }
    
            /**
             * 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

  • 相关阅读:
    jQuery Mobile-jquery Mobile 怎么用ajax提交表单
    Passing data between pages in JQuery Mobile | Ram's Blog
    jQuery Mobile Docs
    jquery mobile header title左对齐 button右对齐
    jquery mobile -role
    jQueryMobile$(document).ready 等价于?( jQuery mobi...
    HTML5,jQuery Mobile 可视化生成器 Tiggzi
    WebApi系列~开放的CORS,跨域资源访问对所有人开放
    WebApi系列~目录
    WebApi系列~基于单请求封装多请求的设计~请求的安全性设计与实现
  • 原文地址:https://www.cnblogs.com/H-BolinBlog/p/5548433.html
Copyright © 2020-2023  润新知