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(); } }
這個是我自定義了一個類,用來判斷藍芽的連線型別...
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