首先告訴你們:個人項目功能包含實時定位,實時軌跡繪製,運動距離展現,縮放自動調整等,基本遇到的bug都被我解決了。你們有問題下面直接問,互相學習(2017.08.08add)!php
首先進入百度地圖開發者中心,找到鷹眼軌跡。按照開發指南首先申請密鑰,即獲取百度要求的sha1值,在Android studio控制檯G:\android2016\LBSdemo>下輸入keytool -list -v -keystore D:\key\lbsdemo.jks提示'keytool' 不是內部或外部命令,也不是可運行的程序。多是我打開方式不對,因而我到命令行窗口在Java的安裝目錄下找到keytool命令,java
那麼按照提示輸入
android
其中命令-keystore後的目錄是Android Studio在打包時開發者設置的key的路徑,接着會讓你輸入當時設置的密碼,密碼輸入時不顯示,回車便可。http://lbsyun.baidu.com/index.php?title=android-yingyan/guide/key此處有官方圖文引導。api
下一步配置工程,下載庫文件解壓後將庫粘到libs文件夾下,並在app的build gradle 添加網絡
sourceSets { main { jniLibs.srcDir 'libs' } }
接下來配置清單文件:(直接從官網上粘)app
一、在Application標籤中聲明SERVICE組件,每一個APP擁有本身獨立的鷹眼追蹤serviceide
<service android:name="com.baidu.trace.LBSTraceService" android:enabled="true" android:process=":remote"> </service>
二、聲明使用權限:工具
<!-- 這個權限用於進行網絡定位--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <!-- 這個權限用於訪問GPS定位--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <!-- 用於訪問wifi網絡信息,wifi信息會用於進行網絡定位--> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> <!-- 獲取運營商信息,用於支持提供運營商信息相關的接口--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <!-- 這個權限用於獲取wifi的獲取權限,wifi信息會用來進行網絡定位--> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> <!-- 用於讀取手機當前的狀態--> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <!-- 寫入擴展存儲,向擴展卡寫入數據,用於寫入對象存儲BOS數據--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <!-- 訪問網絡,網絡定位須要上網--> <uses-permission android:name="android.permission.INTERNET" /> <!-- SD卡讀取權限,用於寫入對象存儲BOS數據--> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <!-- 用於加快GPS首次定位--> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission> <!-- 用於Android M及以上系統,申請加入忽略電池優化白名單--> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"></uses-permission>
在Mainfest.xml正確設置AccessKey(ak),若是設置錯誤將會致使鷹眼服務沒法正常使用。需在Application標籤中加入如下代碼,並填入開發者本身的 Android 類型 ak。meta-data與activity同一級。佈局
AK就是剛剛拿SHA1申請的那個字符串。post
<meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="AK" /> //key:開發者申請的Key
import相關類:
import com.baidu.trace.Trace; import com.baidu.trace.LBSTraceClient; import com.baidu.trace.model.OnCustomAttributeListener; import com.baidu.trace.model.OnTraceListener; import com.baidu.trace.api.track.OnTrackListener; import com.baidu.trace.api.fence.OnFenceListener; import com.baidu.trace.api.entity.OnEntityListener; import com.baidu.trace.api.analysis.OnAnalysisListener; import com.baidu.trace.api.bos.OnBosListener;
以上就是官網上配置工程一節的介紹。
結果,震驚的是,官方demo居然調不起來,看Log
看了論壇的帖子,才意識到我上面獲取的是發佈版本的SHA1,而調試的時候應該用debug的SHA1,那我想的對不對試試就知道了
deblug的SHA1:B2:31:7E:C6:53:FE:4A:1B:E6:C8:B0:24:F5:0A:F5:E4:07:60:A6:BA
release的SHA1:02:D4:70:1E:FD:31:AA:C0:E9:8B:05:E7:DF:0C:DC:99:5D:84:4A:0A
那拿着這個字符串再去百度申請ak,結果,我沉默了,又走了彎路,仍是沒調起來。
我真是醉了,我在命令行拿到的SHA1居然和論壇下載的工具得到的SHA1不同。日狗了。換了SHA1從新設置一下調起來了。接下來就是分析demo移植了。
次日我們來分析demo中是如何得到 手機位置信息並繪製軌跡曲線的。
在TrackApplication中的OnCreate(),
1 @Override 2 public void onCreate() { 3 super.onCreate(); 4 mContext = getApplicationContext(); 5 entityName = CommonUtil.getImei(this); 6 // 若爲建立獨立進程,則不初始化成員變量 7 if ("com.baidu.track:remote".equals(CommonUtil.getCurProcessName(mContext))) { 8 return; 9 } 10 SDKInitializer.initialize(mContext); 11 initView(); 12 initNotification(); 13 mClient = new LBSTraceClient(mContext); 14 mTrace = new Trace(serviceId, entityName); 15 mTrace.setNotification(notification); 16 trackConf = getSharedPreferences("track_conf", MODE_PRIVATE); 17 locRequest = new LocRequest(serviceId); 18 mClient.setOnCustomAttributeListener(new OnCustomAttributeListener() { 19 @Override 20 public Map<String, String> onTrackAttributeCallback() { 21 Map<String, String> map = new HashMap<>(); 22 map.put("key1", "value1"); 23 map.put("key2", "value2"); 24 return map; 25 } 26 }); 27 clearTraceStatus(); 28 }
作了一部分初始化的工做:entityName是設備的IMEI號,在通知欄添加了應用通知Notification告知用戶服務正在運行,實例化軌跡客戶端LBSTraceClient、軌跡服務Trace,Trace有多個構造方法,而且給軌跡服務設置Notification。建立SharedPreference來存儲軌跡服務的配置信息。實例化定位請求LocRequest,mClient.setOnCustomAttributeListener()是爲了自定義參數,官方解釋是:
爲實現自定義屬性數據上傳,開發者須重寫OnCustomAttributeListener監聽器中的onTrackAttributeCallback()接口,調用 LBSTraceClient.setOnCustomAttributeListener()方法設置自定義屬性監聽器,並按照設置的定位週期更新onTrackAttributeCallback()的返回值。SDK每採集一次軌跡,便會自動回調onTrackAttributeCallback()接口,獲取屬性值並寫入當前軌跡點的屬性字段中。自定義屬性監聽器需經過LBSTraceClient.setOnCustomAttributeListener()進行設置,LBSTraceService只回調最新設置的自定義監聽器。onTrackAttributeCallback()的返回值是Map<String, String>類型,每一個對象都是一個<key,value>對,其中key爲entity的自定義字段名稱,value爲值。
clearTraceStatus():
/** * 清除Trace狀態:初始化app時,判斷上次是正常中止服務仍是強制殺死進程,根據trackConf中是否有is_trace_started字段進行判斷。 * <p> * 中止服務成功後,會將該字段清除;若未清除,代表爲非正常中止服務。 */ private void clearTraceStatus() { if (trackConf.contains("is_trace_started") || trackConf.contains("is_gather_started")) { SharedPreferences.Editor editor = trackConf.edit(); editor.remove("is_trace_started"); editor.remove("is_gather_started"); editor.apply(); } }
在TracingActivity如圖,這個佈局就是com.baidu.mapapi.map.MapView和下方兩個按鈕。在OnCreate()的init()初始化三個監聽器和地圖繪製的一些工具類,主要關注點擊兩個按鈕後事件的邏輯。
1 private void init() { 2 initListener(); 3 trackApp = (TrackApplication) getApplicationContext(); 4 viewUtil = new ViewUtil(); 5 mapUtil = MapUtil.getInstance(); 6 mapUtil.init((MapView) findViewById(R.id.tracing_mapView)); 7 mapUtil.setCenter(trackApp); 8 startRealTimeLoc(Constants.LOC_INTERVAL); 9 powerManager = (PowerManager) trackApp.getSystemService(Context.POWER_SERVICE); 10 11 traceBtn = (Button) findViewById(R.id.btn_trace); 12 gatherBtn = (Button) findViewById(R.id.btn_gather); 13 traceBtn.setOnClickListener(this); 14 gatherBtn.setOnClickListener(this); 15 setTraceBtnStyle(); 16 setGatherBtnStyle(); 17 notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 18 }
第8行startRealTimeLoc()看方法名開啓實時定位,傳入實時定位間隔單位是秒,方法實例化了一個內部類runnable,並把這個runnable對象發送到handler處理。這個runnable的子線程調用了TrackApplication的getCurrentLocation()獲取當前位置。
public void startRealTimeLoc(int interval) { realTimeLocRunnable = new RealTimeLocRunnable(interval); realTimeHandler.post(realTimeLocRunnable); }
getCurrentLocation(OnEntityListener entityListener, OnTrackListener trackListener)須要傳入兩個監聽器,在這個方法中,
1 /** 2 * 獲取當前位置 3 */ 4 public void getCurrentLocation(OnEntityListener entityListener, OnTrackListener trackListener) { 5 // 網絡鏈接正常,開啓服務及採集,則查詢糾偏後實時位置;不然進行實時定位 6 if (NetUtil.isNetworkAvailable(mContext) 7 && trackConf.contains("is_trace_started") 8 && trackConf.contains("is_gather_started") 9 && trackConf.getBoolean("is_trace_started", false) 10 && trackConf.getBoolean("is_gather_started", false)) { 11 LatestPointRequest request = new LatestPointRequest(getTag(), serviceId, entityName); 12 ProcessOption processOption = new ProcessOption(); 13 processOption.setNeedDenoise(true); 14 processOption.setRadiusThreshold(100); 15 request.setProcessOption(processOption); 16 mClient.queryLatestPoint(request, trackListener); 17 } else { 18 mClient.queryRealTimeLoc(locRequest, entityListener); 19 } 20 }
判斷是否開啓了軌跡服務和軌跡採集和網絡鏈接肯定查詢糾偏後實時位置或者進行實時定位。構造請求參數和選項請求位置。
回到剛纔的startRealTimeLoc(int interval),傳入的interval間隔也是handler發送消息的間隔。而這個handler在demo中只是簡單的繼承Handler並無自定義handlerMessage()的邏輯。這就是個循環。輪詢設備的位置。
在初始化監聽時,OnTrackListener()重寫了onLatestPointCallback(LatestPointResponse response)方法,其中判斷response的返回碼以及返回的點不是原點後將點轉化爲地圖上的點並調用mapUtil.updateStatus(currentLatLng,true)繪製點。OnEntityListener()重寫了onReceiveLocation(TraceLocation location),與上面相似。OnTraceListener()中重寫了開啓軌跡服務和採集服務成功失敗的回調,
其中上圖的開啓採集是創建在開啓服務的基礎之上的。例如在開啓服務的回調中,成功的話將TrackApplication中的服務開啓標誌位置爲true,並在SharedPreference中持久化,註冊廣播。中止服務的回調中,成功停掉的話把TrackApplication的兩個標記位都置爲false,而且移除SP的兩個key,解除廣播。
public void onStartTraceCallback(int errorNo, String message) { if (StatusCodes.SUCCESS == errorNo || StatusCodes.START_TRACE_NETWORK_CONNECT_FAILED <= errorNo) { trackApp.isTraceStarted = true; SharedPreferences.Editor editor = trackApp.trackConf.edit(); editor.putBoolean("is_trace_started", true); editor.apply(); setTraceBtnStyle(); registerReceiver(); } viewUtil.showToast(TracingActivity.this, String.format("onStartTraceCallback, errorNo:%d, message:%s ", errorNo, message)); }
if (StatusCodes.SUCCESS == errorNo || StatusCodes.CACHE_TRACK_NOT_UPLOAD == errorNo) { trackApp.isTraceStarted = false; trackApp.isGatherStarted = false; // 中止成功後,直接移除is_trace_started記錄(便於區分用戶沒有中止服務,直接殺死進程的狀況) SharedPreferences.Editor editor = trackApp.trackConf.edit(); editor.remove("is_trace_started"); editor.remove("is_gather_started"); editor.apply(); setTraceBtnStyle(); setGatherBtnStyle(); unregisterPowerReceiver(); }
開啓服務時要同時註冊電源鎖和GPS狀態的廣播,中止時解除廣播。這個廣播意義在於:手機鎖屏後一段時間,cpu可能會進入休眠模式,此時沒法嚴格按照採集週期獲取定位依據,致使軌跡點缺失。避免這種狀況的方式是APP持有電量鎖。還有doze 模式:Doze模式是Android6.0上新出的一種模式,是一種全新的、低能耗的狀態,在後臺只有部分任務容許運行,其餘都被強制中止。當用戶一段時間沒有使用手機的時候,Doze模式經過延緩app後臺的CPU和網絡活動減小電量的消耗。若手機廠商生產的定製機型中使用到該模式,須要申請將app添加進白名單,可儘可能幫助鷹眼服務在後臺持續運行。在OnResume()中
// 在Android 6.0及以上系統,若定製手機使用到doze模式,請求將應用添加到白名單。 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { String packageName = trackApp.getPackageName(); boolean isIgnoring = powerManager.isIgnoringBatteryOptimizations(packageName); if (!isIgnoring) { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + packageName)); try { startActivity(intent); } catch (Exception ex) { ex.printStackTrace(); } } }
並在相應的生命週期中調用startRealTimeLoc(packInterval)或者stopRealTimeLoc();
那麼把點擊事件放到後面來講。
case R.id.btn_trace: if (trackApp.isTraceStarted) { trackApp.mClient.stopTrace(trackApp.mTrace, traceListener); stopRealTimeLoc(); } else { trackApp.mClient.startTrace(trackApp.mTrace, traceListener); if (Constants.DEFAULT_PACK_INTERVAL != packInterval) { stopRealTimeLoc(); startRealTimeLoc(packInterval); } } break;
按鈕兩種狀態,中止服務時經過LBS客戶端調用stopTrace(),stopRealTimeLoc();開啓服務時調startTrace(),判斷間隔是否默認值,不然從新調用實時位置方法。
收集按鈕邏輯:
case R.id.btn_gather: if (trackApp.isGatherStarted) { trackApp.mClient.stopGather(traceListener); } else { trackApp.mClient.startGather(traceListener); } break;
那麼咱們的重點是:在本身的demo中定位,而且繪製實時軌跡。並且偏差要在接受範圍內。
其實本身走了一些彎路,踩了一些坑。但願這篇文章能幫到那些也要集成相似功能的開發者朋友。
第一步,將鷹眼的庫文件複製到libs下,鷹眼只負責收集並上傳軌跡,若是須要用到其餘地圖功能須要自行添加相關的庫!
第二步,百度鷹眼平臺申請ak。他有一個獲取應用SHA1碼的工具,若是你們出現230錯誤能夠用這個檢測本身的SHA1是否獲取錯誤。
第三步,按照開發指南配置清單文件和gadle。
第四步,在代碼中作一些初始化,包括:SDKInitializer.intialize(mContext);LBSTraceClient和Trace初始化,client設置時間間隔,定位模式如省電啊或者高精度;BitmapUtil、MapUtil的初始化;mapUtil.init((MapView) findViewById(R.id.mapView));mapview控件的實例化;監聽器初始化,請求HistoryTrackRequest初始化等。不一一列舉。
第五步,在onCreate()中定義本身的定位方法,兩點:輪詢請求位置信息;調用client的定位方法。官方舉了兩種例子:在clent調用startTrace後調用queryLatestPoint()獲取糾偏後的實時位置,此時的回調是onTrackListener接口;不調用startTrace()時調用queryRealTimeLoc()獲取實時位置,此時的回調時onEntityListener接口。在本身初始化回調接口的callback中(onTrackListener的onLatestPointCallback,onEntityListener的onReceiveLocation)調用百度的判斷和繪製方法,關鍵是mapUtil.updateStatus(currentLatLng, true)。此外,startTrace()調用後也是有接口回調的,用來告知開發者服務有沒有被開啓,重寫onTraceListener便可。
第六步,繪製實時軌跡。百度給咱們提供了歷史軌跡查詢和繪製功能。開發者須要作的就是不斷刷新請求到的歷史軌跡的定位點並將他們用折線連起來。那麼具體代碼怎麼實現呢?在上面提到的onTrackListener中有一個onHistoryTrackCallback的回調,看到這個名字就是歷史軌跡回調。這個方法返回了一個HistoryTrackResponse對象,百度有本身的邏輯處理這個對象,咱們要作的就是循環請求上一個endtime到如今System.currentTimeMillis這個時間段的軌跡信息,繪製工做百度已經替咱們作了。本身寫一個循環,調用clent.queryHistoryTrack方法,傳入第四步中初始化的request對象和onTrackListener,並重置request的startTime和endTime便可!
到這裏項目的功能已經實現了,截圖以下:
軌跡記錄包括十幾分鐘的步行和半小時的公交車(我上班的路線),能夠看出功能已經實現了。不足之處可能你們也發現了,在終點處的線都畫成一片了,爲何呢?我當時把他放大又截了一張:
能夠看到終點附近我下了公交步行這一段有明顯錯誤的定位也被繪製到了軌跡中,這就是接下來要說的軌跡糾偏了。
第六步傳入的request對象有一系列糾偏設置:
// 設置須要糾偏 historyTrackRequest.setProcessed(true); // 建立糾偏選項實例 ProcessOption processOption = new ProcessOption(); // 設置須要去噪 processOption.setNeedDenoise(true); // 設置須要抽稀 processOption.setNeedVacuate(true); // 設置須要綁路 processOption.setNeedMapMatch(true); // 設置精度過濾值(定位精度大於100米的過濾掉) processOption.setRadiusThreshold(100); // 設置交通方式爲駕車 // processOption.setTransportMode(TransportMode.walking); // 設置糾偏選項 historyTrackRequest.setProcessOption(processOption);
都是官方的方法,根據須要拿來用就行。中午吃飯的時候發動同事都測一下看效果。結果又遇到問題了,打包後的apk文件你們打開以後只有方格沒有數據,可個人測試可用。應該就是百度ak的問題了,把調試和發佈的SHA1都設置以後就能夠了。
最後分享我在集成百度鷹眼時遇到的一些問題:
1 添加了鷹眼的sdk,發現不全,鷹眼sdk只負責軌跡採集和上傳,若是須要地圖功能,還得再集成地圖sdk。
2 mapView沒法實例化Error inflating class com.baidu.mapapi.map.MapView.....
3 java.lang.IllegalArgumentException: marker's icon can not be null,MapUtil的addOverlay()中的icon爲空,通過仔細排查發現是BitmapUtil沒有實例化
4 項目運行後先出現北京地圖,過了幾秒出現定位地圖,設置選項個人位置
5 在onTrackListener的回調方法中只有實時位置,沒法繪製軌跡。仔細觀察代碼,request須要綁定serviceID
6 查詢軌跡的回調中沒有數據,通過排查,request構建。
7 繪製實時軌跡須要調用startTrace後獲得的數據,不然會空指針。
8 一開始導包後項目直接崩掉,仔細排查發現官方demo的清單文件中的application標籤下加上android:name=".LBSapp",其中name是你項目中繼承了Application的子類。
從接手這個問題到昨天夜裏繪製實時軌跡,花了兩天多的時間,百度的demo確實讓我學到了東西。感謝大佬。原創,歡迎你們提問。
人往高處走,水往低處流。
==============================================八月三號分割線========================================================================
在項目中作運動軌跡和里程計算時,發現了鷹眼使用的一些新問題,因爲使用場景的限制,須要在點擊事件中開啓服務和採集而且要獲取到軌跡和距離信息,固然這些都是要在循環中不斷更新的。出現的問題包括但不限於:沒有定位點;有定位點但開啓軌跡繪製和距離請求後定位點消失且繪製和測距代碼都未執行;未開啓gps時沒有定位信息隨後開啓gps仍無定位和軌跡距離。等等各類異常吧。
通過連夜排查問題發現,在未開啓兩個服務的狀況下調用queryDistance、queryHistoryTrack方法返回的list爲空,軌跡點爲null。即便隨後服務開啓了可是在個人代碼中因爲是根據點的起始時間來更新請求參數的致使我在判斷軌跡點是否爲null以前就作了繪製操做,而百度繪製方法中有一個判斷,當傳入的軌跡點爲零時會移除覆蓋物並將其置爲null這樣就會致使個人地圖上什麼覆蓋物都沒有了並且我是判斷了返回的點的EndPoint不爲null時迭代請求參數,上述狀況下EndPoint是null我沒考慮到,致使位null時仍然在循環相同的請求;此外若是不顯示調用前面的兩個query方法,那麼onTrackListener中相應的回調就不會執行。
因此個人問題其實是對鷹眼服務的使用不夠了解和本身代碼的邏輯不夠嚴謹形成的。
完善辦法:查出這個問題的緣由花了好久,但解決起來就輕鬆多了。第一,確保startService和startGather開啓後再作請求,其實就是在onTraceListener的開啓收集成功回調中或者更改標誌位再開啓請求數據的循環。此處要注意的是,若是在項目中開啓服務和查詢的邏輯寫在一塊兒,再經過標誌位判斷開啓循環請求的話就會出錯,緣由在於開啓服務的代碼回調大概須要兩三秒纔有回調結果,而此時判斷標誌位代碼早已經執行過了,因此請求數據的代碼就永遠得不到執行了。關鍵是第二點,第二點處理好了第一點就沒那麼重要了,在調用請求方法後,onTrackListener的相關回調中作好返回數據爲空和不爲空的邏輯。當返回的點爲空或者不爲空時,應該合理設置請求參數,這樣只要查詢請求的循環跑起來了數據總會及時更新的。第三,在離開軌跡頁面停掉服務時要在onTraceListener的回調中中止輪詢操做。
=============================================================八月八號問題更新==================================================================================
在實時軌跡繪製中會出現縮放頻繁的問題,剛開始十分頭疼。由於認爲這是百度控制的問題,後來通過個人仔細思考,嘿嘿,認爲是由於項目中同時輪詢了定位信息和軌跡繪製,而這兩個在地圖上控制縮放時是有衝突的。在onLatestPointCallback中調用的mapUtil.updateStaus()裏將回調的定位點放置在屏幕的中心區域,只要定位點不在中心就會調用animateMapStaus()。在這個方法中,利用MapStausUpdateFactory的工廠方法構造mapStaus對象再調用baidumap.animateMapStaus()調整縮放。而繪製軌跡調用mapUtil的drawHistoryTrack()方法,此方法中會調用一個重載的animateMapStaus(),會構造一個包含全部歷史軌跡點的mapStaus對象並調用baidumap.animateMapStaus()調整縮放。而在我同時請求定位點和軌跡時這兩個方法就會輪番調用,致使現象就是,一會這個方法設置了一個比例尺,下一秒另外一個同名的重載方法又設置了一個比例尺,地圖 就會不停的縮放。定位了問題以後,就好解決了。項目要求的效果天然是軌跡和定位點必須在屏幕,但定位點不須要必定在屏幕中心,把updateStaus()方法參數稍微改一下便可,使定位點只要在屏幕上就不會調整地圖比例尺就好了。到此運動這一塊跟百度地圖相關的功能都已經解決了。
以上就是最新的鷹眼應用使用分享!若是有以爲描述不清楚的能夠留言。