Android學習筆記之藍牙通信...


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM