BluetoothChat

      本來想自己寫一個藍牙聊天的程序的,可是看了好幾天官方提供的源碼,真的看得想哭,真的太蛋筒了,終於明白,基礎差,在做稍微複雜寫的程序的時候,就能看出自己的不足,就有瓶頸。從這個藍牙開發來說,可以說是我做這個Android以來遇到的第一個比較頭痛的問題,但是由於項目要用到藍牙,所以這些天只能硬着皮頭看下去,不得不說,越看,學到的東西越多,但是我也不能一一說出來,但是,不得不說,任然還有不懂的地方。尤其是多線程那一塊,一定要重新認真看。不得不說,這個程序,給我了很大的刺激,讓我明白,我還有太多要學習的。

       我這裏把我這幾天看的,然後今天整理的官方的程序寫上了我自己的註釋,不是很多,但是最主要的是,我把程序的部分調序了,因爲我覺得這是一個理解問題,就是按照什麼樣的順序和思路來設計程序的。這樣調序,可以很方便理解。但是,我不能保證我的理解思路就是適合別人的,但是至少適合我。還有就是service裏面的幾個線程的程序,我沒有註釋,因爲真的不知道怎麼註釋比較好,不想直接翻譯他提供的英語。

       我依次說下他這三個代碼的思路吧,我覺得第一個DeviceListActivity這個是最容易理解的,也是思路最明確的,內容也最少。大的方面就是兩點,一個是顯示本地已配對設備;二是顯示搜索到的設備。先說第一個,顯示本地已配對設備就直接通過一個set集合把pairedDevices遍歷一遍,然後顯示在listview裏面。第二個稍微複雜點,因爲要先查找,然後再顯示出來。查找直接用startDiscovery就可以了。至於顯示就比較麻煩,因爲查找的過程是查找一個就發送一個廣播,然後繼續查找,直到查找結束。這就有一個問題,怎麼把查找到一個就顯示出一個呢?這就要用到BroadcastReceiver,這裏要設置兩個過濾器,用來處理不同的廣播,一個是用來處理查找到新設備的廣播,一個是用來處理結束查找的廣播。第一個廣播是最重要的,就是查到一個,通過這個過濾器,然後如果是這個廣播,就執行顯示操作。不過這裏顯示有兩部分,一個是顯示name,一個是MAC地址。不過這還麼有完,因爲你只是顯示在listview裏面了,但是你還要設置listview的選項監聽器,因爲我們要能選擇一個設備,然後就進行和設備的配對,至於怎麼配對待會再說,這裏要能響應,然後把MAC地址返回到BluetoothChat這個activity裏面,然後執行配對。所以這裏使用的是setResult,把Intent的內容傳回到主activity。這就是這個DeviceListActivity的全部功能。

        接下來說BluetoothChat這個activity。總的來說是一下幾個步驟,檢測藍牙,藍牙是否打開,藍牙的可被檢測性,建立連接,開始聊天服務,但是有一個是貫穿整個流程的,就是UI的動態更新。藍牙的檢測和打開,可被檢測這都很簡單,不多說了。主要是怎麼建立連接,這個要在service裏面說;這個activity最最主要的有兩點,一個是把信息顯示在在listview上,就是發送和接收到的信息都要顯示出來,這個其實也是在service的connectedThread裏面做的,但是顯示部分是在這個裏面,service裏只是通過handler把信息傳給這個activity,BluetoothChat這個activity只是負責顯示。另外一點就是連接狀態的顯示,分爲四個狀態,沒有操作,連接中,連接了,和接收監聽,我這個翻譯也許不準確,但是應該能理解這四個狀態,不過你從顯示上看,只能看到三個,而監聽這個是看不出來的。這四個狀態也是通過handler傳遞信息,然後顯示在狀態欄的。其實這裏面最難的就是這個handler的使用,因爲他有兩個功能,就是上面兩類信息的顯示。而第二類的顯示就是所謂的UI的更新,也就是界面裏能看到的動態listview和textview的顯示。不過我也不能一下子說清楚這個handler,(我也還在學習中),所以希望我們都能好好學習,嘻嘻。。。

        最後要說的就是這個service,也是最複雜的部分,其實這部分之所以複雜,主要是用到多線程,因爲,這裏面的很多方法都是阻塞線程的。大體上來說,我也只是說我知道的,這個服務,就是建立連接,那麼中間有那些可能性呢?connect,connectionFailed,connected,connectionLost。而這四種可能性我想大家也都可以有自己的理解,我的理解就是,第一個是連接兩個設備,第二個是連接失敗,第三個是連接成功了,第四個也是連接失敗,不過這個和第二個的區別在於,這個的前一狀態是第三個,而不是第一個。由這四個引出的是兩個線程,也是最重要的線程,ConnectThread和ConnectedThread,第一個是用來建立連接的,第二個是已經連接了,處理的是連接中的事,主要是兩點,一個是收發信息,一個是如果發生connectionLost怎麼辦。而第一個建立連接是通過createRfcommSocketToServiceRecord方法來實現的。但是我們前面說到了這裏有三個線程,還有一個是AcceptThread,這是一個監聽socket的線程,用來查看有沒有連接請求,我對他的理解是,他要監聽的是剛纔連接時說到的那個方法的UUID,然後進行連接。然後返回的是一個socket。這裏還有的就是UI的更新,這裏用到的是state的設置,然後通過handler把這個state返回去,然後更新UI。

        好吧,這就是我能理解的部分,還有就是把我的註釋的給展示出來吧。至於以後用到藍牙的部分,以後再更新吧。

        這是這個月最後一更了,這個月本來這應該是最有價值的一篇文章,結果,水平太菜了,沒辦法,只能做到這裏,五一回家玩玩吧,放鬆下。。。

DeviceListActivity.java

package com.example.bluetooth;

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.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.AdapterView.OnItemClickListener;
import android.widget.TextView;

public class DeviceListActivity extends Activity {

	private BluetoothAdapter mBtAdapter;
    private ArrayAdapter<String> mPairedDevicesArrayAdapter;
    private ArrayAdapter<String> mNewDevicesArrayAdapter;
    public static String EXTRA_DEVICE_ADDRESS = "device_address";
    
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		
		//這裏設置窗體爲不確定進度,因爲有progressbar
		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.device_list);
        //setResult(Activity.RESULT_CANCELED);
        
        Button scanButton = (Button) findViewById(R.id.button_scan);
        scanButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                doDiscovery();
                v.setVisibility(View.GONE);
            }
        });
        
        mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
        mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
        
        ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
        pairedListView.setAdapter(mPairedDevicesArrayAdapter);
        pairedListView.setOnItemClickListener(mDeviceClickListener);
        
        ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
        newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
        newDevicesListView.setOnItemClickListener(mDeviceClickListener);
        
        //註冊兩個過濾器,用來接收兩個廣播,一個是搜索到一個可用設備,一個是結束搜索
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        this.registerReceiver(mReceiver, filter);
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        this.registerReceiver(mReceiver, filter);
        
        //獲取本地藍牙適配器,把本地已配對設備顯示出來
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
            for (BluetoothDevice device : pairedDevices) {
                mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
            }
        } else {
            String noDevices = getResources().getText(R.string.none_paired).toString();
            mPairedDevicesArrayAdapter.add(noDevices);
        }
	}
	
	//搜索
	private void doDiscovery(){
		//設置顯示
		setProgressBarIndeterminateVisibility(true);
        setTitle(R.string.scanning);
        findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
        
        //如果正在搜索,取消正在搜索
        if (mBtAdapter.isDiscovering()) {
            mBtAdapter.cancelDiscovery();
        }
        
        //開始搜索
        mBtAdapter.startDiscovery();
	}
	
	//監聽器
	private OnItemClickListener mDeviceClickListener = new OnItemClickListener(){

		@Override
		public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
				long arg3) {
			// TODO Auto-generated method stub
			mBtAdapter.cancelDiscovery();
			
			//獲取內容,然後把後面17位的MAC地址取出來,連接只需要MAC地址
			String info = ((TextView) arg1).getText().toString();
			String address = info.substring(info.length() - 17);
			
			//把MAC地址傳回到主activity,調用setResult在finish之前傳回值
			Intent intent = new Intent();
            intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
            setResult(Activity.RESULT_OK, intent);
            finish();
			
		}};
    
	//註冊一個廣播接收,兩個不同的接收給出不同的動作
	private final BroadcastReceiver mReceiver = new BroadcastReceiver(){

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			String action = intent.getAction();
			
			if(BluetoothDevice.ACTION_FOUND.equals(action))
			{
				//把新發現的設備顯示出來
				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				if (device.getBondState() != BluetoothDevice.BOND_BONDED) 
				{
                    mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
				}
			}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);
                }
			}
			
		}};

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		if (mBtAdapter != null) 
		{
            mBtAdapter.cancelDiscovery();
        }
		//廣播要取消註冊,防止內存泄漏
		this.unregisterReceiver(mReceiver);
	}
		
	

}
BluetoothChat.java
package com.example.bluetooth;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.ActionBar;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
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;

public class BluetoothChat extends Activity {

	//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;
	
	public static final String DEVICE_NAME = "device_name";
    public static final String TOAST = "toast";
    
    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;
    
	private BluetoothAdapter mBluetoothAdapter = null;
	private BluetoothChatService mChatService = null;
	private ArrayAdapter<String> mConversationArrayAdapter;
	private ListView mConversationView;
    private EditText mOutEditText;
    private Button mSendButton;
    private StringBuffer mOutStringBuffer;
	private String mConnectedDeviceName = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		//本地如果沒有藍牙,提示沒有藍牙
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter == null) 
        {
            Toast.makeText(this, "沒有發現藍牙設備", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        
        //藍牙是否打開了,沒有的話,讓用戶選擇是否打開
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, RESULT_FIRST_USER);
        } else {
            if (mChatService == null) setupChat();
        }
        
	}

	//藍牙是否可以被檢測
	private void ensureDiscoverable() {
		// TODO Auto-generated method stub
		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);
	        }
	}
	
	//信息顯示
	private void setupChat() {
		// TODO Auto-generated method stub
		mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
        mConversationView = (ListView) findViewById(R.id.in);
        mConversationView.setAdapter(mConversationArrayAdapter);
        
        mOutEditText = (EditText) findViewById(R.id.edit_text_out);
        mOutEditText.setOnEditorActionListener(mWriteListener);
        
        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);
            }
        });
	}
	
	//EditText+Button響應,軟鍵盤設置
	private TextView.OnEditorActionListener mWriteListener =
	        new TextView.OnEditorActionListener() {
	        public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
	            if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
	                String message = view.getText().toString();
	                sendMessage(message);
	            }
	            return true;
	        }
	    };
	
	//開始聊天服務
	@Override
	protected synchronized void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		if (mChatService != null) 
		{
            if (mChatService.getState() == BluetoothChatService.STATE_NONE) 
            {
              mChatService.start();
            }
        }
	}
  
	protected void sendMessage(String message) {
		// TODO Auto-generated method stub
		//是否已經連接,沒有連接是不能發送信息的
		if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) 
		{
            Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
            return;
        }
		//發送信息
		if (message.length() > 0) 
		{
            byte[] send = message.getBytes();
            mChatService.write(send);
            mOutStringBuffer.setLength(0);
            mOutEditText.setText(mOutStringBuffer);
        }
		
	}

	//停止服務
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		if (mChatService != null) mChatService.stop();
	}

	//設置當前連接狀態的顯示
	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);
    }

    //從BluetoothChatService中讀取message,更新UI顯示
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_STATE_CHANGE:
                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;
                // 寫信息到緩存
                String writeMessage = new String(writeBuf);
                mConversationArrayAdapter.add("Me:  " + writeMessage);
                break;
            case MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // 從緩存中讀信息
                String readMessage = new String(readBuf, 0, msg.arg1);
                mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);
                break;
            case MESSAGE_DEVICE_NAME:
                // 保存設備名稱
                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;
            case MESSAGE_TOAST:
            	//打印toast
                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
                               Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };
    
    
    //DeviceActivity的結果返回響應
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
         switch (requestCode) {
         case REQUEST_CONNECT_DEVICE_SECURE:
             // 返回結果是要求一個連接
             if (resultCode == Activity.RESULT_OK) {
                 connectDevice(data, true);
             }
             break;
         case REQUEST_CONNECT_DEVICE_INSECURE:
             // 
             if (resultCode == Activity.RESULT_OK) {
                 connectDevice(data, false);
             }
             break;
         case REQUEST_ENABLE_BT:
             // 返回結果是要求打開藍牙
             if (resultCode == Activity.RESULT_OK) {
                 setupChat();
             } else {
                 Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
                 finish();
             }
         }
     }

    //連接設備
    private void connectDevice(Intent data, boolean secure) {
        String address = data.getExtras()
            .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        mChatService.connect(device, secure);
    }
    
    
    
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// TODO Auto-generated method stub
		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;
		
	}
}

BluetoothChatService.java

package com.example.bluetooth;

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;

public class BluetoothChatService {

	private final BluetoothAdapter mAdapter;
    private final Handler mHandler;
    private AcceptThread mSecureAcceptThread;
    private AcceptThread mInsecureAcceptThread;
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;
    
    private static final String NAME_SECURE = "BluetoothChatSecure";
    private static final String NAME_INSECURE = "BluetoothChatInsecure";
    
    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");
    	
    //設置UI的狀態
	private int mState;
	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
    
    public BluetoothChatService(Context context, Handler handler) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        //UI的動態顯示,顯示的是狀態
        mState = STATE_NONE;
        //信息的處理線程
        mHandler = handler;
    }
    
	public int getState() {
		// TODO Auto-generated method stub
		return mState;
	}
	
	//改變UI的狀態顯示
	private void setState(int stateListen) {
		// TODO Auto-generated method stub
		mState = stateListen;
        mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, stateListen, -1).sendToTarget();
	}

	//建立設備之間的連接
	public void connect(BluetoothDevice device, boolean secure) {
		// TODO Auto-generated method stub
		if (mState == STATE_CONNECTING) 
		{
            if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
        }
		
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
        
        mConnectThread = new ConnectThread(device, secure);
        mConnectThread.start();
        
        setState(STATE_CONNECTING);
	}
	
	//連接失敗的提示
	private void connectionFailed() {
        // 把連接失敗顯示在UI上
        Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothChat.TOAST, "Unable to connect device");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        // 重新開始服務
        BluetoothChatService.this.start();
    }
	
	//連接已建立,保持連接
	public synchronized void connected(BluetoothSocket socket, BluetoothDevice
            device, final String socketType) {
        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;
        }
        
        mConnectedThread = new ConnectedThread(socket, socketType);
        mConnectedThread.start();

        // 在UI上把當前連接的設備名顯示出來
        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);
    }
	
	//連接中斷
	private void connectionLost() {
        // 連接如果中斷了,就在UI上提示
        Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothChat.TOAST, "Device connection was lost");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        // 重新開始服務
        BluetoothChatService.this.start();
    }
	
	//開始聊天服務
	public void start() {
		// TODO Auto-generated method stub
		if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
		
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
        
        setState(STATE_LISTEN);

        // 開始監聽socket
        if (mSecureAcceptThread == null) {
            mSecureAcceptThread = new AcceptThread(true);
            mSecureAcceptThread.start();
        }
	}
	
	//停止服務
	public void stop() {
		// TODO Auto-generated method stub
		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);
	}
	
	
	//監聽連接請求線程
	private class AcceptThread extends Thread{
		//本地socket
		 private final BluetoothServerSocket mmServerSocket;
		 private String mSocketType;
		 
	     public AcceptThread(boolean secure){
	    	 BluetoothServerSocket tmp = null;
	    	 mSocketType = secure ? "Secure":"Insecure";

            // 創建一個新的服務socket
            try {
                if (secure) {
                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
                        MY_UUID_SECURE);
                } else {
                    tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
                            NAME_INSECURE, MY_UUID_INSECURE);
                }
            } catch (IOException e) {
            }
            mmServerSocket = tmp;
	     }
	     
	     public void run() {
	            setName("AcceptThread" + mSocketType);
	            BluetoothSocket socket = null;

	            //沒有建立連接
	            while (mState != STATE_CONNECTED) {
	                try {
	                    socket = mmServerSocket.accept();
	                } catch (IOException e) {
	                    break;
	                }

                // 建立了連接
                if (socket != null) {
                    synchronized (BluetoothChatService.this) {
                        switch (mState) {
                        case STATE_LISTEN:
                        case STATE_CONNECTING:
                            connected(socket, socket.getRemoteDevice(),
                                    mSocketType);
                            break;
                        case STATE_NONE:
                        case STATE_CONNECTED:
                            try {
                                socket.close();
                            } catch (IOException e) {
                            }
                            break;
                        }
                    }
                }
	        }
	     }
	     
	     public void cancel() {
	            try {
	                mmServerSocket.close();
	            } catch (IOException e) {
	            }
	        }
	     
	}
	
	//連接線程
	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";

            //連接
            try {
                if (secure) {
                    tmp = device.createRfcommSocketToServiceRecord(
                            MY_UUID_SECURE);
                } else {
                    tmp = device.createInsecureRfcommSocketToServiceRecord(
                            MY_UUID_INSECURE);
                }
            } catch (IOException e) {
            }
            mmSocket = tmp;
        }

        public void run() {
            setName("ConnectThread" + mSocketType);

            mAdapter.cancelDiscovery();
            //連接到BluetoothSocket
            try {
                mmSocket.connect();
            } catch (IOException e) {
                try {
                    mmSocket.close();
                } catch (IOException e2) {
                }
                connectionFailed();
                return;
            }

            // 取消連接線程
            synchronized (BluetoothChatService.this) {
                mConnectThread = null;
            }

            // 開始連接線程
            connected(mmSocket, mmDevice, mSocketType);
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
            }
        }
    }
	
	
    
	//已連接線程,通過這個線程發送和接收數據
	private class ConnectedThread extends Thread {
		private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        
        //初始化
        public ConnectedThread(BluetoothSocket socket, String socketType){
        	mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            
            try {
            	//得到輸入輸出數據流
				tmpIn = socket.getInputStream();
				tmpOut = socket.getOutputStream();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            
            mmInStream = tmpIn;
            mmOutStream = tmpOut; 
        }
        
       //讀
       public void run(){
    	   byte[] buffer = new byte[1024];
           int bytes;
           //長連接
           while (true) {
               try {
                   bytes = mmInStream.read(buffer);
                   // 把信息反饋給UI界面顯示出來
                   mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                           .sendToTarget();
               } catch (IOException e) {
                   connectionLost();
                   // 重新開始服務
                   BluetoothChatService.this.start();
                   break;
               }
           }
       } 
       
       //寫
       public void write(byte[] buffer) {
           try {
               mmOutStream.write(buffer);
               mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
                       .sendToTarget();
           } catch (IOException e) {
           }
       }
       
       public void cancel() {
           try {
               mmSocket.close();
           } catch (IOException e) {
           }
       }
        
	}
	
	//寫接口
		public void write(byte[] send) {
			// TODO Auto-generated method stub
			ConnectedThread r;
	        synchronized (this) {
	            if (mState != STATE_CONNECTED) return;
	            r = mConnectedThread;
	        }
	        r.write(send);
		}
	
		
}



      


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章