【发布时间】:2011-06-04 15:19:47
【问题描述】:
我一直在就我的 Android 项目提出一系列不断演变的问题,该项目不断实时绘制蓝牙数据。而且我在提问方面做得不是很好。
所以我需要做的是编辑这个问题,清理它,添加重要的细节,最重要的是我需要添加相关代码部分的代码片段,特别是我已经破解了很多的部分,并提供关于这些代码部分的解释。这样,也许我可能会得到我的问题/疑虑的答案:我当前的解决方案是否可行?当我添加新功能时它会持续吗?
基本上我已经完成的是通过将一些开源代码Blueterm 和OrientationSensor 拼凑在一起来创建我的应用程序的第一个版本。
有人建议我添加一个线程、一个处理程序、一个服务,或者使用 Async Task 或 AIDL 等。但我决定我不想修改或替换我现有的解决方案,除非我真的应该这样做。主要是我想知道它是否足以继续前进并扩展它以添加其他功能。
顺便说一下,我之前所说的 BluetoothData 只是蓝牙数据:它是以 2 到 10 个样本/秒的速率从远程蓝牙设备接收的 16 位数据。我的应用程序基本上是一个数据采集系统,它获取/接收蓝牙数据并绘制它。
这是我开始使用的 Blueterm 开源代码的描述(请参阅上面的链接)。 Blueterm 基本上是一个通过蓝牙进行通信的终端仿真程序。它由几个活动组成,Blueterm 是最重要的。它发现、配对和连接支持 SPP/RfComm 的远程蓝牙设备。连接后,我可以使用 Blueterm 通过发送命令来配置远程设备以打开采样、更改要采样的通道数(到一个通道)、更改输入数据的格式(我喜欢逗号分隔的数据)等
这里是我开始使用的 OrientationSensorExample 开源代码的描述(参见上面的链接)。它基本上是 AnroidPlot 库的示例应用程序。 OrientationSensor 活动实现 SensorEventListener。这包括覆盖 onSenorChanged(),每当获取新的方向传感器数据时都会调用它,它会重绘图形。
将这两个开源项目(Blueterm 和 OrientationSensorExample)拼凑到一个应用程序 (Blueterm) 中,下面是对整个应用程序 (Blueterm) 工作原理的描述。当我启动 Blueterm 时,整个屏幕模拟了一个漂亮的蓝色终端。从选项菜单中,我发现、配对、连接和配置远程蓝牙设备,如上所述。配置远程设备后,我再次转到选项菜单并选择“绘图数据”,这将启动绘图活动。终端模拟器消失了,一个漂亮的来自 Plot 活动的滚动实时图出现了。
我是这样做的。在 onOptionsItemSelected() 我添加了一个案例来启动 Plot 活动,如下所示:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.connect:
if (getConnectionState() == BluetoothSerialService.STATE_NONE) {
// Launch the DeviceListActivity to see devices and do scan
Intent serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
}
else
if (getConnectionState() == BluetoothSerialService.STATE_CONNECTED) {
mSerialService.stop();
mSerialService.start();
}
return true;
case R.id.preferences:
doPreferences();
return true;
case R.id.menu_special_keys:
doDocumentKeys();
return true;
case R.id.plot_data:
doPlotData();
return true;
}
return false;
}
private void doPlotData() {
Intent plot_data = new Intent(this, com.vtrandal.bluesentry.Plot.class);
startActivity(plot_data);
}
然后在蓝牙后台线程中我添加了对update()的调用来调用plotData()如下:
/**
* Look for new input from the ptty, send it to the terminal emulator.
*/
private void update() {
int bytesAvailable = mByteQueue.getBytesAvailable();
int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
try {
int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
append(mReceiveBuffer, 0, bytesRead);
//VTR use existing handler that calls update() to get data into plotting activity
//OrientationSensor orientationSensor = new OrientationSensor();
Plot.plotData(mReceiveBuffer, 0, bytesRead);
} catch (InterruptedException e) {
//VTR OMG their swallowing this exception
}
}
然后在Plot活动中我基本打扫了屋子,去掉了“implements SensorEventListener”和一些相关的方法和变量,写了plotData()来调用如上图。以下是 plotData() 及其辅助方法 splitData() 和 nowPlotData() 目前的样子:
private static StringBuffer strData = new StringBuffer("");
public static void plotData(byte[] buffer, int base, int length) {
Log.i("Entering: ", "plotData()");
/*
byte[] buffer = (byte[]) msg.obj;
int base = msg.arg1;
int length = msg.arg2;
*/
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
if (true) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
Log.w("Log_plotData", "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
strData.append(Character.toString(printableB));
if (b == 10)
{
Log.i("End of line: ", "processBlueData()");
Log.i("strData", strData.toString());
splitData(strData);
strData = new StringBuffer("");
}
}
} catch (Exception e) {
Log.e("Log_plotData_exception", "Exception while processing character "
+ Integer.toString(i) + " code "
+ Integer.toString(b), e);
}
}
Log.i("Leaving: ", "plotData()");
}
private static void splitData(StringBuffer strBuf) {
String strDash = strBuf.toString().trim();
String[] strDashSplit = strDash.split("-");
for (int ndx = 0; ndx < strDashSplit.length; ndx++)
{
if (strDashSplit[ndx].length() > 0)
Log.i("strDashSplit", ndx + ":" + strDashSplit[ndx]);
String strComma = strDashSplit[ndx].trim();
String[] strCommaSplit = strComma.split(",");
for (int mdx = 0; mdx < strCommaSplit.length; mdx++)
{
if (strCommaSplit[mdx].length() > 0)
Log.i("strCommaSplit", mdx + ":" + strCommaSplit[mdx]);
if (mdx == 1)
{
int raw = Integer.parseInt(strCommaSplit[1],16);
Log.i("raw", Integer.toString(raw));
float rawFloat = raw;
Log.i("rawFloat", Float.toString(rawFloat));
float ratio = (float) (rawFloat/65535.0);
Log.i("ratio", Float.toString(ratio));
float voltage = (float) (5.0*ratio);
Log.i("voltage", Float.toString(voltage));
nowPlotData(voltage);
}
}
}
}
public static void nowPlotData(float data) {
// get rid the oldest sample in history:
if (plotHistory.size() > HISTORY_SIZE) {
plotHistory.removeFirst();
}
// add the latest history sample:
plotHistory.addLast(data);
// update the plot with the updated history Lists:
plotHistorySeries.setModel(plotHistory, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
//VTR null pointer exception?
if (plotHistoryPlot == null)
Log.i("aprHistoryPlot", "null pointer exception");
// redraw the Plots:
plotHistoryPlot.redraw();
}
总结时间:我基本上在 Blueterm 活动创建的后台线程中找到了 update() 方法。 update() 方法本质上是使用 append() 方法将新接收的蓝牙数据附加到屏幕缓冲区。因此,后台线程的 update() 方法看起来是调用 plotPlot() 的好地方。所以我设计了 plotData() 来绘制传递给 append() 的数据。只要 plotData() 是一个静态方法,它就可以工作。我希望能解释一下为什么 plotData() 看起来必须是静态的才能工作。
我的总体问题/顾虑是:我当前的解决方案是否可行?当我添加新功能时它会持续吗?
【问题讨论】:
标签: android multithreading static android-activity real-time