zoukankan      html  css  js  c++  java
  • BluetoothChat例子解析

     1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     2       package="com.example.android.BluetoothChat"
     3       android:versionCode="1"
     4       android:versionName="1.0">
     5     <uses-sdk minSdkVersion="6" />
     6     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
     7     <uses-permission android:name="android.permission.BLUETOOTH" />
     8 
     9     <application android:label="@string/app_name"
    10                  android:icon="@drawable/app_icon" >
    11         <activity android:name=".BluetoothChat"
    12                   android:label="@string/app_name"
    13                   android:configChanges="orientation|keyboardHidden">
    14             <intent-filter>
    15                 <action android:name="android.intent.action.MAIN" />
    16                 <category android:name="android.intent.category.LAUNCHER" />
    17             </intent-filter>
    18         </activity>
    19         <activity android:name=".DeviceListActivity"
    20                   android:label="@string/select_device"
    21                   android:theme="@android:style/Theme.Dialog"
    22                   android:configChanges="orientation|keyboardHidden" />
    23     </application>
    24 </manifest>

    第13行、第22行:android:configChanges="orientation|keyboardHidden"

    参考:Android横竖屏的设置和使用

    在androidmanifest.xml中加入配置android:configChanges="orientation|keyboardHidden|navigation“,这样在程序中. Activity就不会重复的调用onCreate(),甚至不会调用onPause、onResume,只会调用一个onConfigurationChanged(Configuration newConfig)的方法。

    如果只是横屏,或者只支持竖屏模式,直接在AndroidManifest.xml的Activity元素加入属性android:screenOrientation=”landscape”。(landscape是横向,portrait是纵向)

    第21行:android:theme="@android:style/Theme.Dialog"

    参考:Android风格与主题(style and theme)

    menu/option_menu.xml

    1 <menu xmlns:android="http://schemas.android.com/apk/res/android">
    2     <item android:id="@+id/scan"
    3           android:icon="@android:drawable/ic_menu_search"
    4           android:title="@string/connect" />
    5     <item android:id="@+id/discoverable"
    6           android:icon="@android:drawable/ic_menu_mylocation"
    7           android:title="@string/discoverable" />
    8 </menu>

    layout/main.xml

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:orientation="vertical"
     3     android:layout_width="fill_parent"
     4     android:layout_height="fill_parent"
     5     >
     6     <ListView android:id="@+id/in"
     7         android:layout_width="fill_parent"
     8         android:layout_height="fill_parent"
     9         android:stackFromBottom="true"
    10         android:transcriptMode="alwaysScroll"
    11         android:layout_weight="1"
    12     />
    13     <LinearLayout
    14         android:orientation="horizontal"
    15         android:layout_width="fill_parent"
    16         android:layout_height="wrap_content"
    17         >
    18         <EditText android:id="@+id/edit_text_out"
    19             android:layout_width="wrap_content"
    20             android:layout_height="wrap_content"
    21             android:layout_weight="1"
    22             android:layout_gravity="bottom"
    23         />
    24         <Button android:id="@+id/button_send"
    25             android:layout_width="wrap_content"
    26             android:layout_height="wrap_content"
    27             android:text="@string/send"
    28         />
    29     </LinearLayout>
    30 </LinearLayout>

    custom_title.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center_vertical"
      >
      <TextView android:id="@+id/title_left_text"
          android:layout_alignParentLeft="true"
          android:ellipsize="end" // 省略号在结尾
          android:singleLine="true"
          style="?android:attr/windowTitleStyle"
          android:layout_width="wrap_content"
          android:layout_height="fill_parent"
          android:layout_weight="1"
        />
        <TextView android:id="@+id/title_right_text"
            android:layout_alignParentRight="true"
            android:ellipsize="end"
            android:singleLine="true"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:textColor="#fff"
            android:layout_weight="1"
        />
    </RelativeLayout>

     参考:android ellipsize的使用

    在onCreate方法之中

    自定义标题栏

    requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

    getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

    获取BluetoothAdapter对象

    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    Activity生命周期日志

    1. 启动BluetoothChat程序

    10-30 22:01:03.983: ERROR/BluetoothChat(5133): +++ ON CREATE +++
    10-30 22:01:04.003: ERROR/BluetoothChat(5133): ++ ON START ++
    10-30 22:01:04.013: DEBUG/BluetoothChat(5133): setupChat()
    10-30 22:01:04.013: ERROR/BluetoothChat(5133): + ON RESUME +
    10-30 22:01:04.033: INFO/BluetoothChat(5133): MESSAGE_STATE_CHANGE: 1

    2. 屏幕进入待机状态(或打开蓝牙权限请求对话框)

    10-30 22:02:45.863: ERROR/BluetoothChat(5133): - ON PAUSE -

    3. 屏幕从待机状态恢复过来

    10-30 22:03:24.743: ERROR/BluetoothChat(5133): + ON RESUME +

    4.1 点击返回按键

    10-30 22:04:38.793: ERROR/BluetoothChat(5133): - ON PAUSE -
    10-30 22:04:38.903: ERROR/BluetoothChat(5133): -- ON STOP --
    10-30 22:04:38.903: ERROR/BluetoothChat(5133): --- ON DESTROY ---
    10-30 22:04:38.913: INFO/BluetoothChat(5133): MESSAGE_STATE_CHANGE: 0

    4.2 点击HOME键

    10-30 22:23:53.223: ERROR/BluetoothChat(5133): - ON PAUSE -
    10-30 22:23:53.353: ERROR/BluetoothChat(5133): -- ON STOP --

    -> 长摁HOME键 点击BluetoothChat应用图标

    10-30 22:25:17.093: ERROR/BluetoothChat(5133): ++ ON START ++
    10-30 22:25:17.093: ERROR/BluetoothChat(5133): + ON RESUME +

    在onStart方法中

    如果蓝牙已经打开 直接调用setupChat方法 如果蓝牙未打开 先打开蓝牙 再调用setupChat方法

    打开 蓝牙权限请求 对话框

    if (!mBluetoothAdapter.isEnabled()) {
          Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
          startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    }

    在setupChat方法中

    获取ListView 设置Adapter

    ArrayAdapter mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
    ListView mConversationView = (ListView) findViewById(R.id.in);
    mConversationView.setAdapter(mConversationArrayAdapter);

    layout/message.xml

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:padding="5dp"
    /> 

    设置TextView.OnEditorActionListener

    EditText mOutEditText = (EditText) findViewById(R.id.edit_text_out);
    mOutEditText.setOnEditorActionListener(mWriteListener);

    创建BluetoothChatService对象

    mChatService = new BluetoothChatService(this, mHandler);

    在onResume方法中

    调用service的start方法

    if (mChatService != null) {
          if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
             mChatService.start();
         }
    }

    在onDestroy方法中

    调用service的stop方法

    if (mChatService != null) mChatService.stop();

    BluetoothChatService

    在BluetoothChatService构造方法中

    BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();

    在BluetoothChatService的start方法中

    10-30 23:09:01.753: DEBUG/BluetoothChatService(7263): start

    创建AcceptThread线程对象 调用线程的start方法

    AcceptThread mAcceptThread = new AcceptThread();
    mAcceptThread.start();

    在AcceptThread构造方法中

    创建BluetoothServerSocket对象

    BluetoothServerSocket mmServerSocket = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

    10-30 23:09:01.773: DEBUG/BluetoothChatService(7263): BEGIN mAcceptThreadThread[Thread-10,5,main]

    10-30 23:09:01.773: DEBUG/BluetoothChatService(7263): setState() 0 [STATE_NONE] -> 1 [STATE_LISTEN]

    在AcceptThread的run方法中

    创建BluetoothSocket对象

    while (mState != STATE_CONNECTED) {

    BluetoothSocket socket = mmServerSocket.accept();

    如果socket不为空

    1. 如果mState为STATE_LISTEN、STATE_CONNECTING

    调用 connected(socket, socket.getRemoteDevice());

    2. 如果mState为STATE_NONE、STATE_CONNECTED

    调用 socket.close();

    }

    在connected(BluetoothSocket socket, BluetoothDevice device)方法中

    if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}

    创建ConnectedThread线程对象 调用线程的start方法

    ConnectedThread mConnectedThread = new ConnectedThread(socket);
    mConnectedThread.start();

    通过handler消息 发送建立好蓝牙连接的设备的名称

    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);

    在ConnectedThread构造方法中

    获取InputStream和OutputStream

    InputStream mmInStream = socket.getInputStream();

    OutputStream mmOutStream = socket.getOutputStream();

    在ConnectedThread的run方法中

    byte[] buffer = new byte[1024];

    int bytes;

    // Keep listening to the InputStream while connected
    while (true) {
        try {
            // Read from the InputStream
            bytes = mmInStream.read(buffer);

            // Send the obtained bytes to the UI Activity
            mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                    .sendToTarget();
        } catch (IOException e) {
            Log.e(TAG, "disconnected", e);
            connectionLost();
            break;
        }
    }

    在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);

    在BluetoothChat.sendMessage(String message)方法中

     if (message.length() > 0) {
    // Get the message bytes and tell the BluetoothChatService to write
    byte[] send = message.getBytes();
    mChatService.write(send);

    }

    在BluetoothChatService.write(byte[] out)方法中

    调用 mConnectedThread.write(out);

    在ConnectedThread.write(byte[] buffer)方法中

    mmOutStream.write(buffer);

    // Share the sent message back to the UI Activity
    mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
            .sendToTarget();

    obtainMessage得到的Handler对象不是自己创建的,而是从MessagePool 拿的,省去了创建对象申请内存的开销。

    所以以后使用的时候尽量使用 Message msg = handler.obtainMessage();的形式创建Message,不要自己New Message。至于message产生之后你使用sendToTarget或者是sendMessage效率影响并不大。

    在AcceptThread.cancel方法中

    调用 mmServerSocket.close();

    在ConnectedThread.cancel方法中

    调用 mmSocket.close();

    在ConnectThread.cancel方法中

    调用 mmSocket.close();

    在BluetoothChat.onCreateOptionsMenu(Menu menu)方法中

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.option_menu, menu);

    menu/option_menu.xml

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/scan"
              android:icon="@android:drawable/ic_menu_search"
              android:title="@string/connect" />
        <item android:id="@+id/discoverable"
              android:icon="@android:drawable/ic_menu_mylocation"
              android:title="@string/discoverable" />
    </menu>

    在BluetoothChat.onOptionsItemSelected(MenuItem item)方法中

    switch (item.getItemId()) {
    case R.id.scan:
        // Launch the DeviceListActivity to see devices and do scan
        Intent serverIntent = new Intent(this, DeviceListActivity.class);
        startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
        return true;
    case R.id.discoverable:
        // Ensure this device is discoverable by others
        ensureDiscoverable();
        return true;

    在BluetoothChat.ensureDiscoverable()方法中

    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);
    }

    在DeviceListActivity.onCreate方法中

    setContentView(R.layout.device_list);

    layout/device_list.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <TextView android:id="@+id/title_paired_devices"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/title_paired_devices"
            android:visibility="gone"
            android:background="#666"
            android:textColor="#fff"
            android:paddingLeft="5dp"
        />
        <ListView android:id="@+id/paired_devices"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:stackFromBottom="true"
            android:layout_weight="1"
        />
        <TextView android:id="@+id/title_new_devices"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/title_other_devices"
            android:visibility="gone"
            android:background="#666"
            android:textColor="#fff"
            android:paddingLeft="5dp"
        />
        <ListView android:id="@+id/new_devices"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:stackFromBottom="true"
            android:layout_weight="2"
        />
        <Button android:id="@+id/button_scan"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/button_scan"
        />
    </LinearLayout>

    PairedListView及其ArrayAdapter

    ArrayAdapter mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);

    ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
    pairedListView.setAdapter(mPairedDevicesArrayAdapter);
    pairedListView.setOnItemClickListener(mDeviceClickListener);

    newDevicesListView及其ArrayAdapter

    ArrayAdapter mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);

    ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
    newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
    newDevicesListView.setOnItemClickListener(mDeviceClickListener);

    创建DeviceListActivity.mReceiver对象

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            // When discovery finds a device
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // Get the BluetoothDevice object from the Intent
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // If it's already paired, skip it, because it's been listed already
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                }
            // When discovery is finished, change the Activity title
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                setProgressBarIndeterminateVisibility(false);
                setTitle(R.string.select_device);
                if (mNewDevicesArrayAdapter.getCount() == 0) {
                    String noDevices = getResources().getText(R.string.none_found).toString();
                    mNewDevicesArrayAdapter.add(noDevices);
                }
            }
        }
    };

    在DeviceListActivity.onCreate方法中

    注册mReceiver

    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    this.registerReceiver(mReceiver, filter);

    filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    this.registerReceiver(mReceiver, filter);

    获取pairedDevices

    BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();

    Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();

    将pairedDevices添加到pairedListView中

    for (BluetoothDevice device : pairedDevices) {
        mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }

    如果pairedDevices.size为0,显示No devices have been paired

    String noDevices = getResources().getText(R.string.none_paired).toString();
    mPairedDevicesArrayAdapter.add(noDevices);

    在DeviceListActivity.doDiscovery()方法中

    setProgressBarIndeterminateVisibility(true);
    setTitle(R.string.scanning);

    findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);

    if (mBtAdapter.isDiscovering()) {
         mBtAdapter.cancelDiscovery();
    }

    mBtAdapter.startDiscovery();

    在DeviceListActivity.onCreate方法中

    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

    在DeviceListActivity.onDestroy()方法中

    mBtAdapter.cancelDiscovery();

    this.unregisterReceiver(mReceiver);

    在mDeviceClickListener.onItemClick方法中

    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();

    在BluetoothChatService中使用到关键字synchronized的地方有

    1. private synchronized void setState(int state)

    2. public synchronized int getState()

    3. public synchronized void start()

    4. public synchronized void connect(BluetoothDevice device)

    5. public synchronized void connected(BluetoothSocket socket, BluetoothDevice device)

    6. public synchronized void stop()

    7. 在BluetoothChatService.write(byte[] out)方法中用到

    synchronized (this)

    8. 在AcceptThread.run()方法中用到

    synchronized (BluetoothChatService.this)

    9. 在ConnectThread.run()方法中用到

    synchronized (BluetoothChatService.this)

    参考:Java synchronized详解

  • 相关阅读:
    重构SWF为fla文件三
    重构SWF为fla文件四
    SQL Server中删除重复数据的几个方法
    重构SWF为fla文件五
    重构SWF为fla文件六
    MySQL下载与安装
    C++根据.h文件批量生成需要的函数框架
    pku acm 2362 square 解题报告
    Ackerman 函数的解法
    Web.Config 的读写
  • 原文地址:https://www.cnblogs.com/fengzhblog/p/2746675.html
Copyright © 2011-2022 走看看