`

android 和 PC端 进行蓝牙通信 demo

阅读更多

转http://royal2xiaose.iteye.com/blog/1420138

 

前提:

      1. 使用真机测试

      2. 测试前请蓝牙配对好手机与PC机蓝牙适配器(所以你需要一个蓝牙适配器插入PC USB口)

 

demo测试效果:

      当手机左右摇摆时将数据传递到PC端,打印出来。(android重力感应)

 

PC服务端代码:

import java.io.IOException;
import java.io.InputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

public class BTServer implements Runnable {

	// 流连接通知 用于创建流连接
	private StreamConnectionNotifier myPCConnNotifier = null;
	// 流连接
	private StreamConnection streamConn = null;
	// 接受数据字节流
	private byte[] acceptedByteArray = new byte[12];
	// 读取(输入)流
	private InputStream inputStream = null;

	/**
	 * 主线程
	 *  
	 * @param args
	 */
	public static void main(String[] args) {
		new BTServer();
	}

	/**
	 * 构造方法
	 */
	public BTServer() {
		try {
			// 得到流连接通知,下面的UUID必须和手机客户端的UUID相一致。
			myPCConnNotifier = (StreamConnectionNotifier) Connector
					.open("btspp://localhost:0000110100001000800000805F9B34FB");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 打开连接通道并读取流线程
		new Thread(this).start();
	}

	@Override
	public void run() {
		try {
			String inSTR = null;
			// 持续保持着监听客户端的连接请求
			while (true) {
				// 获取流连接
				streamConn = myPCConnNotifier.acceptAndOpen();
				// 获取流通道
				inputStream = streamConn.openInputStream();
				// 读取字节流
				while (inputStream.read(acceptedByteArray) != -1) {
					inSTR = new String(acceptedByteArray);
					System.out.println(inSTR);
					if (inSTR.contains("EXIT")) {
						// 手机客户端退出则关闭连接通道。
						inputStream.close();
						if (streamConn != null) {
							streamConn.close();
						} 
						break;
					}  
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 服务端请导入bluecove.jar 和 commons-io.jar包

 

android手机客户端代码:

 

BlueTooth.java

package com.royal.bluetooth;

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

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;

/**
 * BlueTooth & Sensor
 * 
 * @author royal
 * 
 */

public class BlueTooth extends Activity {

	private static final int REQUEST_DISCOVERY = 0x1;
	// 建立蓝牙通信的UUID 
	private static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	// 自带的蓝牙适配器
	private BluetoothAdapter bluetoothAdapter = null;
	// 扫描得到的蓝牙设备
	private BluetoothDevice device = null;
	// 蓝牙通信socket
	private BluetoothSocket btSocket = null;
	// 手机输出流
	private OutputStream outStream = null;
	private byte[] msgBuffer = null;
	// 传感器管理
	private SensorManager sensorMgr = null;
	// 传感器感应
	private Sensor sensor = null;
	// 手机x、y、z轴方向数据
	private int x, y, z;

	/**
	 * 当这个activity第一次被创建的时候呼叫该方法
	 **/
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		/* 使程序窗口全屏 */
		// 创建一个没有title的全屏主题
		this.setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
		// 窗口全屏
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		// 设置全屏标志
		this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);

		// 按bluetooth.xml文件布局风格
		setContentView(R.layout.bluetooth);

		// Gravity sensing 获取传感器
		sensorMgr = (SensorManager) this.getSystemService(SENSOR_SERVICE);
		sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

		// 获取手机默认上的蓝牙适配器
		bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

		// 开启手机蓝牙设备
		bluetoothAction();

		// 查询附近所有的蓝牙设备并选择连接
		connectToDevice();
	}

	/**
	 * 蓝牙开始 查询手机是否支持蓝牙,如果支持的话,进行下一步。 查看蓝牙设备是否已打开,如果否则打开。
	 */
	public void bluetoothAction() {
		// 查看手机是否有蓝牙设备功能
		if (hasAdapter(bluetoothAdapter)) {
			if (!bluetoothAdapter.isEnabled()) {
				// 开启蓝牙功能
				bluetoothAdapter.enable();
			}
		} else {
			// 程序终止
			this.finish();
		}
	}

	/**
	 * 查看手机是否有蓝牙设备功能
	 * 
	 * @param ba
	 *            蓝牙设备适配器
	 * @return boolean
	 */
	public boolean hasAdapter(BluetoothAdapter ba) {
		if (ba != null) {
			return true;
		}
		displayLongToast("该手机没有蓝牙功能!");
		return false;
	}

	/**
	 * 创建一个长时间弹出的提示窗口toast
	 * 
	 * @param str
	 *            提示字符串
	 */
	public void displayLongToast(String str) {
		Toast toast = Toast.makeText(this, str, Toast.LENGTH_LONG);
		toast.setGravity(Gravity.TOP, 0, 220);
		toast.show();
	}
	
	/**
	 * 创建一个短时间弹出的提示窗口toast
	 * 
	 * @param str
	 *            提示字符串
	 */
	public void displayShortToast(String str) {
		Toast toast = Toast.makeText(this, str, Toast.LENGTH_SHORT);
		toast.setGravity(Gravity.TOP, 0, 220);
		toast.show();
	}

	/**
	 * 蓝牙若启动,则查询附近的所有蓝牙设备进行选择连接
	 */
	public void connectToDevice() {
		if (bluetoothAdapter.isEnabled()) {
			// 跳到另一个activity---DiscoveryActivity,该类用于查询附近所有的蓝牙设备。
			Intent intent = new Intent(this, DiscoveryActivity.class);
			// 弹出窗口提示
			displayLongToast("请选择一个蓝牙设备进行连接!");

			// 手机此时跳进DiscoveryActivity程序界面。
			// 注意:利用startActivityForResult回调数据返回当前的程序。
			// 详细参考:http://snmoney.blog.163.com/blog/static/440058201073025132670/
			this.startActivityForResult(intent, REQUEST_DISCOVERY);
		} else {
			this.finish();
		}
	}

	/**
	 * startActivityForResult触发调用DiscoveryActivity后进行处理
	 * 获取到相应的蓝牙地址数据后,开始我们核心的数据交互
	 */
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		// TODO Auto-generated method stub
		// super.onActivityResult(requestCode, resultCode, data);

		// 这里确保相互回调时数据的准确传输
		if (requestCode != REQUEST_DISCOVERY) {
			return;
		}
		if (resultCode != RESULT_OK) {
			return;
		}

		// 获取到DiscoveryActivity点击项后传过来的蓝牙设备地址
		String addressStr = data.getStringExtra("address");
		// 根据蓝牙设备地址得到该蓝牙设备对象(这是扫描到的蓝牙设备哦,不是自己的)
		device = bluetoothAdapter.getRemoteDevice(addressStr);
		try {
			//根据UUID创建通信套接字
			btSocket = device.createRfcommSocketToServiceRecord(uuid);
		} catch (Exception e) {
			displayLongToast("通信通道创建失败!");
		}

		if (btSocket != null) {
			try {
				//这一步一定要确保连接上,不然的话程序就卡死在这里了。
				btSocket.connect();
				displayLongToast("通信通道连接成功!");
			} catch (IOException ioe) {
				displayLongToast("通信通道连接失败!");
				try {
					btSocket.close();
					displayLongToast("通信通道已关闭!");
				} catch (IOException ioe2) {
					displayLongToast("通信通道尚未连接,无法关闭!");
				}
			} 
			try {
				// 获取输出流
				outStream = btSocket.getOutputStream();
				// 手机发出数据
				sendSensorData();
			} catch (IOException e) {
				displayLongToast("数据流创建失败!");
			} 
		}
	}

	/**
	 * 发送数据 发出从手机通过重力感应器获取到的数据
	 */
	public void sendSensorData() {
		// 重力感应监听
		SensorEventListener lsn = new SensorEventListener() {
			// 重写内部方法,当精确度发生变化是触发该方法。
			@Override
			public void onAccuracyChanged(Sensor s, int accuracy) {
				// TODO Auto-generated method stub
			}

			// 重写内部方法,当数据发生变化的时候触发该方法。
			@Override
			public void onSensorChanged(SensorEvent se) {
				// TODO Auto-generated method stub
				/**
				 * 当手机横向头部朝左屏幕正对自己时 x=10,y=0,z=0; 当手机竖向屏幕正对自己时 x=0,y=10,z=0;
				 * 当手机平放屏幕朝上时 x=0,y=0,z=10; 由此可知:当手握手机且竖向屏幕正对自己时,有: 水平就是X轴
				 * 垂直就是Y轴 屏幕所对方向便是Z轴 具体可参考简单例子---SensorDemo
				 */
				x = (int)se.values[SensorManager.DATA_X];
				y = (int)se.values[SensorManager.DATA_Y];
				z = (int)se.values[SensorManager.DATA_Z];
				if (y > 5 || y < -5) {
//					String str = String.valueOf(x).concat(String.valueOf(y)).concat(String.valueOf(z));
					String str = "x" + String.valueOf(x) + "y" + String.valueOf(y) + "z" + String.valueOf(z) + "/";
					msgBuffer = str.getBytes();
					try {
						System.out.println("x=" + x + " y =" + y + " z =" + z);
						outStream.write(msgBuffer);
					} catch (IOException e) {
						displayShortToast("数据发送失败!");
					}
				}
//				if (y > 5 || y < -5) {
//					DataModel dataModel=new DataModel(x,y,z);
//					try {
//						System.out.println("x=" + x + " y =" + y + " z =" + z);
//						msgBuffer = dataModel.convertSelfToByteArray();
//						System.out.println("--------"+msgBuffer.length);
//						outStream.write(msgBuffer);
//					} catch (IOException e) {
//						Log.e("BlueTooth",e.getMessage());
//						e.printStackTrace();
//						displayShortToast("数据发送失败!");
//					}
//				}
			}
		};
		

		// 别忘了注册重力感应器,由于android的一些东东是又很大一部分都要这么干的。
		// 所有要注意。比如蓝牙这块,在对它打开的时候其实你也要注册权限的。看AndroidManifest.xml文件。
		sensorMgr
				.registerListener(lsn, sensor, SensorManager.SENSOR_DELAY_GAME);
	}

	// ////////////////////以下是退出程序的一些操作,不关核心功能的事/////////////////////////////////
	/**
	 * 重写方法:点击返回键,确认是否退出程序。
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		// TODO Auto-generated method stub
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			Builder alertDialog = new AlertDialog.Builder(this);
			// 设置弹出框的图标
			alertDialog.setIcon(R.drawable.icon);
			// 设置弹出框的title
			alertDialog.setTitle(R.string.prompt);
			// 设置弹出框的提示信息
			alertDialog.setMessage(R.string.quit);
			// 设置弹出框确认键触发事件
			alertDialog.setPositiveButton(R.string.confirm,
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog,
								int whichButton) {
							// TODO Auto-generated method stub
							try {
								outStream.write("QUIT".getBytes());
								btSocket.close();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							finish(); 
						}
					});
			// 设置弹出框取消键触发事件(不做任何操作)
			alertDialog.setNegativeButton(R.string.cancel,
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							// TODO Auto-generated method stub
						}
					});
			// 显示弹出框
			alertDialog.show();
			return true;
		} else {
			// 如果点击的不是返回键按钮,那么该做什么操作就做什么操作。
			return super.onKeyDown(keyCode, event);
		}
	}

	/**
	 * 重写方法:销毁线程,退出系统。
	 */
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		System.exit(0);
	}

}

 DiscoveryActivity.java

package com.royal.bluetooth;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ListView;
import android.widget.SimpleAdapter;

/**
 * 该类集成ListActivity,主要是扫描并显示出附近所有的蓝牙设备 结果返回给BlueTooth
 * 
 * @author royal
 */
public class DiscoveryActivity extends ListActivity {

	// 获取手机默认上的蓝牙适配器
	private BluetoothAdapter blueToothAdapter = BluetoothAdapter
			.getDefaultAdapter();

	// 把每一个HashMap键值对的蓝牙设备信息存放到list数组中并按文件布局风格的方式呈现出来
	private ArrayList<HashMap<String, String>> list = null;
	// 用于真正存放所有扫描到的蓝牙设备的list
	private List<BluetoothDevice> _devices = new ArrayList<BluetoothDevice>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);

		/* 使程序窗口全屏 */
		// 创建一个没有title的全屏主题
		this.setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
		// 窗口全屏
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		// 设置全屏标志
		this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);

		// 按discovery.xml文件布局风格
		setContentView(R.layout.discovery);

		list = new ArrayList<HashMap<String, String>>();

		// 把扫描都的每一个蓝牙设备放到list中,并呈现给客户端
		showDevices();
	}

	/**
	 * 把扫描都的每一个蓝牙设备放到list中,并呈现给客户端。
	 */
	public void showDevices() {
		// 获取所有已配对的蓝牙设备
		Set<BluetoothDevice> devices = blueToothAdapter.getBondedDevices();

		if (devices.size() > 0) {
			Iterator<BluetoothDevice> it = devices.iterator();
			BluetoothDevice bluetoothDevice = null;
			HashMap<String, String> map = new HashMap<String, String>();
			while (it.hasNext()) {
				bluetoothDevice = it.next();
				// 把每一个获取到的蓝牙设备的名称和地址存放到HashMap数组中,比如:xx:xx:xx:xx:xx: royal
				map.put("address", bluetoothDevice.getAddress());
				map.put("name", bluetoothDevice.getName());
				// 该list用于存放呈现的蓝牙设备,存放的是每个设备的map
				list.add(map);
				// 该list用于存放的是真正的每一个蓝牙设备对象
				_devices.add(bluetoothDevice);
			}

			// 构造一个简单的自定义布局风格,各个参数都有明确的相对应。具体给google一下SimpleAdapter和参考一些文献
			SimpleAdapter listAdapter = new SimpleAdapter(this, list,
					R.layout.device, new String[] { "address", "name" },
					new int[] { R.id.address, R.id.name });
			this.setListAdapter(listAdapter);
		}
	}

	/**
	 * list点击项触发事件 当设备扫描显示完成后,可选择点击相应的设备进行连接。
	 */
	protected void onListItemClick(ListView l, View v, int position, long id) {
		Intent result = new Intent();
		String addressStr = _devices.get(position).getAddress();
		//地址只取到17位,虽然addressStr和address都一样 xx:xx:xx:xx:xx:xx
		String address = addressStr.substring(addressStr.length() - 17);
		
		result.putExtra("address", address);
		// 这个就是回传数据了,将地址传回给BlueTooth---activity
		// 这里的resultCode是RESULT_OK,BlueTooth---activity方法onActivityResult里对应的resultCode也应该是RESULT_OK
		//只有resultCode值相匹配,才能确保result数据回调不出错。
		setResult(RESULT_OK, result);
		// 一定要finish,只有finish后才能将数据传给BlueTooth---activity
		// 并在onActivityResult做处理
		finish();
	}

}

 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.royal.bluetooth"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
    
        <activity android:name=".BlueTooth"
                  android:label="@string/app_name" 
                  android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <activity android:name=".DiscoveryActivity"
        		  android:screenOrientation="landscape">
        	<intent-filter>
        		<action android:name="android.intent.action.MAIN"/>
        		<category android:name="android.intent.category.DEFAULT"/>
        	</intent-filter>
        </activity>

    </application>
    
    <!-- 打开和关闭蓝牙部分的权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    
    <uses-sdk android:minSdkVersion="7" />

</manifest> 

 device.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    android:paddingBottom="1dip"
    android:paddingLeft="10dip"
    android:paddingRight="10dip"
    android:paddingTop="1dip" >

    <TextView
        android:id="@+id/address"
        android:layout_width="180dip"
        android:layout_height="30dip"
        android:singleLine="true"
        android:textSize="10pt" />

    <TextView
        android:id="@+id/name"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="right"
        android:textSize="10pt" />

</LinearLayout>

 discovery.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent" 
	android:layout_height="fill_parent">

	<LinearLayout
		android:id="@+id/listLinearLayout"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:orientation="vertical">
		
		<ListView
			android:id="@id/android:list"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			android:drawSelectorOnTop="false"
			android:scrollbars="vertical"/>
			
	</LinearLayout>
	
</LinearLayout>

 

分享到:
评论

相关推荐

    基于蓝牙的PC与Android端通讯DEMO

    在"基于蓝牙的PC与Android端通讯DEMO"中,我们关注的是如何构建一个跨平台的蓝牙通信系统,使得个人计算机(PC)能够与Android设备相互通信。下面将详细介绍这个过程涉及的关键知识点。 首先,PC端作为服务端,使用...

    蓝牙串口通信工具Demo.zip

    4. **Java在蓝牙通信中的应用**:Java的`android.bluetooth`包提供了处理蓝牙连接和数据传输的类和接口,如`BluetoothAdapter`用于管理蓝牙设备,`BluetoothServerSocket`和`BluetoothSocket`分别用于创建服务器端和...

    手机与PC蓝牙通信程序Demo

    《手机与PC蓝牙通信程序Demo解析》 在现代科技领域,设备间的无线通信技术发展迅速,其中蓝牙技术作为短距离无线通信的一种,被广泛应用在手机、个人计算机(PC)等设备间的数据传输。本篇文章将深入探讨一个由个人...

    Android与PC蓝牙交互源代码

    提供的压缩包文件中,"Android与PC蓝牙交互Android代码.zip"包含了Android应用的源代码,而"Android与PC蓝牙交互PC代码.zip"则包含PC端的实现。通过分析这些源代码,你可以深入理解蓝牙通信的细节,并在此基础上扩展...

    linux下基于Bluez实现蓝牙SPP服务端demo

    基于Bluez实现了蓝牙串口通信,用来与手机端,pc端传输数据。蓝牙模块服务多种多样,这个小demo实现了linux下建立蓝牙服务端的demo,并带了一个客户端测试。 使用时,可通过两台设备,分别作为服务端可客户端,连接...

    Android Socket编程实例(与PC通信,不同手机中间通信)

    在提供的"Android Socket demo"压缩包中,可能包含了一个简单的Android Socket通信示例项目,包括客户端和服务器端的代码实现,你可以通过学习和运行这个示例来加深对Android Socket编程的理解。记得在实际部署时,...

    NRF52832 手机端安卓原码

    "手机端安卓原码"指的是针对Android平台的源代码,用于与NRF52832进行通信。这通常涉及到Android应用开发,使用Java或Kotlin语言编写,可能包含BLE库,如Android BluetoothGatt API,用于连接、读取、写入和订阅NRF...

    经典蓝牙+ble蓝牙(低功耗蓝牙)客户端\服务端开发

    在IT行业中,蓝牙技术是一种...总之,经典蓝牙和BLE蓝牙的客户端和服务端开发涵盖了网络通信、设备发现、数据传输等多个方面,开发者需要深入理解蓝牙协议栈和Android蓝牙API,才能有效地构建稳定、高效的蓝牙应用。

    Android应用源码安卓源码包wifi蓝牙串口&Socket通讯窗口抖动Widget小组件等20个合集.zip

    Android应用源码安卓与PC的Socket通信项目C#版+Java版.rar Android应用源码安卓与PC的Socket通信项目java版.rar Android系统访问串口设备源码.rar android蓝牙连接打印机.rar samsung android 蓝牙4.0开发工具包和...

    热敏打印机sdk外加demo

    热敏打印机SDK是一种软件开发工具包,专门为开发者设计,用于集成到各种平台的应用程序中,以便与热敏打印机进行通信并实现打印功能。这个SDK包含了必要的库、接口、示例代码和文档,使得开发者能够轻松地在PC、...

    局域网android设备发现功能

    在提供的文件中,"T1_Discover_Demo"和"T1_Discover_Demo_server"可能是两个相关的示例应用,一个是客户端(设备发现者),另一个是服务器端(被发现设备)。这两个应用可能包含了实现UDP发现的具体代码和逻辑。例如...

    UnityBluetooth:适用于Unity3D的简单易用的蓝牙适配器

    UnityBluetooth是一款专为Unity3D游戏引擎开发的蓝牙适配器,它允许开发者轻松地在游戏或应用程序中集成蓝牙功能,实现设备之间的通信。这款适配器尤其适用于那些需要进行本地多玩家互动或者数据交换的项目,比如...

    remote_controller:用于创建遥控器的Android应用

    设备之间通过蓝牙传输进行通信。 贡献者 项目领导 前端工程师 后端工程师 前端工程师 测试仪 吉拉 文献资料 设计文件 测试场景 数据库架构 图表文件 莫斯科 技术 Android Studio(Android仿真器) Arduino...

Global site tag (gtag.js) - Google Analytics