Android Bluetooth 学习(2)应用层实现蓝牙设备查找、tcp_ip通信

旧巷老猫 提交于 2019-12-02 18:01:25

Bluetooth结构

    1、JAVA层
    frameworks/base/core/java/android/bluetooth/

   包含了bluetooth的JAVA类。

    2、JNI层

    frameworks/base/core/jni/android_bluetooth_开头的文件

   
   定义了bluez通过JNI到上层的接口。

   frameworks/base/core/jni/android_server_bluetoothservice.cpp

   调用硬件适配层的接口system/bluetooth/bluedroid/bluetooth.c

    3、bluez库

    external/bluez/

    
   这是bluez用户空间的库,开源的bluetooth代码,包括很多协议,生成libbluetooth.so。

    
    4、硬件适配层

    system/bluetooth/bluedroid/bluetooth.c

  
   包含了对硬件操作的接口

   system/bluetooth/data/*

   一些配置文件,复制到/etc/bluetooth/。

   还有其他一些测试代码和工具。


一、简略介绍Bluetooth开发使用到的类

1、BluetoothAdapter,蓝牙适配器,可判断蓝牙设备是否可用等功能。
常用方法列举如下:
    cancelDiscovery() ,取消搜索过程,在进行蓝牙设备搜索时,如果调用该方法会停止搜索。(搜索过程会持续12秒)
    disable()关闭蓝牙,也就是我们常说的禁用蓝牙。
    enable()打开蓝牙,这个方法打开蓝牙但不会弹出提示,正常流程操作下,我们会让系统提示用户是否打开蓝牙设备。如下两行代码可轻松搞定。

    Intent enabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enabler,reCode);//同startActivity(enabler);(在主Activity启动一个二级Activity,reCode一般等于3,一定记得要在AndroidManifest.xml里面添加蓝牙权限
    getAddress()获取本地蓝牙地址
    getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter
    getName()获取本地蓝牙名称
    getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备
    getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
    isDiscovering()判断当前是否正在查找设备,是返回true
    isEnabled()判断蓝牙是否打开,已打开返回true,否则,返回false
    listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回    
    BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步
    startDiscovery()开始搜索,这是搜索的第一步

 2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备
    createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket
    这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket
    这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter
    这个类有几个隐藏方法,涉及到蓝牙的自动配对,setPin,createBond,cancelPairingUserInput,等方法(需要通过java的反射,调用这几个隐藏方法)

3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不过了,既然是Socket,方法就应该都差不多,
    这个类一种只有三个方法
    两个重载的accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!
    还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接
    close()关闭!

4.BluetoothSocket,跟BluetoothServerSocket相对,是客户端
    一共5个方法,不出意外,都会用到
    close(),关闭
    connect()连接
    getInptuStream()获取输入流
    getOutputStream()获取输出流
    getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

二、蓝牙设备的发现、查找。

1.基于安全性考虑,设置开启可被搜索后,Android系统会默认给出120秒的时间,其他设备能在这120秒内搜索到它。
    Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    startActivityForResult(enalbe,REQUEST_DISCOVERABLE);
2.搜索蓝牙设备

    BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();
    _bluetooth.startDiscovery();
3.关闭蓝牙设备
    BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();
    _bluetooth.disable();
4.创建蓝牙客户端
    BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(UUID号));
    socket.connect();
4.创建蓝牙服务端
    BluetoothServerSocket _serverSocket = _bluetooth.listenUsingRfcommWithServiceRecord(服务端名称,UUID.fromeString(UUID号));
    BluetoothSocket socket = _serverSocket.accept();
    InputStream inputStream = socket.getInputStream();
三、相关代码实现
注:tcp_ip模块需要在系统setting模块中,完成蓝牙的配对,只有配对成功的,才能进行socket通信(具体如何配对和如何自动配对,将在bluetoot3或者4中进行讲解
AndridManifest.xml


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.thecaseforbluetooth"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".BluetoothActivity"
            android:label="@string/title_activity_bluetooth" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnSearch"
  android:text="查找设备"
/>
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnExit"
  android:text="退出应用"
/>
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnDis"
  android:text="设置可被发现"
/>
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnserver"
  android:text="启动服务端"
/>
<ToggleButton
android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/tbtnSwitch"
  android:text="开/关 蓝牙设备"
/>
<ListView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/lvDevices"
  
/>
</LinearLayout>
BluetoothActivity.java
package com.example.thecaseforbluetooth;

import java.util.ArrayList;
import java.util.Set;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.ToggleButton;

public class BluetoothActivity extends Activity {
	public Button searchBtn;//搜索蓝牙设备
	public Button exitBtn;//退出应用
	public Button discoverBtn;//设置可被发现
	public ToggleButton openBtn;//开关蓝牙设备
	public Button serverbtn;
	public ListView listView;//蓝牙设备清单
	public ArrayAdapter<String> adapter;
	public ArrayList<String> list =new ArrayList<String>();
	private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	Set<BluetoothDevice> bondDevices ;
	public Context context ;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        searchBtn = (Button)findViewById(R.id.btnSearch);
        exitBtn = (Button)findViewById(R.id.btnExit);
        discoverBtn = (Button)findViewById(R.id.btnDis);
        openBtn = (ToggleButton)findViewById(R.id.tbtnSwitch);
        serverbtn = (Button)findViewById(R.id.btnserver);
        listView = (ListView)findViewById(R.id.lvDevices);
        context = getApplicationContext();
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,list);
        listView.setAdapter(adapter);
        openBtn.setChecked(false);
        //注册广播接收信号
        IntentFilter intent = new IntentFilter(); 
        intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果 
        intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); //每当扫描模式变化的时候,应用程序可以为通过ACTION_SCAN_MODE_CHANGED值来监听全局的消息通知。比如,当设备停止被搜寻以后,该消息可以被系统通知給应用程序。
        intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //每当蓝牙模块被打开或者关闭,应用程序可以为通过ACTION_STATE_CHANGED值来监听全局的消息通知。
        registerReceiver(searchReceiver, intent); 
        //显示已配对设备以及搜索未配对设备
        searchBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				if(bluetoothAdapter.isDiscovering()){
					bluetoothAdapter.cancelDiscovery();
				}
				list.clear();
				bondDevices = bluetoothAdapter.getBondedDevices();
				
				for(BluetoothDevice device : bondDevices) {
					String str = "	已配对完成	" + device.getName() +"	" 
                    + device.getAddress(); 
					list.add(str);
					adapter.notifyDataSetChanged();
				}
				bluetoothAdapter.startDiscovery();
			}
		});
        //退出应用
        exitBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				BluetoothActivity.this.finish();
			}
		});
        //设置蓝牙设备可发现
        discoverBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent discoverIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
				discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
				startActivity(discoverIntent);
			}
		});
        //开关蓝牙设备
        openBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				if(openBtn.isChecked() == true){
					bluetoothAdapter.disable();
				}
				else if(openBtn.isChecked() == false){
					bluetoothAdapter.enable();
				}
			}
		});
        //作为服务端开启
        serverbtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				ServerThread serverThread = new ServerThread(bluetoothAdapter, context);
				Toast.makeText(context, "server 端启动", 5000).show();
				serverThread.start();
			}
		});
        listView.setOnItemClickListener(new ItemClickListener());
    }
    @Override
	public void onStart() {
		super.onStart();
		// If BT is not on, request that it be enabled.
		if(bluetoothAdapter == null){
			Toast.makeText(context, "蓝牙设备不可用", 5000).show();
		}
		 if (!bluetoothAdapter.isEnabled()) {
			Intent enableIntent = new Intent(
					BluetoothAdapter.ACTION_REQUEST_ENABLE);
			startActivityForResult(enableIntent, 3);
		}
		 
	}
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
    private final BroadcastReceiver searchReceiver = new BroadcastReceiver() {
		
		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			 String action = intent.getAction(); 
			 BluetoothDevice device = null;
			 if(BluetoothDevice.ACTION_FOUND.equals(action)){
				 device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 
                 if (device.getBondState() == BluetoothDevice.BOND_NONE) { 
				 Toast.makeText(context, device.getName()+"", 5000).show();
				 String str = "	未配对完成	" + device.getName() +"	" 
                 + device.getAddress(); 
				 if (list.indexOf(str) == -1)// 防止重复添加 
                     list.add(str); 
                 }
				 adapter.notifyDataSetChanged();
			 }
			
		}
	};
	  public class ItemClickListener implements OnItemClickListener {

		@Override
		public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
				long arg3) {
			// TODO Auto-generated method stub
			 if(bluetoothAdapter.isDiscovering())
				 bluetoothAdapter.cancelDiscovery(); 
             String str = list.get(arg2); 
             String address = str.substring(str.length() - 17); 
             BluetoothDevice btDev = bluetoothAdapter.getRemoteDevice(address); 
             ClientThread clientThread = new ClientThread(btDev, context);
             clientThread.start();
		}}
}
Bluetoothprotocol.java
package com.example.thecaseforbluetooth;

public interface Bluetoothprotocol {
	public static final String PROTOCOL_SCHEME_L2CAP = "btl2cap";
	public static final String PROTOCOL_SCHEME_RFCOMM = "btspp";
	public static final String PROTOCOL_SCHEME_BT_OBEX = "btgoep";
	public static final String PROTOCOL_SCHEME_TCP_OBEX = "tcpobex";
}
ServerThread.java
package com.example.thecaseforbluetooth;

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

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

public class ServerThread extends Thread {
	public BluetoothServerSocket mserverSocket;
	public BluetoothAdapter bluetoothAdapter;
	public BluetoothSocket socket;
	public Context context;
	public ServerThread(BluetoothAdapter bluetoothAdapter,Context context) {
		this.bluetoothAdapter = bluetoothAdapter;
		this.context = context;
	}

	public void run() {
		
		try {
			/* 创建一个蓝牙服务器 
			 * 参数分别:服务器名称、UUID	 */	
			mserverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM,
					UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));		
//			/* 接受客户端的连接请求 */
			socket = mserverSocket.accept();
			//下面代码作者偷懒,读写另外起一个线程最好
			//接收数据
			 byte[] buffer = new byte[1024];
	            int bytes;
	            InputStream mmInStream = null;
	            
				try {
					mmInStream = socket.getInputStream();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}	
				System.out.println("zhoulc server");
	            while(true){
	            	 if( (bytes = mmInStream.read(buffer)) > 0 )
	                    {
		                    byte[] buf_data = new byte[bytes];
					    	for(int i=0; i<bytes; i++)
					    	{
					    		buf_data[i] = buffer[i];
					    	}
							String s = new String(buf_data);
							System.out.println(s+"zhoulc server is in");
	            }  
	            }
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

}
	
}
ClientThread.java
package com.example.thecaseforbluetooth;

import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.widget.Toast;

public class ClientThread extends Thread {
	public BluetoothSocket socket;
	public BluetoothDevice device;
	public Context context;
	public ClientThread(BluetoothDevice device,Context context){
		this.device = device;
		this.context = context;
	}
	public void run() {
		try {
			//创建一个Socket连接:只需要服务器在注册时的UUID号
			socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
			//连接
			socket.connect();
			//下面代码作者偷懒,读写另外起一个线程最好
			//发送数据
			if (socket == null) 
			{
				Toast.makeText(context, "链接失败", 5000).show();
				return;
			}
			System.out.println("zhoulc client");
			while(true){
				try {
					System.out.println("zhoulc client is in");
					String msg = "hello everybody I am client";
					OutputStream os = socket.getOutputStream(); 
					os.write(msg.getBytes());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}			
			}
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		} 
	}
}


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!