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信息的地方开始。
NMEA资料参见:卫星定位数据NMEA介绍
一、GPS定位的应用层实现
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");
}
};
}
二、GPS定位的Framework层实现
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信息。
那么接下来,就要寻找nmea数据的来源了。
mNmeaListener通过LocationManager类的addNmeaListener()方法进行注册(register):
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有没有被注册过,若果没有,注册之。
注册到哪里去了呢?
由mNmeaListeners成员的定义可知,和GpsStatus.NmeaListener进行关联的是GpsStatusListenerTransport,而它是LocationManager类的一个内部类。
只看相关的部分:
// This class is used to send GPS status events to the client's main thread.
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub