Android基於位置的服務,不得不提到andriod.location包,它提供了很方便的API來實現基於位置的服務。和其他android系統服務一樣,我們不能直接實例化一個LocationManager,而是通過getSystemService(Context.LOCATION_SERVICE)獲取LocationManager實例。在獲得LocationManager實例後,我們可以做三件事情:
1、查詢上次已知所有用戶位置列表;
2、註冊或取消註冊用戶位置階段性從LocationProvider獲得的更新;
3、註冊或取消註冊一個指定的Intent,如果手機設備接近一個指定經緯度指定範圍(由半徑米指定)。
相關組件。和location相關的GoogleMap API是MapView,在聚焦MapView時,可捕獲按鍵,觸屏手勢去放大縮小地圖。
相關權限設置。
<manifest ... >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
</manifest>
ACCESS_COARSE_LOCATION只提供NETWORK_PROVIDER的權限,ACCESS_FINE_LOCATION則提供NETWORK_PROVIDER和GPS_PROVIDER。
調試。可通過Eclipse中DDMS調試,或者命令方式調試。打開DDMS的EmulatorControls中有LocationControls部分,選擇設備後,可向設備發送經緯度信息。
Android.Location包的目錄結構分析。
frameworks/base/location/java/android/location
-----Address.aidl提供一個parcelable類
-----Address.java提供地址信息,包含featurename, postal code, country code。
-----Country.aidl提供一個parcelable類
-----Country.java提供ISO標準兩個字符國家碼(CN僅限於大陸,不含港澳臺)
-----ICountryDetector.aidl獲取國家碼接口類
-----CountryDetector.java獲取用戶所在地國家碼
-----ICountryListener.aidl檢測國家碼監聽
----CountryListener.java檢測國家碼監聽
-----Criteria.aidl提供一個parcelable類
-----Criteria.java提供精確度信息
-----GeocoderParams.aidl提供一個parcelable類
-----GeocoderParams.java獲取Geocoder參數
-----Geofence.aidl提供一個parcelable類
-----Geofence.java對獲取的經緯度判斷保護
-----GpsSatellite.java描述GPS衛星狀態,主要在GpsStatus.java中使用
-----GpsStatus.java描述GPSengine的狀態,通過僞隨機碼初始化衛星得到mSatelliteList
-----IGeocodeProvider.aidl接口類提供兩個方法
-----IGpsStatusListener.aidlGPS的狀態監聽。
-----IGpsStatusProvider.aidl增加和移除GPS狀態監聽
-----ILocationListener.aidl定義接口,在LocationManager中ListenerTransport實現
-----LocationListener.java定義接口
-----ILocationManager.aidl定義接口,在LocationManagerService中實現
-----INetInitiatedListener.aidl定義接口GpsLocationProvider中實現
(從多個aidl文件分析看,aidl並非僅在service類中使用,另一種使用方法(Binder)是:定義aidl文件後,在其他文件中只需newIXXX.Stub並實現aidl中定義的方法,然後該實例即可作爲其他類的成員,並最終調用方法)
-----Location.aidl提供一個parcelable類
-----Location.javaLocation類,實現Parcelable的類
-----LocationProvider.java提供諸如是否需要網絡,衛星等信息
-----LocationRequest.aidl提供一個parcelable類
-----LocationRequest.java請求對應精度的位置更新服務
ACCURACY_FINE:1M,ACCURACY_BLOCK100M,ACCURACY_CITY10KM
其中可設置位置更新請求間隔時間setInterval(),最快間隔時間setFastestInterval()等。
-----LocationManager.java其他類或接口大多和這個文件有關,下面篇幅重點介紹
frameworks/base/location/java/com/android/internal/location
-----GpsNetInitiatedHandler.javaGPS網絡初始化的處理類,彈提示框等,在GpsLocationProvider.java中用到。
擴展:frameworks/base/services/jni/下有個Onload.cpp,JNI_OnLoad註冊了一些系統服務如:
PowerManagerService,SystemServer,location_GpsLocationProvider等,其中location_GpsLocationProvider對應註冊JNI接口的文件是com_android_server_location_GpsLocationProvider.cpp
-----ILocationProvider.aidl
-----ProviderProperties.aidl
-----ProviderProperties.java
-----ProviderRequest.aidl
-----ProviderRequest.java
frameworks/base/location/lib/java/com/android/location/provider
-----GeocodeProvider.java
-----LocationProviderBase.java
-----LocationRequestUnbundled.java
-----ProviderPropertiesUnbundled.java
-----ProviderRequestUnbundled.java
frameworks/base/services/java/com/android/server/location
-----ComprehensiveCountryDetector.java
-----CountryDetectorBase.java
-----GeocoderProxy.java
-----GeofenceManager.java
-----GeofenceState.java
-----GpsLocationProvider.java
-----GpsXtraDownloader.java
-----LocationBasedCountryDetector.java
-----LocationBlacklist.java
-----LocationFudger.java
-----LocationProviderInterface.java
-----LocationProviderProxy.java
-----MockProvider.java
-----PassiveProvider.java
問題1:CountryDetector類中的成員ICountryDetectormService調用detectCountry()接口時,怎麼和CountryDetectorService.java中的detectorCountry()聯繫在一起呢?
在系統啓動時,SystemServer起來時,在run()函數中會加載不少系統服務,代碼如:
try{
Slog.i(TAG,"Country Detector");
countryDetector= new CountryDetectorService(context);
ServiceManager.addService(Context.COUNTRY_DETECTOR,countryDetector);
}catch (Throwable e) {
reportWtf("startingCountry Detector", e);
}
而之後運行到ContextImpl.java中時,我們看到有個static代碼塊,代碼片段如下:
static{
......
registerService(COUNTRY_DETECTOR,new StaticServiceFetcher() {
publicObject createStaticService() {
IBinderb = ServiceManager.getService(COUNTRY_DETECTOR);
returnnew CountryDetector(ICountryDetector.Stub.asInterface(b));
}});
......
}
從上面兩個代碼片段就可以看出,在SystemServer加載服務後,在ContextImpl中獲取到對應的Binder後,將CountryDetectorService作爲參數傳給CountryDetector的構造函數中。這樣,就完成了CountryDetectorService和成員變量mService的綁定。從而我們也看出,aidl實現Service的數據通信,並非一定要使用bindService將ServiceConnection和IXXX.aidl綁定在一起。
問題2:IGeocodeProvider作爲aidl文件,如果將虛函數看作插槽。爲什麼GeocodeProvider不直接將接口實現?仍然作爲虛函數使用呢?
搜了一圈,終於在LocationManagerService中找到一個成員mGpsStatusProvider,有如下代碼:
@Override
public boolean addGpsStatusListener(IGpsStatusListener listener) {
if(mGpsStatusProvider == null) {
return false;
}
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),LocationManager.GPS_PROVIDER);
try{
mGpsStatusProvider.addGpsStatusListener(listener);
}catch (RemoteException e) {
Slog.e(TAG,"mGpsStatusProvider.addGpsStatusListener failed", e);
returnfalse;
}
return true;
}
該函數的調用流程實際上是從LocationManager中addGpsStatusListener調起的。爲什麼這麼說呢?和問題1流程相同,在LocationManager中有個變量ILocationManagermService,從問題1分析可得出結論,實際上mService即LocationManagerService。那下面的這段代碼就好理解了:
public boolean addGpsStatusListener(GpsStatus.Listener listener) {
boolean result;
if(mGpsStatusListeners.get(listener) != null) {
//listener is already registered
return true;
}
try{
GpsStatusListenerTransporttransport = new GpsStatusListenerTransport(listener);
result = mService.addGpsStatusListener(transport);
if(result) {
mGpsStatusListeners.put(listener,transport);
}
}catch (RemoteException e) {
Log.e(TAG,"RemoteException in registerGpsStatusListener: ", e);
result = false;
}
return result;
}
一、Geocoder
Geocoder可以在街道地址和經緯度地圖座標之間進行轉換。它提供了對兩種地理編碼功能的訪問:
ForwardGeocoding(前向地理編碼):查找某個地址的經緯度
ReverseGeocoding(反向地理編碼):查找一個給定的經緯度所對應的街道地址。
對應的API是:
List<Address> getFromLocationName(String locationName, int maxResults)
List<Address> getFromLocation(double latitude,double longitude,int maxResults);
二、LocationManager相關的兩個知識點
provider:LocationManager獲取位置信息的途徑,常用的有兩種:GPS和NETWORK。GPS定位更精確,缺點是隻能在戶外使用,耗電嚴重,並且返回用戶位置信息的速度遠不能滿足用戶需求。NETWORK通過基站和Wi-Fi信號來獲取位置信息,室內室外均可用,速度更快,耗電更少。爲了獲取用戶位置信息,我們可以使用其中一個,也可以同時使用兩個。
LocationListener:位置監聽器接口,定義了常見的provider狀態變化和位置的變化的方法,我們需要實現此接口,完成自己的處理邏輯,然後LocationManager註冊此監聽器,完成對各種狀態的監聽。
在locationManager.java中,可以看到PROVIDER有幾種,就會對應有幾種類型的LocationListener。GPS_PROVIDER對應mGpsListener。例如:在MTK寫的一個apk rcse中,AndroidManifest.xml中有如下代碼:
<service
android:name=".core.ims.service.presence.pidf.geoloc.GPSLocationService">
<intent-filter>
<actionandroid:name="com.orangelabs.rcs.GPS" />
</intent-filter>
</service>
GPSLocationService繼承自Service,其中有個onBind()函數,註冊了對應GPS_PROVIDER的監聽:
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000, 0, mGpsListener);
先來看下requestLocationUpdates,mLocationManager是LocationManager類型,在LocationManager.java中找到對應接口後,發現最終調用的是如下接口:
private void requestLocationUpdates(LocationRequest request, LocationListenerlistener,
Looperlooper, PendingIntent intent) {
String packageName = mContext.getPackageName();
//wrap the listener class
ListenerTransport transport = wrapListener(listener, looper);
try{
mService.requestLocationUpdates(request,transport, intent, packageName);
}catch (RemoteException e) {
Log.e(TAG, "RemoteException",e);
}
}
首先,WrapListener將傳入的mGpsListener放入hashmapmListeners中。在try塊代碼中,mLocationManagerService是LocationManagerService類型,最終調用到LocationManagerService.java中的requestLocationUpdates函數。
GoogleMaps V2簡單應用
GoogleMaps Android API V2改進:
1、API作爲GooglePlay
servicesSDK一部分
2、Maps裝入MapFragment類,該類繼承自android的Fragment類,取代MapView
3、允許擴展標準的Activity,而不再必須是V1中的MapActivity
4、使用矢量貼圖,所以地圖顯示更快,佔用帶寬更少
5、改良的緩存,用戶不會再看到有空白區域
6、使用3D地圖,移動用戶的視角,可以看透視圖
開發局限性:
官網上說V1的APIKey 2013年3月18號開始停止申請了,而且V1Key不適用V2。
運行侷限性:
設備安裝有GooglePlay service APK;
版本限制:
android2.2或更高版本
1、SDK配置
打開Eclipse的AndroidSDK Manager,安裝更新Extras下面的googleplay services,這點很關鍵,否則無法使用。按照google官網的步驟執行後出現程序異常,需要加載庫文件解決。
在Eclipse裏面選擇:File>Import>Android>ExistingAndroid Code Into Workspace然後點擊Next.之後Browse...,找到路徑下的<android-sdk-folder>/extras/google/google_play_services/libproject/google-play-services_lib,然後選擇Finish。
第二步是添加對這個庫的引用:在自己的項目上右鍵,選Properties,左邊選Android,然後在下面的Library裏面Add剛纔的google-play-services_lib。
有傳言說V2不能在AVD上運行,可能Google還會對此問題進行更新。在Stackoverflow上已經有過討論,簡單的採用以下方法即可在emulator中跑起來。
在AVD上安裝兩個包(GooglePlay Store和GooglePlayservices):vending.apk和gms.apk,(給一個網盤鏈接:http://pan.baidu.com/share/link?shareid=190602&uk=2701745266)
2、獲取AndroidMaps Api Key
想要獲取googlemap api服務,需要在應用中添加AndroidMaps ApiKey。先找到本地的debug.keystore文件。如果不知道路徑,可打開Eclipse,在Window\Preference\Android\Build中可以看到,一般路徑爲~/.android/debug.keystore。然後用keytool工具獲取唯一的指紋認證碼:
linux中命令如下:
keytool-list -v -keystore ~/.android/debug.keystore -alias androiddebugkey-storepass android -keypass android
window系統命令如下:
keytool-list -v -keystore "C:\Users\your_user_name\.android\debug.keystore"-alias androiddebugkey -storepass android -keypass android
以上命令其實並無太大差別,只是linux和window存儲debug.keystore的路徑不同。
生成的SHA-1指紋認證碼如下:
別名名稱:androiddebugkey
創建日期:2012-8-28
項類型:PrivateKeyEntry
認證鏈長度:1
認證[1]:
所有者:CN=AndroidDebug, O=Android, C=US
簽發人:CN=AndroidDebug, O=Android, C=US
序列號:503c9c42
有效期:Tue Aug 28 18:24:02 CST 2012 至ThuAug 21 18:24:02 CST 2042
證書指紋:
MD5:3A:D9:CA:87:53:A7:38:1C:56:77:32:18:5B:D7:EB:72
SHA1:39:03:D5:1E:0F:F1:E0:AC:12:11:2F:D8:8D:88:0F:EF:58:07:EE:4A
簽名算法名稱:SHA1withRSA
版本:3
爲了獲取APIKey,需要用你的gmail帳號登錄,進入後點擊CreateProject按鈕,左邊導航欄點Services。在其中找到GoogleMaps Android Api v2,選擇打開on,在進入的頁面中接受協議內容並點Accept。然後再在導航欄中選擇ApiAccess,在頁面中找到CreateNew Android Key....然後在以下彈出框中輸入以上指紋及應用包名,中間以分號隔開,例如:39:03:D5:1E:0F:F1:E0:AC:12:11:2F:D8:8D:88:0F:EF:58:07:EE:4A;com.dewav.locationtest
生成40位字符的APIkey碼:
APIkey: |
AIzaSyB3fUxGpH15Y_4y53VBHIps4IueTuTQJnY |
在androidManifest.xml中<application>標籤中間填入apikey,例如:
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyB3fUxGpH15Y_4y53VBHIps4IueTuTQJnY"/>
添加權限控制,例如:
<permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE" android:protectionLevel="signature"/> <uses-permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE"/>
另外,添加其他權限:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/> <!-- The following two permissions are not required to use Google Maps Android API v2, but are recommended. --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
因爲googlemap v2 api要求使用OpenglES Version 2,所以,我們必須加入
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
3、MapFragment和sampleCode
首先在佈局文件中加入MapFragment,
<?xml version="1.0" encoding="utf-8"?> <fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.google.android.gms.maps.MapFragment"/> 在mapActivity中,添加如下代碼: public class MapActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map); } } 除了用Google Map Android Api V1和Google Map Android Api V2外,還可用百度的SDK實現地圖app,詳細實例可參考百度SDK的介紹及sample code。
參考資料:
GoogleMap Android APIV1:https://developers.google.com/maps/documentation/android/v1/hello-mapview?hl=zh-CN
GoogleMap Android API V2:
https://developers.google.com/maps/documentation/android/?hl=zh-CN
APIKEY request:https://code.google.com/apis/console/#project:470805776290:access
介紹百度地圖:http://blog.csdn.net/lyq8479/article/details/6384428
百度地圖SDK:http://developer.baidu.com/map/sdk-android.htm
http://www.cnblogs.com/mengdd/archive/2013/01/01/2841390.html
http://wiki.eoe.cn/page/Using_the_Location_Manager
http://blog.csdn.net/liuhe688/article/details/6566505
http://www.cnblogs.com/feisky/archive/2010/01/20/1652230.html