Android:V4.2.2 Source Insight
在漫长的Android源码编译等待过程中,想起之前写过一部分的Android定位实现的探究小品,于是继续探究。
注:代码都是片段化的代码,用来提纲挈领的说明问题。
定位的基础知识:
1、定位芯片和CPU之间通过串口进行通信
2、串口和CPU之间传输的是ASCII格式的NMEA(National Marine Electronics Association)信息,如:
$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F $GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D $GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70 $GPRMC,092204.999,A,4250.5589,S,14718.5084,E,0.00,89.68,211200,,*25基于以上两点,要探知定位数据从GPS芯片到应用层的流程,最好的途径就是从应用层输出NEMA信息的地方开始。
Luckily,在应用层我们可以通过onNmeaReceived()方法获取到NMEA信息,如下Code Fragment:
public class GpsTestActivity extends ActionBarActivity {
/* Other Codes */
/** 获取系统的定位服务,记得在AndroidManifest中赋予定位方面的权限:
* <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
* <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
* <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
*/
LocationManager mLocationService = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationService.addNmeaListener(mNmeaListener);
private GpsStatus.NmeaListener mNmeaListener = new NmeaListener() {
@Override
public void onNmeaReceived(long timestamp, String nmea) {
System.out.println(nmea + "\n");
}
};
}
GpsStatus.NmeaListener是一个接口类,来自GpsStatus.java文件:
frameworks\base\location\java\android\location\GpsStatus.java
/**
* Used for receiving NMEA sentences from the GPS.
* NMEA 0183 is a standard for communicating with marine electronic devices
* and is a common method for receiving data from a GPS, typically over a serial port.
* See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
* You can implement this interface and call {@link LocationManager#addNmeaListener}
* to receive NMEA data from the GPS engine.
*/
public interface NmeaListener {
void onNmeaReceived(long timestamp, String nmea);
}在上述App中,我们的应用程序实现了该方法,一旦NMEA数据到来,onNmeaReceived()方法就被调用一次,我们在Console上可以看到原始的NEMA信息。frameworks\base\location\java\android\location\LocationManager.java
/**
* Adds an NMEA listener.
*
* @param listener a {@link GpsStatus.NmeaListener} object to register
*
* @return true if the listener was successfully added
*
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
boolean result;
/* mNmeaListeners是LocationManager类的成员变量:
* private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
* new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
*/
if (mNmeaListeners.get(listener) != null) {
// listener is already registered
return true;
}
try {
GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
result = mService.addGpsStatusListener(transport);
if (result) {
mNmeaListeners.put(listener, transport);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
result = false;
}
return result;
}这里,先检测定义的NmeaListener有没有被注册过,若果没有,注册之。// This class is used to send GPS status events to the client's main thread.
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
private final GpsStatus.NmeaListener mNmeaListener;
// This must not equal any of the GpsStatus event IDs
private static final int NMEA_RECEIVED = 1000;
private class Nmea {
long mTimestamp;
String mNmea;
Nmea(long timestamp, String nmea) {
mTimestamp = timestamp;
mNmea = nmea;
}
}
private ArrayList<Nmea> mNmeaBuffer;
//G psStatusListenerTransport(GpsStatus.Listener listener){}
GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
mNmeaListener = listener;
mListener = null;
mNmeaBuffer = new ArrayList<Nmea>();
}
@Override
public void onNmeaReceived(long timestamp, String nmea) {
if (mNmeaListener != null) {
synchronized (mNmeaBuffer) {
mNmeaBuffer.add(new Nmea(timestamp, nmea));
}
Message msg = Message.obtain();
msg.what = NMEA_RECEIVED;
// remove any NMEA_RECEIVED messages already in the queue
mGpsHandler.removeMessages(NMEA_RECEIVED);
mGpsHandler.sendMessage(msg);
}
}
private final Handler mGpsHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == NMEA_RECEIVED) {
synchronized (mNmeaBuffer) {
int length = mNmeaBuffer.size();
for (int i = 0; i < length; i++) {
Nmea nmea = mNmeaBuffer.get(i);
mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
}
mNmeaBuffer.clear();
}
} else {
// synchronize on mGpsStatus to ensure the data is copied atomically.
}
}
}
};
}在GpsStatusListenerTransport类中:rameworks\base\location\java\android\location\IGpsStatusListener.aidl
oneway interface IGpsStatusListener
{
void onGpsStarted();
void onGpsStopped();
void onFirstFix(int ttff);
void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs, in float[] elevations, in float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask);
void onNmeaReceived(long timestamp, String nmea);
}注:和IGpsStatusListener接头的是GpsLocationProvider类:
frameworks\base\services\java\com\android\server\location\GpsLocationProvider.java
public class GpsLocationProvider implements LocationProviderInterface {
// 此处省略1000+N行
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
private final class Listener implements IBinder.DeathRecipient {
final IGpsStatusListener mListener;
Listener(IGpsStatusListener listener) {
mListener = listener;
}
@Override
public void binderDied() {
if (DEBUG) Log.d(TAG, "GPS status listener died");
synchronized (mListeners) {
mListeners.remove(this);
}
if (mListener != null) {
mListener.asBinder().unlinkToDeath(this, 0);
}
}
}
/**
* called from native code to report NMEA data received
*/
private void reportNmea(long timestamp) {
synchronized (mListeners) {
int size = mListeners.size();
if (size > 0) {
// don't bother creating the String if we have no listeners
int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
String nmea = new String(mNmeaBuffer, 0, length);
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
listener.mListener.onNmeaReceived(timestamp, nmea);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in reportNmea");
mListeners.remove(listener);
// adjust for size of list changing
size--;
}
}
}
}
}
}GPS定位功能最终需要调用硬件实现,操作硬件就必须通过C/C++完成,GpsLocationProvider中包含许多native方法,采用JNI机制为上层提供服务。源码编译出错,解决问题去。。。
native_read_nmea()在GpsLocationProvider类中定义:
private native int native_read_nmea(byte[] buffer, int bufferSize);native指明它是本地方法,和它对应的C/C++文件的实现是:
static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jbyteArray nmeaArray, jint buffer_size);How?Next...
frameworks\base\services\jni\com_android_server_location_GpsLocationProvider.cpp
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
/* other members... */
{"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea},
/* other members... */
};JNINativeMethod是Android中采用的Java和C/C++函数的映射方式,并在其中描述了函数的参数和返回值:
typedef struct {
const char* name; // Java文件中的本地方法
const char* signature; // 述了函数的参数和返回值
void* fnPtr; // 指针,指向具体的C/C++函数
} JNINativeMethod;详细内容这里还是不展开了。static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj,
jbyteArray nmeaArray, jint buffer_size)
{
// this should only be called from within a call to reportNmea
jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0);
int length = sNmeaStringLength;
if (length > buffer_size)
length = buffer_size;
memcpy(nmea, sNmeaString, length);
env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
return length;
}待续。。。。。
版权声明:本文为博主原创文章,未经博主允许不得转载。
【Android架构篇】之定位数据如何从GPS芯片到应用层(一)
原文地址:http://blog.csdn.net/u013686019/article/details/47444839