Bluetooth LE(低功耗蓝牙) - 第二部分
回顾
在前面的文章中我们介绍了Bluetooth LE的背景也说明了我们在本系列文章中将要开发什么,但是还没有实际的代码。我们将在这篇文章中纠正这一点,我们将通过定义 Service/Activity 架构来确保蓝牙操作从UI中解耦。
Service 与 Activity 通信
在我们继续之前,我应该指出的是,我们不打算在这篇文章中去探究BLE的细节。起初,我们打算建立一个Activity并绑定Service,它将使我们能够把所有的蓝牙操作从UI中解耦,同时让我们从BLE接收到数据后更新UI。
要做到这一点,我们将使用Messenger模式。它能够帮助我们不通过任何直接的方法调用而实现两个组件之间的通信。 Messenger模式要求每个组件来实现自身的Messenger实现:当类的实例被创建后处理传入的Message对象。Activity和Service的实现将运行在UI线程,但是我们将确保他们在各自的方法调用上彼此透明。
通过实现他们各自的Messenger实现以及在相应地方的逻辑处理,使我们的代码更容易理解和维护。
public class BleService extends Service {
public static final String TAG = "BleService";
static final int MSG_REGISTER = 1;
static final int MSG_UNREGISTER = 2;
private final Messenger mMessenger;
private final List<Messenger> mClients =
new LinkedList<Messenger>(); public BleService() {
mMessenger = new Messenger(
new IncomingHandler(this));
} @Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
} private static class IncomingHandler extends Handler {
private final WeakReference<BleService> mService; public IncomingHandler(BleService service) {
mService = new WeakReference<BleService>(service);
} @Override
public void handleMessage(Message msg) {
BleService service = mService.get();
if (service != null) {
switch (msg.what) {
case MSG_REGISTER:
service.mClients.add(msg.replyTo);
Log.d(TAG, "Registered");
break;
case MSG_UNREGISTER:
service.mClients.remove(msg.replyTo);
Log.d(TAG, "Unegistered");
break;
default:
super.handleMessage(msg);
}
}
}
}
}
基本代码非常简单,但仍有一些细微之处值得去解释。
首先,InnerHandler 声明为静态。不要试图以一个非静态内部类来实现这个,否则会泄漏内存(leaking memory),这可能非常严重。这是因为一个类的非静态内部类可以持有一个该类的实例,并可以直接访问其成员变量和方法。简单来说,Java垃圾收集器将不会破坏被其他对象引用的对象(实际上比这更复杂一点,但也足够解释所发生的情况)。该Handler的父类实例是一个Service对象(一个Android Context),所以如果有任意一个对象持有该Handler实例的引用,都将隐式地阻止该Service对象被垃圾回收掉。这就是所谓的“Context leak (上下文泄漏)”。它也是一个非常糟糕的事情,因为上下文可能相当大。
(译者注:关于“Context Leak”更多的介绍请查看http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html)
我们避免上下文泄漏的方法是始终将内部类声明为静态,if they are declared within classes which subclass Context (不知道该怎么翻译)。 这也意味着我们已经失去了使用内部类的一个主要优点:访问父类属性和(or或)方法的能力。但我们可以很容易地通过使用WeakReference(弱引用)来克服这一点。弱引用可以使我们保持对一个对象的引用,并且不会阻止它被垃圾回收机制回收。
所以我们的InnerHandler类被构造成拥有一个包裹在WeakReference对象中的它父类的实例的引用。而不是直接拥有其父类的引用。这样InnerHandler可以调用 WeakReference 的get()方法获取其父类实例的引用。我们需要做一个null判断因为父类的实例如果被垃圾回收那么该引用将为空,但如果它不为空,那么我们可以以与非静态内部类完全相同的方式使用该实例的引用。
另一件值得一提的事是,我们目前有两种类型的消息:注册和取消注册。这允许多个用户订阅Service发出的信息(Service会从我们的BLE设备中获得更新信息)。在我们的示例应用程序中只有Activity将从Service中得到更新信息,但在现实世界中应用程序可能有更多的组件所需要的数据,所以发布/订阅模型是合适的。
我们的Activity:
public class BleActivity extends Activity {
public static final String TAG = "BluetoothLE";
private final Messenger mMessenger;
private Intent mServiceIntent;
private Messenger mService = null;
private ServiceConnection mConnection =
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = new Messenger(service);
try {
Message msg = Message.obtain(null,
BleService.MSG_REGISTER);
if (msg != null) {
msg.replyTo = mMessenger;
mService.send(msg);
} else {
mService = null;
}
} catch (Exception e) {
Log.w(TAG, "Error connecting to BleService",
e);
mService = null;
}
} @Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
}; public BleActivity() {
super();
mMessenger = new Messenger(new IncomingHandler(this));
} @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
mServiceIntent = new Intent(this, BleService.class);
} @Override
protected void onStop() {
if (mService != null) {
try {
Message msg = Message.obtain(null,
BleService.MSG_UNREGISTER);
if (msg != null) {
msg.replyTo = mMessenger;
mService.send(msg);
}
} catch (Exception e) {
Log.w(TAG,
"Error unregistering with BleService",
e);
mService = null;
} finally {
unbindService(mConnection);
}
}
super.onStop();
} @Override
protected void onStart() {
super.onStart();
bindService(mServiceIntent, mConnection,
BIND_AUTO_CREATE);
} private static class IncomingHandler extends Handler {
private final WeakReference<BleActivity> mActivity; public IncomingHandler(BleActivity activity) {
mActivity =
new WeakReference<BleActivity>(activity);
} @Override
public void handleMessage(Message msg) {
BleActivity activity = mActivity.get();
if (activity != null) {
//TODO: Do something
}
super.handleMessage(msg);
}
}
}
在Activity的onstart()和onstop()方法中绑定和解绑Service。在ServiceConnection方法(当Service绑定和解除绑定操作完成时回调)中,Activity‘s Messenger 则通过向Service Messenger 发送适当的消息实现注册和反注册。
之后我们需要在Manifest中添加适当的声明(我没有将它展示在这里,但在源码中可以看到) 。我们有一个简单的Activity和Service配对,他们能够互相通信。如果我们运行这个app,它什么都没做,但在logcat中显示了我们所期望的注册/注销工作:
com.stylingandroid.ble D/BleService﹕ Registered
com.stylingandroid.ble D/BleService﹕ Unegistered
所以现在我们拥有了一个app的框架,它使我们能够从Service中收集数据并更新我们的UI。
下期预告
抱歉,我们已经从上周没有包含代码的文章中脱离出来了,这周的文章中虽然包含代码却和本系列文章的主题(Bluetooth LE)不怎么相关。但是对学习BLE来现在是一个好的开始,在下一篇文章将开始把BLE与我们的发现结合在一起。
这篇文章的源代码可以在 这里 找到。
Bluetooth LE(低功耗蓝牙) - 第二部分的更多相关文章
- Bluetooth LE(低功耗蓝牙) - 第一部分
前言 在写这篇文章的时候,谷歌刚刚发布了Android Wear ,摩托罗拉也发布了 Moto 360 智能手表.Android Wear的API还是相当基本的,是很好的文档材料,而且还会不断的更新, ...
- Bluetooth LE(低功耗蓝牙) - 第三部分
回顾 在本系列的前两篇文章中,我们已经了解了一些关于Bluetooth LE的背景并建立一个简单的Activity / Service框架. 在这篇文章中,我们将探讨Bluetooth LE的细节 ...
- Bluetooth LE(低功耗蓝牙) - 第五部分
回顾: 在本系列前面的文章中我们完成了发现BLE传感器并与之建立连接.现在只剩下从其中获取数据了,但是这并没有看起来那么简单.在这篇文章中我们将讨论GATT的特点以及如何促进主机与传感器之间的数据交换 ...
- Bluetooth LE(低功耗蓝牙) - 第四部分
回顾 在本系列前几篇文章中我们完成了BLE设备的发现 , 为我们的app通过BLE显示从TI SensorTag设备中获取到环境温度和湿度的工作打下了基础.在这篇文章中我们将着眼于连接到我们所发现的S ...
- Bluetooth LE(低功耗蓝牙) - 第六部分(完)
在本系列前面的文章中我们已经了解了,在我们从一个TI SensorTag中获取温度和湿度数据之前,我们需要经历的各种步骤.在本系列中的最后一篇文章,我们将完成注册并接收SensorTag的通知,并接收 ...
- Bluetooth Low Energy——蓝牙低功耗
Android4.3(API级别18)引入内置平台支持BLE的central角色,同时提供API和app应用程序用来发现设备,查询服务,和读/写characteristics.与传统蓝牙(Classi ...
- Android使用BLE(低功耗蓝牙,Bluetooth Low Energy)
背景 在学习BLE的过程中,积累了一些心得的DEMO,放到Github,形成本文.感兴趣的同学可以下载到源代码. github: https://github.com/vir56k/bluetooth ...
- 低功耗之战!ANT VS Bluetooth LE
利用近距离无线通信技术将手机及可穿戴式传感器终端等与智能电话连接起来,实现新的功能.最近,以此为目标的行动正在展开.其中备受关注的近距离无线方式是“ANT”和“Bluetooth LE”.为了在各种便 ...
- BLE——低功耗蓝牙(Bluetooth Low Energy)
1.简介 以下蓝牙协议特指低功耗蓝牙协议. 蓝牙协议是由SIG制定并维护的通信协议,蓝牙协议栈是蓝牙协议的具体实现. 各厂商都根据蓝牙协议实现了自己的一套函数库——蓝牙协议栈,所以不同厂商的蓝牙协议栈 ...
随机推荐
- ubuntu15.10英文系统中文输入法配置 fcitx
15.10 默认安装的输入法engine就是fcitx,如果你安装的时候locale选中文,应该不用任何折腾就可以用了,但我习惯了用英文系统,所以..... 系统安装好之后,做如下修改: 安装语言包 ...
- django开发框架之jumpserver
发现一个不错的开源堡垒机 jumpserver: https://github.com/ibuler/jumpserver 最开始看的是jumpserver2.0.0 版本,具体的实现方式是: 1. ...
- td中使用overflow:hidden; 无效解决方案
td中使用overflow:hidden; 无效解决方案 >>>>>>>>>>>>>>>>>> ...
- android java获取当前时间的总结
import java.text.SimpleDateFormat; SimpleDateFormat formatter = new SimpleDateFormat (&q ...
- Bootstrap 开关(switch)控件需要注意的问题
远程文档地址:http://www.bootcss.com/p/bootstrap-switch/ 先上lz遇到的小坑:自古无图无真相的原则 上面代码注释掉后 就是下面这个图片效果!然后加载顺序也要注 ...
- Java-struts2 之值栈问题
这里是根据一个小项目,将数据库的值查出来,然后在页面前台进行遍历的方法 放入值的几种方式: Struts2的三种存值取值的方式 值栈: 栈上下文: ActionContext: package com ...
- oracle学习笔记2:创建修改表
1.创建表 CREATE TABLE ORDERINFO ( ORDERID NUMBER(*, 0) NOT NULL , ORDERCODE VARCHAR2(20 BYTE) NOT NULL ...
- ul ol dl
1.ul是无序列表,也就是说没有排列限制可以随意加li: <ul> <li>可以随意放置</li> <li>可以随意放置</li> < ...
- Centos6.5最小化安装:配置网络和自启动服务
参考http://www.111cn.net/sys/CentOS/56456.htm 1.开启网络连接,禁止IPV6启用 1.开启网络连接 vi /etc/sysconfig/network-sc ...
- Python 学习之urllib模块---用于发送网络请求,获取数据(2)
接着上一次的内容. 先说明一下关于split()方法:它通过指定分隔符对字符串进行切片,如果参数num 有指定值,则仅分隔 num 个子字符串(把一个字符串分割成很多字符串组成的list列表) 语法: ...