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