Android學習筆記之藍芽通訊...

weixin_33941350發表於2015-07-27

PS:最近同學問我藍芽的事,因此自己也就腦補了一下藍芽...

 

學習內容:

1.如何實現藍芽通訊技術...

 

  藍芽通訊其實是手機裡很常用的一種通訊方式,現在的手機中是必然存在藍芽的,藍芽通訊也是有一部分優點的,功耗低,安全性比較高,但是缺點想必大家都知道,傳輸的速率也確實是不快,相比於Wifi通訊,確實不值一提...雖然影響範圍並不高,但是既然藍芽存在,那麼還是有必要知道藍芽是如何進行通訊的...藍芽通訊有兩種方式,最常用的就是使用socket套接字來實現藍芽通訊...

  藍芽通訊原理:藍芽通訊的原理很簡單,一個裝置作為服務端,另一個裝置作為客戶端,服務端對外暴露,客戶端通過傳送連線請求,服務端進行響應,然後返回一個BluetoothSocket套接字來管理這個連線...那麼服務端個客戶端就會共享一個RFCOMM埠,進行資料傳遞...聽起來其實蠻簡單的,實現過程還是有點複雜的...這裡我還是直接上一個程式碼吧...

  這個是Activity.java檔案...這是個非常長的程式碼塊,看完誰都頭痛...我們還是進行分塊解釋...先把這些程式碼都略過...還是看最下面的解釋...

package com.qualcomm.bluetoothclient;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

public class ClientActivity extends Activity implements OnItemClickListener {

    private Context mContext;
    private BluetoothAdapter mBluetoothAdapter; // Bluetooth介面卡
    private BluetoothDevice device;             // 藍芽裝置
    private ListView mListView;
    private ArrayList<ChatMessage> list;
    private ClientAdapter clientAdapter;        // ListView介面卡
    private Button disconnect = null, sendButton = null;
    private EditText editText = null;
    
    private BluetoothSocket socket;     // 客戶端socket
    private ClientThread mClientThread; // 客戶端執行執行緒
    private ReadThread mReadThread;     // 讀取流執行緒
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        init();
    }

    // 變數初始化
    private void init() {
        // TODO Auto-generated method stub
        mContext = this;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//獲取本地藍芽...
        
        list = new ArrayList<ChatMessage>();// 初始化list
        clientAdapter = new ClientAdapter(mContext, list);  //介面卡,用來限制如何顯示ListView...
        mListView = (ListView) findViewById(R.id.list);
        
        mListView.setFastScrollEnabled(true);
        
        mListView.setAdapter(clientAdapter);
        
        mListView.setOnItemClickListener(this);
        
        // 註冊receiver監聽,註冊廣播...
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver, filter);
        
        // 獲取已經配對過的藍芽裝置
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();//獲取發現的藍芽裝置的基本資訊...
        
        if (pairedDevices.size() > 0) {  
            for (BluetoothDevice device : pairedDevices) {
                //將手機名字和實體地址放入到listview中...
                list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true));
                
                clientAdapter.notifyDataSetChanged();  //重新繪製ListView
                
                mListView.setSelection(list.size() - 1);
            }
        } else {
            list.add(new ChatMessage("沒有已經配對過的裝置", true));
            clientAdapter.notifyDataSetChanged();
            mListView.setSelection(list.size() - 1);
        }
        
        editText = (EditText) findViewById(R.id.edit);
        editText.setEnabled(false);
        editText.clearFocus();
        
        sendButton = (Button) findViewById(R.id.btn_send);
        sendButton.setEnabled(false);
        sendButton.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                String msg = editText.getText().toString();
                if (msg.length() > 0) {
                    sendMessageHandler(msg);
                    editText.setText("");
                    editText.clearFocus();
                    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);//定義一個輸入法物件...通過Context.INPUT_METHOD_SERVICE獲取例項...
                    //當EditText沒有焦點的時候,阻止輸入法的彈出...其實就是在沒點選EditText獲取焦點的時候,沒有輸入法的顯示...
                    imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
                } else {
                    Toast.makeText(mContext, "傳送內容不能為空", Toast.LENGTH_SHORT).show();
                }
            }
        });
        
        disconnect = (Button) findViewById(R.id.disconnect);
        disconnect.setEnabled(false);
        disconnect.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                // 關閉相關服務
                closeClient();
                BluetoothMsg.isOpen = false;
                
                BluetoothMsg.serviceOrCilent = BluetoothMsg.ServerOrCilent.NONE;
                Toast.makeText(mContext, "連線已斷開", Toast.LENGTH_SHORT).show();
            }
        });
    }
    
    @Override
    protected void onStart() {
        // TODO Auto-generated method stub
        super.onStart();
        if (mBluetoothAdapter != null) {  //本地藍芽存在...
            if (!mBluetoothAdapter.isEnabled()) {   //判斷藍芽是否被開啟...
                // 傳送開啟藍芽的意圖,系統會彈出一個提示對話方塊
                Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableIntent, RESULT_FIRST_USER);
                
                // 設定藍芽的可見性,最大值3600秒,預設120秒,0表示永遠可見(作為客戶端,可見性可以不設定,服務端必須要設定)
                
                //開啟本機的藍芽功能,持續的時間是永遠可見...
                Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
                startActivity(displayIntent);
                
                // 直接開啟藍芽
                mBluetoothAdapter.enable();
            }
        }
    }
    
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        // 掃描
        scanDevice();
    }
    
    /**
     * 藍芽裝置掃描過程中(mBluetoothAdapter.startDiscovery())會發出的訊息
     * ACTION_FOUND 掃描到遠端裝置
     * ACTION_DISCOVERY_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
                // 通過EXTRA_DEVICE附加域來得到一個BluetoothDevice裝置
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                
                // If it's already paired, skip it, because it's been listed already
                // 如果這個裝置是不曾配對過的,新增到list列表
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) 
                {
                    list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), false));
                    clientAdapter.notifyDataSetChanged();
                    mListView.setSelection(list.size() - 1);
                }
            // When discovery is finished, change the Activity title
            } 
            else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) 
            {
                setProgressBarIndeterminateVisibility(false);
                if (mListView.getCount() == 0) 
                {
                    list.add(new ChatMessage("沒有發現藍芽裝置", false));
                    clientAdapter.notifyDataSetChanged();
                    mListView.setSelection(list.size() - 1);
                }
            }
        }
    };
    
    // Handler更新UI
    private Handler LinkDetectedHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //Toast.makeText(mContext, (String)msg.obj, Toast.LENGTH_SHORT).show();
            if(msg.what==1)
            {
                list.add(new ChatMessage((String)msg.obj, true));
            }
            else
            {
                list.add(new ChatMessage((String)msg.obj, false));
            }
            clientAdapter.notifyDataSetChanged();
            mListView.setSelection(list.size() - 1);
            
        }
        
    };
    
    // 當連線上伺服器的時候才可以選擇傳送資料和斷開連線
    private Handler refreshUI = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 0) {
                disconnect.setEnabled(true);
                sendButton.setEnabled(true);
                editText.setEnabled(true);
            }
        }
    };
    
    // 開啟客戶端連線服務端
    private class ClientThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            if (device != null) {
                try {
                    socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
                    // 連線
                    Message msg = new Message();
                    msg.obj = "請稍候,正在連線伺服器: "+ BluetoothMsg.BlueToothAddress;
                    msg.what = 0;
                    LinkDetectedHandler.sendMessage(msg);
                    
                    // 通過socket連線伺服器,這是一個阻塞過程,直到連線建立或者連線失效
                    socket.connect();
                    
                    Message msg2 = new Message();
                    msg2.obj = "已經連線上服務端!可以傳送資訊";
                    msg2.what = 0;
                    LinkDetectedHandler.sendMessage(msg2);
                    
                    // 更新UI介面
                    Message uiMessage = new Message();
                    uiMessage.what = 0;
                    refreshUI.sendMessage(uiMessage);
                    
                    // 可以開啟讀資料執行緒
                    mReadThread = new ReadThread();
                    mReadThread.start();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    // socket.connect()連線失效
                    Message msg = new Message();
                    msg.obj = "連線服務端異常!斷開連線重新試一試。";
                    msg.what = 0;
                    LinkDetectedHandler.sendMessage(msg);
                }
            }
        }
    }
    
    // 通過socket獲取InputStream流
    private class ReadThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            byte[] buffer = new byte[1024];
            int bytes;
            InputStream is = null;
            try {
                is = socket.getInputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            while(true) {
                try {
                    if ((bytes = is.read(buffer)) > 0) {
                        byte[] data = new byte[bytes];
                        for (int i = 0; i < data.length; i++) {
                            data[i] = buffer[i];
                        }
                        String s = new String(data);
                        Message msg = new Message();
                        msg.obj = s;
                        msg.what = 1;
                        LinkDetectedHandler.sendMessage(msg);
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    try {
                        is.close();
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    break;
                }
            }
        }
    }
    
    // 傳送資料
    private void sendMessageHandler(String msg) {
        if (socket == null) {
            Toast.makeText(mContext, "沒有可用的連線", Toast.LENGTH_SHORT).show();
            return;
        }
        //如果連線上了,那麼獲取輸出流...
        try {
            OutputStream os = socket.getOutputStream();
            os.write(msg.getBytes());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        list.add(new ChatMessage(msg, false));  //將資料存放到list中
        clientAdapter.notifyDataSetChanged();
        mListView.setSelection(list.size() - 1);
    }
    
    // 停止服務
    private void closeClient() {
        new Thread() {
            public void run() {
                if (mClientThread != null) {
                    mClientThread.interrupt();
                    mClientThread = null;
                }
                if (mReadThread != null) {
                    mReadThread.interrupt();
                    mReadThread = null;
                }
                try {
                    if (socket != null) {
                        socket.close();
                        socket = null;
                    }
                } catch (IOException e) {
                    // TODO: handle exception
                }
            }
        }.start();
    }
    
    // 掃描裝置
    private void scanDevice() {
        // TODO Auto-generated method stub
        if (mBluetoothAdapter.isDiscovering()) {  //如果正在處於掃描過程...
            mBluetoothAdapter.cancelDiscovery();  //取消掃描...
        } else {
            list.clear();
            clientAdapter.notifyDataSetChanged();
            
            // 每次掃描前都先判斷一下是否存在已經配對過的裝置
            Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
            if (pairedDevices.size() > 0) {
                    for (BluetoothDevice device : pairedDevices) {
                        list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true));
                        clientAdapter.notifyDataSetChanged();
                        mListView.setSelection(list.size() - 1);
                    }
            } else {
                    list.add(new ChatMessage("No devices have been paired", true));
                    clientAdapter.notifyDataSetChanged();
                    mListView.setSelection(list.size() - 1);
              }                
            /* 開始搜尋 */
            mBluetoothAdapter.startDiscovery();
        }
    }

    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        // TODO Auto-generated method stub
        ChatMessage item = list.get(arg2);  //item儲存著message和一個boolean數值...
        String info = item.getMessage();    //單純獲取message的資訊...
        
        String address = info.substring(info.length() - 17);//獲取MAC地址...其實就是硬體地址...
        
        
        BluetoothMsg.BlueToothAddress = address;
        
        // 停止掃描
        // BluetoothAdapter.startDiscovery()很耗資源,在嘗試配對前必須中止它
        mBluetoothAdapter.cancelDiscovery();
        
        // 通過Mac地址去嘗試連線一個裝置
        device = mBluetoothAdapter.getRemoteDevice(BluetoothMsg.BlueToothAddress);
        mClientThread = new ClientThread();  //開啟新的執行緒...
        mClientThread.start();
        BluetoothMsg.isOpen = true;
        
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if (mBluetoothAdapter != null) {
            mBluetoothAdapter.cancelDiscovery();
            // 關閉藍芽
            mBluetoothAdapter.disable();
        }
        unregisterReceiver(mReceiver);
        closeClient();
    }
}
View Code

   這個是我自定義了一個類,用來判斷藍芽的連線型別...

package com.qualcomm.bluetoothclient;

public class BluetoothMsg {

    /** 
     * 藍芽連線型別  
     * 
     */  
    public enum ServerOrCilent {
        NONE,  
        SERVICE,  
        CILENT  
    };
    
    //藍芽連線方式  
    public static ServerOrCilent serviceOrCilent = ServerOrCilent.NONE;  
    //連線藍芽地址  
    public static String BlueToothAddress = null, lastblueToothAddress = null;  
    //通訊執行緒是否開啟  
    public static boolean isOpen = false;
    
}

  這個是儲存我們傳送的資料資訊的自定義類...

package com.qualcomm.bluetoothclient;
/*
 *  這裡定義一個類,用來儲存我們傳送的資料資訊...
 * 
 */
public class ChatMessage {

    private String message;
    private boolean isSiri;
    
    public ChatMessage(String message, boolean siri) {
        // TODO Auto-generated constructor stub
        this.message = message;
        this.isSiri = siri;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isSiri() {
        return isSiri;
    }

    public void setSiri(boolean isSiri) {
        this.isSiri = isSiri;
    }
    
    
}

  最後這個是介面卡...因為我這裡使用到了ListView,因此我需要使用一個介面卡來設定ListView以何種方式顯示在螢幕上...

package com.qualcomm.bluetoothclient;

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

/*
 * 
 * 
 * 
 * 
 * 
 */
public class ClientAdapter extends BaseAdapter {

    private ArrayList<ChatMessage> list;
    private LayoutInflater mInflater;
    
    public ClientAdapter(Context context, ArrayList<ChatMessage> messages) {
        // TODO Auto-generated constructor stub
        this.list = messages;
        this.mInflater = LayoutInflater.from(context);
    }
    
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }
    
    @Override
    public int getItemViewType(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder viewHolder = null;
        ChatMessage message = list.get(position);
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item, null);
            viewHolder = new ViewHolder((View)convertView.findViewById(R.id.list_child)
                    , (TextView)convertView.findViewById(R.id.chat_msg));
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        if (message.isSiri()) {
            viewHolder.child.setBackgroundResource(R.drawable.msgbox_rec);
        } else {
            viewHolder.child.setBackgroundResource(R.drawable.msgbox_send);
        }
        viewHolder.msg.setText(message.getMessage());
        
        return convertView;
    }

    class ViewHolder {
          protected View child;
        protected TextView msg;

        public ViewHolder(View child, TextView msg){
            this.child = child;
            this.msg = msg;
            
        }
    }

}

  在這裡我進行正式的解釋...先說第二部分..第二部分是一些初始化的操作...也就是獲取本地藍芽,註冊廣播,設定監聽的一些過程...因為實現通訊,我們首先要判斷我們的手機是否有藍芽...獲取到本機的藍芽... 也就是這句話是實現通訊的第一步...mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();我們才能夠開啟藍芽...然後進行操作...詳細解釋在程式碼中...

    //→_→ 第二部分...
    
    // 變數初始化
    private void init() {
        // TODO Auto-generated method stub
        mContext = this;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//獲取本地藍芽...實現通訊的第一步...
        
        list = new ArrayList<ChatMessage>();// 初始化list,由於我們最後會將資料以列表的形式進行顯示,因此使用ListView...
        clientAdapter = new ClientAdapter(mContext, list);  //介面卡,用來限制如何顯示ListView...
        
        mListView = (ListView) findViewById(R.id.list);
        
        mListView.setFastScrollEnabled(true);  //使用快速滑動功能..目的是能夠快速滑動到指定位置...
        
        mListView.setAdapter(clientAdapter);
        
        mListView.setOnItemClickListener(this);
        
        /* 下面是註冊receiver監聽,註冊廣播...說一下為什麼要註冊廣播...
         * 因為藍芽的通訊,需要進行裝置的搜尋,搜尋到裝置後我們才能夠實現連線..如果沒有搜尋,那還談什麼連線...
         * 因此我們需要搜尋,搜尋的過程中系統會自動發出三個廣播...這三個廣播為:
         * ACTION_DISCOVERY_START:開始搜尋...
         * ACTION_DISCOVERY_FINISH:搜尋結束...
         * ACTION_FOUND:正在搜尋...一共三個過程...因為我們需要對這三個響應過程進行接收,然後實現一些功能,因此
         * 我們需要對廣播進行註冊...知道廣播的人應該都知道,想要對廣播進行接收,必須進行註冊,否則是接收不到的...
         * */
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver, filter);
        
        // 定義一個集合,來儲存已經配對過的裝置...
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
        
        if (pairedDevices.size() > 0) {  //如果存在裝置...
            for (BluetoothDevice device : pairedDevices) {//遍歷...
                
                //將手機名字和實體地址放入到listview中...
                list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true));
                
                clientAdapter.notifyDataSetChanged();  //重新繪製ListView
                
                mListView.setSelection(list.size() - 1); //設定list儲存的資訊的位置...說白了該條資訊始終在上一條資訊的下方...
            }
        } else {  
            list.add(new ChatMessage("沒有已經配對過的裝置", true));
            clientAdapter.notifyDataSetChanged();
            mListView.setSelection(list.size() - 1);
        }
        
        editText = (EditText) findViewById(R.id.edit);
        editText.setEnabled(false);  
        editText.clearFocus();  //設定沒有焦點..也就是無法輸入任何文字...
        
        sendButton = (Button) findViewById(R.id.btn_send);
        sendButton.setEnabled(false);

        sendButton.setOnClickListener(new OnClickListener() {//設定監聽,只有獲取到焦點後才能進行此過程...
            
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                String msg = editText.getText().toString();
                
                if (msg.length() > 0) {
                    //呼叫第五部分執行緒,通過執行緒傳送我們輸入的文字...
                    sendMessageHandler(msg);
                    //傳送完清空...
                    editText.setText("");
                    editText.clearFocus();
                    
                    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);//定義一個輸入法物件...通過Context.INPUT_METHOD_SERVICE獲取例項...
                    //當EditText沒有焦點的時候,阻止輸入法的彈出...其實就是在沒點選EditText獲取焦點的時候,沒有輸入法的顯示...
                    imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
                } else {
                    Toast.makeText(mContext, "傳送內容不能為空", Toast.LENGTH_SHORT).show();
                }
            }
        });
        
        disconnect = (Button) findViewById(R.id.disconnect);
        disconnect.setEnabled(false);
        //為斷開連線設定監聽...
        disconnect.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                
                // 關閉相關服務,呼叫第六部分的函式...
                closeClient();
                //表示藍芽狀態需要關閉...
                BluetoothMsg.isOpen = false;
                
                BluetoothMsg.serviceOrCilent = BluetoothMsg.ServerOrCilent.NONE;
                Toast.makeText(mContext, "連線已斷開", Toast.LENGTH_SHORT).show();
            }
        });
    }

  接著就是第三部分了...我們獲取到了本地的藍芽裝置,我們就需要把藍芽進行開啟了..只有開啟了藍芽,才能夠進行搜尋,連線等操作..因此這一步是實現藍芽通訊的關鍵...

//→_→ 第三部分...
    @Override
    protected void onStart() {
        // TODO Auto-generated method stub
        super.onStart();
        if (mBluetoothAdapter != null) {  //本地藍芽存在...
            if (!mBluetoothAdapter.isEnabled()) {   //判斷藍芽是否被開啟...
                
                // 傳送開啟藍芽的意圖,系統會彈出一個提示對話方塊,開啟藍芽是需要傳遞intent的...
                // intent這個重要的東西,大家應該都知道,它能夠實現應用之間通訊和互動....
                Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                //開啟本機的藍芽功能...使用startActivityForResult()方法...這裡我們開啟的這個Activity是需要它返回執行結果給主Activity的...
                startActivityForResult(enableIntent, RESULT_FIRST_USER);
                
                        
                Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                // 設定藍芽的可見性,最大值3600秒,預設120秒,0表示永遠可見(作為客戶端,可見性可以不設定,服務端必須要設定)
                displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
                //這裡只需要開啟另一個activity,讓其一直顯示藍芽...沒必要把資訊返回..因此呼叫startActivity()
                startActivity(displayIntent);
                
                // 直接開啟藍芽
                mBluetoothAdapter.enable();//這步才是真正開啟藍芽的部分....
            }
        }
    }
    
    //這步就是當執行緒從Pause狀態到Avtive狀態要執行的過程...
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        // 掃描
        scanDevice();  //呼叫第七部分的掃描過程...
    }

  然後是第四部分....第四部分是對廣播響應後的接收過程..這個過程也是重要的,因為我們在開啟藍芽後掃描的時候,如果有裝置可以連線,我們得做一些操作讓使用者知道有裝置可以連線,總不能有裝置連線,我們什麼也不告訴使用者,這就不合理吧....因此我們在這一步是需要給使用者反饋資訊的...讓使用者知道下一步應該做什麼...這一部分倒是很簡單,沒複雜的東西..

//→_→ 第四部分...
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
          
            String action = intent.getAction(); //獲取當前正在執行的動作...


            if (BluetoothDevice.ACTION_FOUND.equals(action)) //正在搜尋過程...
            {
                // 通過EXTRA_DEVICE附加域來得到一個BluetoothDevice裝置
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                
                // 如果這個裝置是不曾配對過的,新增到list列表
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) 
                {
                    list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), false));
                    clientAdapter.notifyDataSetChanged();
                    mListView.setSelection(list.size() - 1);
                }
            } 
            else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) //搜尋結束後的過程...
            {
                setProgressBarIndeterminateVisibility(false);  //這步是如果裝置過多是否顯示滾動條...
                if (mListView.getCount() == 0) 
                {
                    list.add(new ChatMessage("沒有發現藍芽裝置", false));
                    clientAdapter.notifyDataSetChanged();
                    mListView.setSelection(list.size() - 1);
                }
            }
        }
    };

  第五部分就是執行緒部分了,所有的執行緒部分...涉及UI的更新,資料資訊的顯示,將資料進行傳送,接收伺服器返回的資料資訊...這步是實現資料傳遞的關鍵...

   //→_→ 第五部分...
    
    private Handler LinkDetectedHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //這步多了一個判斷..判斷的是ListView儲存的資料是我們要傳輸給服務端的資料,還是那些我們定義好的提示資料...
            if(msg.what==1)
            {
                list.add(new ChatMessage((String)msg.obj, true));
            }
            else
            {
                list.add(new ChatMessage((String)msg.obj, false));
            }
            clientAdapter.notifyDataSetChanged();
            mListView.setSelection(list.size() - 1);
            
        }
        
    };
    // Handler更新UI...
    // 當連線上伺服器的時候才可以選擇傳送資料和斷開連線,並且要對介面進行重新整理操作...
    private Handler refreshUI = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 0) {
                disconnect.setEnabled(true);
                sendButton.setEnabled(true);
                editText.setEnabled(true);
            }
        }
    };
    
    // 開啟客戶端連線服務端,一個新的執行緒...
    private class ClientThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            if (device != null) {
                try {
                    /* 下面這步也是關鍵,我們如果想要連線伺服器,我們需要呼叫方法createRfcommSocketToServiceRecord
                     * 引數00001101-0000-1000-8000-00805F9B34FB表示的是預設的藍芽串列埠...通過傳遞引數呼叫方法,
                     * 會返回給我們一個套接字..這一步就是獲取套接字,實現連線的過程...
                     * 
                     * */
                    socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
                    // 連線
                    Message msg = new Message();
                    msg.obj = "請稍候,正在連線伺服器: "+ BluetoothMsg.BlueToothAddress;
                    msg.what = 0;
                    LinkDetectedHandler.sendMessage(msg); //呼叫執行緒,顯示msg資訊...
                    
                    // 通過socket連線伺服器,正式形成連線...這是一個阻塞過程,直到連線建立或者連線失效...
                    socket.connect();
                    //如果實現了連線,那麼服務端和客戶端就共享一個RFFCOMM通道...
                    Message msg2 = new Message();
                    msg2.obj = "已經連線上服務端!可以傳送資訊";
                    msg2.what = 0;
                    LinkDetectedHandler.sendMessage(msg2); //呼叫執行緒,顯示msg資訊...
                    
                    // 如果連線成功了...這步就會執行...更新UI介面...否則走catch(IOException e)
                    Message uiMessage = new Message();
                    uiMessage.what = 0;
                    refreshUI.sendMessage(uiMessage);
                    
                    // 可以開啟讀資料執行緒
                    mReadThread = new ReadThread();
                    mReadThread.start();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    // socket.connect()連線失效
                    Message msg = new Message();
                    msg.obj = "連線服務端異常!斷開連線重新試一試。";
                    msg.what = 0;
                    LinkDetectedHandler.sendMessage(msg);
                }
            }
        }
    }
    
    // 通過socket獲取InputStream流..
    private class ReadThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
    
            byte[] buffer = new byte[1024];
            int bytes;
            InputStream is = null;
            try {
                is = socket.getInputStream(); //獲取伺服器發過來的所有位元組...
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            while(true) {
                try {//讀取過程,將資料資訊儲存在ListView中...
                    if ((bytes = is.read(buffer)) > 0) {
                        byte[] data = new byte[bytes];
                        for (int i = 0; i < data.length; i++) {
                            data[i] = buffer[i];
                        }
                        String s = new String(data);
                        Message msg = new Message();
                        msg.obj = s;
                        msg.what = 1;  //這裡的meg.what=1...表示的是伺服器傳送過來的資料資訊..
                        LinkDetectedHandler.sendMessage(msg);
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    try {
                        is.close();
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    break;
                }
            }
        }
    }
    //這一步表示的是傳送資料的過程...
    private void sendMessageHandler(String msg) {
        if (socket == null) {
            Toast.makeText(mContext, "沒有可用的連線", Toast.LENGTH_SHORT).show();
            return;
        }
        //如果連線上了,那麼獲取輸出流...
        try {
            OutputStream os = socket.getOutputStream();
            os.write(msg.getBytes());//獲取所有的自己然後往外傳送...
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        list.add(new ChatMessage(msg, false));  //將資料存放到list中
        clientAdapter.notifyDataSetChanged();
        mListView.setSelection(list.size() - 1);
    }

  第六部分就是一個停止服務的一個過程,就不細說了,第七部分就是一個掃描的過程,掃描附近的設別...

 // →_→ 第七部分...
    // 掃描裝置
    private void scanDevice() {
        // TODO Auto-generated method stub
        if (mBluetoothAdapter.isDiscovering()) {  //如果正在處於掃描過程...
            mBluetoothAdapter.cancelDiscovery();  //取消掃描...
        } else {
            list.clear();  
            clientAdapter.notifyDataSetChanged();
            
            // 每次掃描前都先判斷一下是否存在已經配對過的裝置
            Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
            if (pairedDevices.size() > 0) {  //如果存在配對過的裝置,那麼就直接遍歷集合,然後顯示..
                    for (BluetoothDevice device : pairedDevices) {
                        list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true));
                        clientAdapter.notifyDataSetChanged();
                        mListView.setSelection(list.size() - 1);
                    }
            } else {  //如果沒有就進行新增...
                    list.add(new ChatMessage("No devices have been paired", true));
                    clientAdapter.notifyDataSetChanged();
                    mListView.setSelection(list.size() - 1);
              }                
            /* 開始搜尋 */
            mBluetoothAdapter.startDiscovery();
        }
    }

  第八部分為連線過程做了一些初始化的工作,獲取其他裝置的實體地址,通過實體地址實現連線...至於第九部分就是銷過程了,第一部分就是變數的定義過程...就沒什麼好說的了...

 //→_→  第八部分...
    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        // TODO Auto-generated method stub
        ChatMessage item = list.get(arg2);  //item儲存著message和一個boolean數值...
        String info = item.getMessage();    //單純獲取message的資訊...
        
        String address = info.substring(info.length() - 17);//獲取MAC地址...其實就是硬體地址...因為連線設別必然從實體地址下手...
        
        
        BluetoothMsg.BlueToothAddress = address;//賦值
        
        // 停止掃描
        // BluetoothAdapter.startDiscovery()很耗資源,在嘗試配對前必須中止它
        mBluetoothAdapter.cancelDiscovery();
        
        // 通過Mac地址去嘗試連線一個裝置,
        device = mBluetoothAdapter.getRemoteDevice(BluetoothMsg.BlueToothAddress);
        
        mClientThread = new ClientThread();  
        mClientThread.start();   //開啟新的執行緒...開始連線...這裡只是做了一些初始化的工作..連線過程還是ClientThread執行緒...
        BluetoothMsg.isOpen = true;
        
    }

xml檔案我就不貼了,直接放一個原始碼吧...這個原始碼包含一個客戶端,一個服務端...(注意,這個測試需要在兩個真機上測試,模擬器是沒有作用的...)

原始碼地址:http://files.cnblogs.com/files/RGogoing/Bluetooth.zip

 

相關文章