通过本例子说明如何搭建自定义的应用层服务。
本例子建立了一个模拟的Gps应用服务,通过监听串口设备获取GPS数据(非阻塞),获取到有效的数据后通过handler通知UI线程刷新,以下给出源码
GpsDevice.java
package com.example.gpsservice;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import android.location.Location;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class GpsDevice {
/*****************************************************************************************************************/
private final String TAG = "GpsDevice";
private final int GPS_Device_SERIAL_GPS_READABLE = 0x1000;
private int fd = -1;
private FileDescriptor mFd = null;
private FileInputStream mFileInputStream = null;
private FileOutputStream mFileOutputStream = null;
private GpsLocationChangedListener mGpsListener;
private Location mLastLocation;
private Location mLocation;
private GpsDeviceListenerHandler mHandler;
private GpsDeviceMonitorThread mMonitorThread;
/*****************************************************************************************************************/
static {
System.loadLibrary("GpsDevice");
}
/*****************************************************************************************************************/
public GpsDevice() throws IOException
{
// 打开模拟GPS设备,串口
fd = openSerialPort("/dev/ttyUSB2");
if( fd == -1 )
{
throw new IOException();
}
// 设置串口属性为115200 8 N 1
if( setSerialPort(fd, 115200, 8, 0 , 1, 0 ) < 0 )
throw new IOException();
// 转换fd为输入输出流
FileDescriptor mFd = convertFileDescriptor(fd);
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
// 启动handler 用于处理listener, 确保listener工作在UI主线程,否则刷新UI会报错
mHandler = new GpsDeviceListenerHandler();
// 开启监听端口线程
mMonitorThread = new GpsDeviceMonitorThread();
mLastLocation = new Location("");
mLocation = new Location("");
// 将fd加入可读监听队列
Selector.clearAllSets();
Selector.addReadSet(fd);
}
protected void finalize()
{
mMonitorThread.stop();
closeSerialPort(fd);
}
public void setGpsLocationChangedListener(GpsLocationChangedListener listener)
{
mGpsListener = listener;
}
public void run()
{
mMonitorThread.start();
}
public void checkLocationChanged()
{
if( mLocation.getLatitude() != mLastLocation.getLatitude() || mLocation.getLongitude() != mLastLocation.getLongitude())
{
if( mGpsListener != null ) mGpsListener.onGpsLocationChanged(mLocation);
mLastLocation.setLatitude(mLocation.getLatitude());
mLastLocation.setLongitude(mLocation.getLongitude());
}
}
public void getLocation()
{
byte[] buffer = new byte[512];
int size = 0;
try {
size = mFileInputStream.read(buffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 解析串口协议数据,本例中为自定义数据,可以根据自己的协议修改
int a = buffer[0] & 0xFF;
int b = buffer[1] & 0xFF;
if( a == 0x5A && b == 0xBB && size == 17 )
{
int n1, n2, n3, n4;
n1 = buffer[9] & 0xFF;
n2 = buffer[8] & 0xFF;
n3 = buffer[7] & 0xFF;
n4 = buffer[6] & 0xFF;
double latitude = n1 << 24 | n2 << 16 | n3 << 8 | n4;
n1 = buffer[13] & 0xFF;
n2 = buffer[12] & 0xFF;
n3 = buffer[11] & 0xFF;
n4 = buffer[10] & 0xFF;
double longitude = n1 << 24 | n2 << 16 | n3 << 8 | n4;
mLocation.setLatitude(latitude/1000000);
mLocation.setLongitude(longitude/1000000);
Log.d(TAG, "latitude : " + mLocation.getLatitude() + ",longitude = " + mLocation.getLongitude());
}
}
/*****************************************************************************************************************/
// CLASS
private class GpsDeviceMonitorThread extends Thread{
public void run()
{
while (!isInterrupted())
{
if( Selector.runSelector(100) > 0 )
{
Message message = new Message();
if( Selector.isReadable(fd))
{
getLocation();
message.what = GPS_Device_SERIAL_GPS_READABLE;
mHandler.sendMessage(message);
}
}
else
{
Log.d(TAG, "select time out");
}
}
}
}
private class GpsDeviceListenerHandler extends Handler
{
public void handleMessage(Message msg)
{
switch (msg.what)
{
case GPS_Device_SERIAL_GPS_READABLE:
checkLocationChanged();
break;
default:
break;
}
super.handleMessage(msg);
}
}
/*****************************************************************************************************************/
// INTERFACE
public interface GpsLocationChangedListener
{
public void onGpsLocationChanged(Location location);
}
/*****************************************************************************************************************/
//JNI
private native int openSerialPort(String device);
private native FileDescriptor convertFileDescriptor(int fd);
//@param[in] baudrate 2400, 4800, 9600, 9600, 19200, 38400, 57600, 115200
//@param[in] databit 5, 6, 7, 8
//@param[in] fctl flow control, 0: none, 1: hardware, 2: software
//@param[in] stopbit 1, 2
//@param[in] parity 0: none, 1: odd, 2: even
private native int setSerialPort(int fd, int baudrate, int databit, int fctl, int stopbit, int parity);
private native int closeSerialPort(int fd);
}
为了实现非阻塞的读取设备,本例采用了select轮训方式查看设备是否可读,便于扩展成多设备,多服务
Selector.java
package com.example.gpsservice;
// Linux Select 函数的简单封装
public class Selector {
static {
System.loadLibrary("Selector");
}
public static void clearAllSets()
{
ClearAllSets();
}
public static void clearReadSets()
{
ClearReadSets();
}
public static void clearWriteSets()
{
ClearWriteSets();
}
public static void addReadSet(int fd)
{
AddReadSet(fd);
}
public static void addWriteSet(int fd)
{
AddWriteSet(fd);
}
public static void removeReadSet(int fd)
{
RemoveReadSet(fd);
}
public static void removeWriteSet(int fd)
{
RemoveWriteSet(fd);
}
public static boolean isReadable(int fd)
{
return IsReadSet(fd) > 0;
}
public static boolean isWritable(int fd)
{
return IsWriteSet(fd) > 0;
}
// -1 : error 0 : timeout 1 : readable or writable
public static int runSelector(int timeout)
{
return DoSelect(timeout);
}
// JNI
private static native void ClearAllSets();
private static native void ClearReadSets();
private static native void ClearWriteSets();
private static native void AddReadSet(int fd);
private static native void AddWriteSet(int fd);
private static native void RemoveReadSet(int fd);
private static native void RemoveWriteSet(int fd);
private static native int IsReadSet(int fd);
private static native int IsWriteSet(int fd);
private static native int DoSelect(int timeout);
}
最后通过UI简单测试,拿到有效的GPS数据后,通过TextView显示出来
GpsService.java
package com.example.gpsservice;
import java.io.IOException;
import android.app.Activity;
import android.location.Location;
import android.os.Bundle;
import android.view.Menu;
import android.widget.TextView;
import com.example.gpsservice.GpsDevice.GpsLocationChangedListener;
public class GpsService extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gps_service);
try {
GpsDevice gpsDevice = new GpsDevice();
gpsDevice.setGpsLocationChangedListener(new GpsLocationChangedListener(){
@Override
public void onGpsLocationChanged(Location location) {
// TODO Auto-generated method stub
updateGpsLocation(location);
}});
gpsDevice.run();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_gps_service, menu);
return true;
}
public void updateGpsLocation(Location locaction)
{
if( locaction == null ) return;
((TextView)findViewById(R.id.TextView_GpsInfo)).setText("N:" + locaction.getLatitude() + ",E:" + locaction.getLongitude());
}
}