蓝牙笔和手机通过蓝牙协议的通讯 通过蓝牙笔点击杂志上印刷的电影,手机上播放点中的电影应用
先描述一下我的这个应用,用一根蓝牙笔点击特制的杂志上印刷的电影名称,比如:点击片名为《金龙鱼葵花籽油5L》的视频,蓝牙笔读取到视频名称背后的杂志码比如是234567,因为蓝牙笔只能读到指令不可能直接读取到234567这个杂志码,比如234567这个杂志码对应的指令是qwerty,然后我把这个qwerty解析成234567,然后去数据库里查询这个234567所对应的播放地址比如是:http://www.youku.com/movie/fengsheng.ram,然后用WebView去打开这个网址,就可以实现蓝牙笔点击电影名称,在手机上播放的效果了。
应用实现步骤
1:先把蓝牙笔和手机配对
只有蓝牙配对的两个具有蓝牙适配器的设备才具有通讯的功能
2:首先在AndroidManifest.xml里面配置权限
//打开蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH" />
//蓝牙管理权限
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
//访问互联网权限
<uses-permission android:name="android.permission.INTERNET" />
3:阅读深圳生产蓝牙笔的厂商提供的参考文档
从文档来看,蓝牙笔接收到请求后会给我们客户端发送的数据包的大小是10个字节
4:假设场景
我们假设在这个场景中,蓝牙笔是服务端,我的手机应用是客户端,蓝牙笔内置的程序已经被厂商写好了,内置程序一直在等待请求连接,如果连接上了蓝牙笔就把它里面的数据返给我。这只是假设。我这个假设被深圳厂商确定了,所以我们只需要编写客户端的BlueSocket就行了。
5:布局文件
main.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"
>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="请求远程蓝牙笔"
android:id="@+id/btn_request"
/>
</LinearLayout>
main2.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"
>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="读取数据"
android:id="@+id/btn_read"
/>
<WebView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
6:代码文件BluetoothRequest.java
package com.menglin.bluetoothrequest;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.bluetooth.BluetoothSocket;
public class MminActivity extends Activity
{
private BluetoothSocket bluetoothSocket = null;
private Button btn_request = null;
private Button btn_read = null;
private Boolean connect_result = false;
private WebView webview;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//得到连接按钮
btn_request = (Button)findViewById(R.id.btn_request);
//绑定连接监听器事件
btn_request.setOnClickListener(new ButtonListener());
}
//绑定连接按钮的监听器
private class ButtonListener implements OnClickListener
{
@Override
public void onClick(View v)
{
//得到BluetoothAdapter对象
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
//判断BluetoothAdapter对象是否为空,如果为空,则表明本机没有蓝牙设备
if(adapter != null)
{
Log.d("mytag","手机拥有蓝牙设备");
//调用isEnabled()方法判断当前蓝牙设备是否可用
if(!adapter.isEnabled())
{
//如果蓝牙设备不可用的话,创建一个intent对象,该对象用于启动一个Activity,提示用户启动蓝牙适配器
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent);
}
//得到所有已经配对的远程蓝牙适配器对象
Set<BluetoothDevice> devices = adapter.getBondedDevices();
if(devices.size()>0)
{
//用迭代
for(Iterator iterator = devices.iterator();iterator.hasNext();)
{
//得到BluetoothDevice对象,也就是说得到配对的蓝牙适配器
BluetoothDevice device = (BluetoothDevice)iterator.next();
//输出远程蓝牙设备的地址和名称进行测试用
Log.d("mytag",device.getAddress());
Log.d("mytag",device.getName());
//翻译Android的SDK得到这些信息“创建一个RFCOMM BluetoothSocket准备开始一个安全装置连接到这个偏僻的外向利用SDP uuid查找的。提示:如果你是连接到一个蓝牙系列板然后试着用著名的仕达屋优先计划UUID 0000 - 00001101 - 1000 - 8000 - 00805 F9B34FB。然而如果你是连接到一个机器人同伴那么请产生你自己独特的UUID。 ”
//所以我们就就要用到规定好的蓝牙串口服务唯一标识
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
try
{
bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid);
bluetoothSocket.connect();
//能输出这句话证明连接上远程的蓝牙笔了
Log.d("mytag","我连接上了远程的蓝牙笔了");
connect_result = true;
//表示连接上了远程的蓝牙笔
if(connect_result == true)
{
//加载第二个布局文件
setContentView(R.layout.main2);
webview = (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true);
//得到读取蓝牙笔数据按钮
btn_read = (Button)findViewById(R.id.btn_read);
//绑定读取蓝牙笔数据的按钮监听事件
btn_read.setOnClickListener(new ButtonReadListener());
}
else
{
Log.d("mytag","没有连接上蓝牙笔");
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
else
{
System.out.println("这个手机没有蓝牙设备");
}
}
}
//绑定读取蓝牙笔数据按钮的监听器
private class ButtonReadListener implements OnClickListener
{
@Override
public void onClick(View v)
{
if(connect_result)
{
try
{
InputStream inputStream = bluetoothSocket.getInputStream();
//从深圳生产蓝牙笔的厂商提供的参考文档来看,蓝牙笔接收到请求后会给我们客户端发送的数据包的大小是10个字节,所以我们正好用10个字节去接收数据包。
byte data[] = new byte[10];
int i = 0;
//从InputStream对象中读取服务端蓝牙笔所发送的数据
inputStream.read(data);
long codeid = GetCodeID(data);
webview.loadUrl("我们的网址?CodeID="+ codeid);
}
catch (IOException e)
{
e.printStackTrace();
}
}
else
{
Log.d("mytag","没有连接上蓝牙笔");
}
}
}
//得到最终的杂志码
//我这段代码比较麻烦了,貌似可以转化成DWord,在统一转化为十六进制格式
private long GetCodeID(byte data[])
{
String result = "";
String str = "";
for (int i = 0; i < data.length; i++)
{
//阅读深圳生产蓝牙笔的厂商提供的参考文档,我们发现’+‘,它已经定义好了占一个字节,我们不管这个”+“是什么意思,“I”和“D”也已经被厂商定义好了,共占2个字节,我们依然不管是什么意思,序号占一个字节,\r和\n各占一个字节,所以我们需要的数据其实只是数组下标为4,5,6,7的value
//注意从蓝牙笔里面读取出来的数据是十进制的,我们要把它最终转化为十六进制
//只要数组下标为4,5,6,7的value
if(i == 4 || i == 5 || i == 6 || i == 7)
{
//如果是正数
if(data[i]>0)
{
str = Integer.toHexString(data[i]);//将value转化为十六进制字符
}
else
{
str = Integer.toHexString(data[i]);//将value转化为十六进制字符
str = str.replace("ffffff", ""); //替换负数转化中出现的补位字符
}
result += str;
}
}
int codeid = Integer.parseInt(result,16);//将最终的字符串按十六进制转换为数字
return codeid;
}
}
7:调试截图
我们看到代码已经运行到Log.d()说明已经连接上远程的蓝牙笔了。
我们从Debug模式中监视变量的值,这个字节数组中看到data[0]=43,而43的ASCII码值是“+”,正好是深圳厂商给的文档里的第一个字符,data[1]=73 而73的ASCII码值是“I”,正好是深圳厂商给的文档里的第二个字符,data[3]=68 而73的ASCII码值是“D”,data[8]=13,而13的ASCII码值是“\r”,正好是深圳厂商给的文档里的倒数第二个字符,data[9]=10而10的ASCII码值是“\n”,正好是深圳厂商给的文档里的最后一个字符。所以我们需要的数据其实只是数组下标为4,5,6,7的value,那么是data[4]=0,而0的十六进制还是0,data[5]=2,而2的十六进制还是2,那么是data[6]=38,而38的十六进制是26,data[7]=-54,而-54的十六进制是ffffffca,那么这个数据包就是0226ca,而 0226ca的十进制是141002。
8:去数据库里查询看读到的这个141002对不对
注意看,我们通过蓝牙笔得到的141002和我们数据库里查询的一样吧
最终我们找到《金龙鱼葵花籽油5L》的视频的播放地址了,我们只需要把这个地址给WebView的loadUrl()方法,就能实现蓝牙笔点击杂志,手机播放被点击的视频了
9:运行流程
蓝牙笔开机的时候指示灯是红绿交替显示,当单击“请求远程蓝牙笔”按钮后,如果连接上了, 蓝牙笔的指示灯是绿色不停的显示同时手机屏幕会切换到布局文件为main2.xml的界面,然后我们把蓝牙笔头触放到杂志的视频名称或者图片上,点击“读取数据”按钮后,就能在手机上打开你所点的视频了。
10:扩展场景
假设以后地铁站或者公交站有麦当劳的大的电子Logo,我们用蓝牙笔(当然不是我现在做这个应用的蓝牙笔了它太大了),也许就是个手机挂链,点击这个Logo,就把麦当劳的优惠劵下载到手机上了。