android中使用百度定位sdk实时的计算移动距离
/***************************************************
*TODO: description .
* @author: gao_chun
* @version: 1.0.0
*@remark: 转载请注明出处
**************************************************/
前段时间因为项目需求,通过百度定位adk写了一个实时更新距离的程序(类似大家坐的士时,车上的里程表),遇到很多技术点,总结了一下发表出来和大家相互学习。直接要求定位具体的位置应该是不难的,只需要引入百度定位adk,并配置相关参数就可以完成,显示百度地图也类似,但是如果需要不断的实时显示移动距离,GPS定位从一个点,到第二个点,从第二个点,到第三个点,从第三个点......,移动距离是多少呢?不得不说,要实现这种需求的确存在一定的难度。
目标:使用百度定位sdk开发实时移动距离计算功能,根据经纬度的定位,计算行驶公里数并实时刷新界面显示。
大家都知道定位有三种方式:GPS 、Wifi 、 基站 .
误差方面的话,使用GPS误差在10左右,Wifi则在20 - 300左右 ,而使用基站则误差在100 - 300左右的样子,因为在室内GPS是定位不到的,必须在室外,
而我们项目的需求正好需要使用GPS定位,所以我们这里设置GPS优先。车,不可能在室内跑吧。
使用技术点:
1.百度定位sdk
2.sqlite数据库(用于保存经纬度和实时更新的距离)
3.通过经纬度计算距离的算法方式
4.TimerTask 、Handler
大概思路:
1)创建项目,上传应用到百度定位sdk获得应用对应key,并配置定位服务成功。
2)将配置的定位代码块放入service中,使程序在后台不断更新经纬度
3)为应用创建数据库和相应的数据表,编写 增删改查 业务逻辑方法
4)编写界面,通过点击按钮控制是否开始计算距离,并引用数据库,初始化表数据,实时刷新界面
5)在service的定位代码块中计算距离,并将距离和经纬度实时的保存在数据库(注:只要经纬度发生改变,计算出来的距离就要进行保存)
6)界面的刷新显示
文章后附源码下载链接
以下是MainActivity中的代码,通过注释可以理解思路流程.
[java] view plain copy print ?
- packageapp.ui.activity;
- importjava.util.Timer;
- importjava.util.TimerTask;
- importandroid.content.Intent;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.view.View;
- importandroid.view.WindowManager;
- importandroid.widget.Button;
- importandroid.widget.TextView;
- importandroid.widget.Toast;
- importapp.db.DistanceInfoDao;
- importapp.model.DistanceInfo;
- importapp.service.LocationService;
- importapp.ui.ConfirmDialog;
- importapp.ui.MyApplication;
- importapp.ui.R;
- importapp.utils.ConstantValues;
- importapp.utils.LogUtil;
- importapp.utils.Utils;
- publicclassMainActivityextendsActivity{
- privateTextViewmTvDistance;//控件
- privateButtonmButton;
- privateTextViewmLng_lat;
- privatebooleanisStart=true;//是否开始计算移动距离
- privateDistanceInfoDaomDistanceInfoDao;//数据库
- privatevolatilebooleanisRefreshUI=true;//是否暂停刷新UI的标识
- privatestaticfinalintREFRESH_TIME=5000;//5秒刷新一次
- privateHandlerrefreshHandler=newHandler(){//刷新界面的Handler
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- caseConstantValues.REFRESH_UI:
- if(isRefreshUI){
- LogUtil.info(DistanceComputeActivity.class,"refreshui");
- DistanceInfomDistanceInfo=mDistanceInfoDao.getById(MyApplication.orderDealInfoId);
- LogUtil.info(DistanceComputeActivity.class,"界面刷新--->"+mDistanceInfo);
- if(mDistanceInfo!=null){
- mTvDistance.setText(String.valueOf(Utils.getValueWith2Suffix(mDistanceInfo.getDistance())));
- mLng_lat.setText("经:"+mDistanceInfo.getLongitude()+"纬:"+mDistanceInfo.getLatitude());
- mTvDistance.invalidate();
- mLng_lat.invalidate();
- }
- }
- break;
- }
- super.handleMessage(msg);
- }
- };
- //定时器,每5秒刷新一次UI
- privateTimerrefreshTimer=newTimer(true);
- privateTimerTaskrefreshTask=newTimerTask(){
- @Override
- publicvoidrun(){
- if(isRefreshUI){
- Messagemsg=refreshHandler.obtainMessage();
- msg.what=ConstantValues.REFRESH_UI;
- refreshHandler.sendMessage(msg);
- }
- }
- };
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
- WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//保持屏幕常亮
- setContentView(R.layout.activity_expensecompute);
- startService(newIntent(this,LocationService.class));//启动定位服务
- Toast.makeText(this,"已启动定位服务...",1).show();
- init();//初始化相应控件
- }
- privatevoidinit(){
- mTvDistance=(TextView)findViewById(R.id.tv_drive_distance);
- mDistanceInfoDao=newDistanceInfoDao(this);
- refreshTimer.schedule(refreshTask,0,REFRESH_TIME);
- mButton=(Button)findViewById(R.id.btn_start_drive);
- mLng_lat=(TextView)findViewById(R.id.longitude_Latitude);
- }
- @Override
- publicvoidonClick(Viewv){
- super.onClick(v);
- switch(v.getId()){
- caseR.id.btn_start_drive://计算距离按钮
- if(isStart)
- {
- mButton.setBackgroundResource(R.drawable.btn_selected);
- mButton.setText("结束计算");
- isStart=false;
- DistanceInfomDistanceInfo=newDistanceInfo();
- mDistanceInfo.setDistance(0f);//距离初始值
- mDistanceInfo.setLongitude(MyApplication.lng);//经度初始值
- mDistanceInfo.setLatitude(MyApplication.lat);//纬度初始值
- intid=mDistanceInfoDao.insertAndGet(mDistanceInfo);//将值插入数据库,并获得数据库中最大的id
- if(id!=-1){
- MyApplication.orderDealInfoId=id;//将id赋值到程序全局变量中(注:该id来决定是否计算移动距离)
- Toast.makeText(this,"已开始计算...",0).show();
- }else{
- Toast.makeText(this,"idis-1,无法执行距离计算代码块",0).show();
- }
- }else{
- //自定义提示框
- ConfirmDialogdialog=newConfirmDialog(this,R.style.dialogNoFrame){
- @Override
- publicvoidsetDialogContent(TextViewcontent){
- content.setVisibility(View.GONE);
- }
- @Override
- publicvoidsetDialogTitle(TextViewtitle){
- title.setText("确认结束计算距离?");
- }
- @Override
- publicvoidstartMission(){
- mButton.setBackgroundResource(R.drawable.btn_noselect);
- mButton.setText("开始计算");
- isStart=true;
- isRefreshUI=false;//停止界面刷新
- if(refreshTimer!=null){
- refreshTimer.cancel();
- refreshTimer=null;
- }
- mDistanceInfoDao.delete(MyApplication.orderDealInfoId);//删除id对应记录
- MyApplication.orderDealInfoId=-1;//停止定位计算
- Toast.makeText(DistanceComputeActivity.this,"已停止计算...",0).show();
- }
- };
- dialog.show();
- }
- break;
- }
- }
- }
以下是LocationService中的代码,即配置的百度定位sdk代码块,放在继承了service的类中 LocationService.java (方便程序在后台实时更新经纬度)
- packageapp.service;
- importjava.util.concurrent.Callable;
- importjava.util.concurrent.ExecutorService;
- importjava.util.concurrent.Executors;
- importandroid.app.Service;
- importandroid.content.Intent;
- importandroid.os.IBinder;
- importapp.db.DistanceInfoDao;
- importapp.model.GpsLocation;
- importapp.model.DistanceInfo;
- importapp.ui.MyApplication;
- importapp.utils.BDLocation2GpsUtil;
- importapp.utils.FileUtils;
- importapp.utils.LogUtil;
- importcom.baidu.location.BDLocation;
- importcom.baidu.location.BDLocationListener;
- importcom.baidu.location.LocationClient;
- importcom.baidu.location.LocationClientOption;
- importcom.computedistance.DistanceComputeInterface;
- importcom.computedistance.impl.DistanceComputeImpl;
- publicclassLocationServiceextendsService{
- publicstaticfinalStringFILE_NAME="log.txt";//日志
- LocationClientmLocClient;
- privateObjectlock=newObject();
- privatevolatileGpsLocationprevGpsLocation=newGpsLocation();//定位数据
- privatevolatileGpsLocationcurrentGpsLocation=newGpsLocation();
- privateMyLocationListennermyListener=newMyLocationListenner();
- privatevolatileintdiscard=1;//Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。
- privateDistanceInfoDaomDistanceInfoDao;
- privateExecutorServiceexecutor=Executors.newSingleThreadExecutor();
- @Override
- publicIBinderonBind(Intentintent){
- returnnull;
- }
- @Override
- publicvoidonCreate(){
- super.onCreate();
- mDistanceInfoDao=newDistanceInfoDao(this);//初始化数据库
- //LogUtil.info(LocationService.class,"Threadid----------->:"+Thread.currentThread().getId());
- mLocClient=newLocationClient(this);
- mLocClient.registerLocationListener(myListener);
- //定位参数设置
- LocationClientOptionoption=newLocationClientOption();
- option.setCoorType("bd09ll");//返回的定位结果是百度经纬度,默认值gcj02
- option.setAddrType("all");//返回的定位结果包含地址信息
- option.setScanSpan(5000);//设置发起定位请求的间隔时间为5000ms
- option.disableCache(true);//禁止启用缓存定位
- option.setProdName("app.ui.activity");
- option.setOpenGps(true);
- option.setPriority(LocationClientOption.GpsFirst);//设置GPS优先
- mLocClient.setLocOption(option);
- mLocClient.start();
- mLocClient.requestLocation();
- }
- @Override
- @Deprecated
- publicvoidonStart(Intentintent,intstartId){
- super.onStart(intent,startId);
- }
- @Override
- publicvoidonDestroy(){
- super.onDestroy();
- if(null!=mLocClient){
- mLocClient.stop();
- }
- startService(newIntent(this,LocationService.class));
- }
- privateclassTaskimplementsCallable<String>{
- privateBDLocationlocation;
- publicTask(BDLocationlocation){
- this.location=location;
- }
- /**
- *检测是否在原地不动
- *
- *@paramdistance
- *@return
- */
- privatebooleannoMove(floatdistance){
- if(distance<0.01){
- returntrue;
- }
- returnfalse;
- }
- /**
- *检测是否在正确的移动
- *
- *@paramdistance
- *@return
- */
- privatebooleancheckProperMove(floatdistance){
- if(distance<=0.1*discard){
- returntrue;
- }else{
- returnfalse;
- }
- }
- /**
- *检测获取的数据是否是正常的
- *
- *@paramlocation
- *@return
- */
- privatebooleancheckProperLocation(BDLocationlocation){
- if(location!=null&&location.getLatitude()!=0&&location.getLongitude()!=0){
- returntrue;
- }
- returnfalse;
- }
- @Override
- publicStringcall()throwsException{
- synchronized(lock){
- if(!checkProperLocation(location)){
- LogUtil.info(LocationService.class,"locationdataisnull");
- discard++;
- returnnull;
- }
- if(MyApplication.orderDealInfoId!=-1){
- DistanceInfomDistanceInfo=mDistanceInfoDao.getById(MyApplication.orderDealInfoId);//根据MainActivity中赋值的全局id查询数据库的值
- if(mDistanceInfo!=null)//不为空则说明车已经开始行使,并可以获得经纬度,计算移动距离
- {
- LogUtil.info(LocationService.class,"行驶中......");
- GpsLocationtempGpsLocation=BDLocation2GpsUtil.convertWithBaiduAPI(location);//位置转换
- if(tempGpsLocation!=null){
- currentGpsLocation=tempGpsLocation;
- }else{
- discard++;
- }
- //日志
- StringlogMsg="(plat:--->"+prevGpsLocation.lat+"plgt:--->"+prevGpsLocation.lng+")\n"+
- "(clat:--->"+currentGpsLocation.lat+"clgt:--->"+currentGpsLocation.lng+")";
- LogUtil.info(LocationService.class,logMsg);
- /**计算距离*/
- floatdistance=0.0f;
- DistanceComputeInterfacedistanceComputeInterface=DistanceComputeImpl.getInstance();//计算距离类对象
- distance=(float)distanceComputeInterface.getLongDistance(prevGpsLocation.lat,prevGpsLocation.lng,
- currentGpsLocation.lat,currentGpsLocation.lng);//移动距离计算
- if(!noMove(distance)){//是否在移动
- if(checkProperMove(distance)){//合理的移动
- floatdrivedDistance=mDistanceInfo.getDistance();
- mDistanceInfo.setDistance(distance+drivedDistance);//拿到数据库原始距离值,加上当前值
- mDistanceInfo.setLongitude(currentGpsLocation.lng);//经度
- mDistanceInfo.setLatitude(currentGpsLocation.lat);//纬度
- //日志记录
- FileUtils.saveToSDCard(FILE_NAME,"移动距离--->:"+distance+drivedDistance+"\n"+"数据库中保存的距离"+mDistanceInfo.getDistance());
- mDistanceInfoDao.updateDistance(mDistanceInfo);
- discard=1;
- }
- }
- prevGpsLocation=currentGpsLocation;
- }
- }
- returnnull;
- }
- }
- }
- /**
- *定位SDK监听函数
- */
- publicclassMyLocationListennerimplementsBDLocationListener{
- @Override
- publicvoidonReceiveLocation(BDLocationlocation){
- executor.submit(newTask(location));
- LogUtil.info(LocationService.class,"经度:"+location.getLongitude());
- LogUtil.info(LocationService.class,"纬度:"+location.getLatitude());
- //将经纬度保存于全局变量,在MainActivity中点击按钮时初始化数据库字段
- if(MyApplication.lng<=0&&MyApplication.lat<=0)
- {
- MyApplication.lng=location.getLongitude();
- MyApplication.lat=location.getLatitude();
- }
- }
- publicvoidonReceivePoi(BDLocationpoiLocation){
- if(poiLocation==null){
- return;
- }
- }
- }
- }
[java] view plain copy print ?
- packageapp.db;
- importandroid.content.Context;
- importandroid.database.sqlite.SQLiteDatabase;
- importandroid.database.sqlite.SQLiteOpenHelper;
- publicclassDBOpenHelperextendsSQLiteOpenHelper{
- privatestaticfinalintVERSION=1;//数据库版本号
- privatestaticfinalStringDB_NAME="distance.db";//数据库名
- publicDBOpenHelper(Contextcontext){//创建数据库
- super(context,DB_NAME,null,VERSION);
- }
- @Override
- publicvoidonCreate(SQLiteDatabasedb){//创建数据表
- db.execSQL("CREATETABLEIFNOTEXISTSmilestone(idINTEGERPRIMARYKEYAUTOINCREMENT,distanceINTEGER,longitudeDOUBLE,latitudeDOUBLE)");
- }
- @Override
- publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){//版本号发生改变的时
- db.execSQL("droptablemilestone");
- db.execSQL("CREATETABLEIFNOTEXISTSmilestone(idINTEGERPRIMARYKEYAUTOINCREMENT,distanceINTEGER,longitudeFLOAT,latitudeFLOAT)");
- }
- }
以下是应用中需要使用的数据库业务逻辑封装类 DistanceInfoDao.java
[java] view plain copy print ?
- packageapp.db;
- importandroid.content.Context;
- importandroid.database.Cursor;
- importandroid.database.sqlite.SQLiteDatabase;
- importapp.model.DistanceInfo;
- importapp.utils.LogUtil;
- publicclassDistanceInfoDao{
- privateDBOpenHelperhelper;
- privateSQLiteDatabasedb;
- publicDistanceInfoDao(Contextcontext){
- helper=newDBOpenHelper(context);
- }
- publicvoidinsert(DistanceInfomDistanceInfo){
- if(mDistanceInfo==null){
- return;
- }
- db=helper.getWritableDatabase();
- Stringsql="INSERTINTOmilestone(distance,longitude,latitude)VALUES('"+mDistanceInfo.getDistance()+"','"+mDistanceInfo.getLongitude()+"','"+mDistanceInfo.getLatitude()+"')";
- LogUtil.info(DistanceInfoDao.class,sql);
- db.execSQL(sql);
- db.close();
- }
- publicintgetMaxId(){
- db=helper.getReadableDatabase();
- Cursorcursor=db.rawQuery("SELECTMAX(id)asidfrommilestone",null);
- if(cursor.moveToFirst()){
- returncursor.getInt(cursor.getColumnIndex("id"));
- }
- return-1;
- }
- /**
- *添加数据
- *@paramorderDealInfo
- *@return
- */
- publicsynchronizedintinsertAndGet(DistanceInfomDistanceInfo){
- intresult=-1;
- insert(mDistanceInfo);
- result=getMaxId();
- returnresult;
- }
- /**
- *根据id获取
- *@paramid
- *@return
- */
- publicDistanceInfogetById(intid){
- db=helper.getReadableDatabase();
- Cursorcursor=db.rawQuery("SELECT*frommilestoneWHEREid=?",newString[]{String.valueOf(id)});
- DistanceInfomDistanceInfo=null;
- if(cursor.moveToFirst()){
- mDistanceInfo=newDistanceInfo();
- mDistanceInfo.setId(cursor.getInt(cursor.getColumnIndex("id")));
- mDistanceInfo.setDistance(cursor.getFloat(cursor.getColumnIndex("distance")));
- mDistanceInfo.setLongitude(cursor.getFloat(cursor.getColumnIndex("longitude")));
- mDistanceInfo.setLatitude(cursor.getFloat(cursor.getColumnIndex("latitude")));
- }
- cursor.close();
- db.close();
- returnmDistanceInfo;
- }
- /**
- *更新距离
- *@paramorderDealInfo
- */
- publicvoidupdateDistance(DistanceInfomDistanceInfo){
- if(mDistanceInfo==null){
- return;
- }
- db=helper.getWritableDatabase();
- Stringsql="updatemilestonesetdistance="+mDistanceInfo.getDistance()+",longitude="+mDistanceInfo.getLongitude()+",latitude="+mDistanceInfo.getLatitude()+"whereid="+mDistanceInfo.getId();
- LogUtil.info(DistanceInfoDao.class,sql);
- db.execSQL(sql);
- db.close();
- }
- }
以下是需要使用到的实体类 DistanceInfo.java (set数据到对应变量,以实体类作为参数更新数据库)
[java] view plain copy print ?
- packageapp.model;
- publicclassDistanceInfo{
- privateintid;
- privatefloatdistance;
- privatedoublelongitude;
- privatedoublelatitude;
- publicintgetId(){
- returnid;
- }
- publicvoidsetId(intid){
- this.id=id;
- }
- publicfloatgetDistance(){
- returndistance;
- }
- publicvoidsetDistance(floatdistance){
- this.distance=distance;
- }
- publicdoublegetLongitude(){
- returnlongitude;
- }
- publicvoidsetLongitude(doublelongitude){
- this.longitude=longitude;
- }
- publicdoublegetLatitude(){
- returnlatitude;
- }
- publicvoidsetLatitude(doublelatitude){
- this.latitude=latitude;
- }
- @Override
- publicStringtoString(){
- return"DistanceInfo[id="+id+",distance="+distance
- +",longitude="+longitude+",latitude="+latitude+"]";
- }
- }
保存经纬度信息的类 GpsLocation
[java] view plain copy print ?
- packageapp.model;
- publicclassGpsLocation{
- publicdoublelat;//纬度
- publicdoublelng;//经度
- }
[java] view plain copy print ?
- packageapp.utils;
- importit.sauronsoftware.base64.Base64;
- importjava.io.BufferedReader;
- importjava.io.IOException;
- importjava.io.InputStreamReader;
- importjava.net.HttpURLConnection;
- importjava.net.URL;
- importorg.json.JSONObject;
- importapp.model.GpsLocation;
- importcom.baidu.location.BDLocation;
- publicclassBDLocation2GpsUtil{
- staticBDLocationtempBDLocation=newBDLocation();//临时变量,百度位置
- staticGpsLocationtempGPSLocation=newGpsLocation();//临时变量,gps位置
- publicstaticenumMethod{
- origin,correct;
- }
- privatestaticfinalMethodmethod=Method.correct;
- /**
- *位置转换
- *
- *@paramlBdLocation百度位置
- *@returnGPS位置
- */
- publicstaticGpsLocationconvertWithBaiduAPI(BDLocationlBdLocation){
- switch(method){
- caseorigin://原点
- GpsLocationlocation=newGpsLocation();
- location.lat=lBdLocation.getLatitude();
- location.lng=lBdLocation.getLongitude();
- returnlocation;
- casecorrect://纠偏
- //同一个地址不多次转换
- if(tempBDLocation.getLatitude()==lBdLocation.getLatitude()&&tempBDLocation.getLongitude()==lBdLocation.getLongitude()){
- returntempGPSLocation;
- }
- Stringurl="http://api.map.baidu.com/ag/coord/convert?from=0&to=4&"
- +"x="+lBdLocation.getLongitude()+"&y="
- +lBdLocation.getLatitude();
- Stringresult=executeHttpGet(url);
- LogUtil.info(BDLocation2GpsUtil.class,"result:"+result);
- if(result!=null){
- GpsLocationgpsLocation=newGpsLocation();
- try{
- JSONObjectjsonObj=newJSONObject(result);
- StringlngString=jsonObj.getString("x");
- StringlatString=jsonObj.getString("y");
- //解码
- doublelng=Double.parseDouble(newString(Base64.decode(lngString)));
- doublelat=Double.parseDouble(newString(Base64.decode(latString)));
- //换算
- gpsLocation.lng=2*lBdLocation.getLongitude()-lng;
- gpsLocation.lat=2*lBdLocation.getLatitude()-lat;
- tempGPSLocation=gpsLocation;
- LogUtil.info(BDLocation2GpsUtil.class,"result:"+gpsLocation.lat+"||"+gpsLocation.lng);
- }catch(Exceptione){
- e.printStackTrace();
- returnnull;
- }
- tempBDLocation=lBdLocation;
- returngpsLocation;
- }else{
- LogUtil.info(BDLocation2GpsUtil.class,"百度API执行出错,urlis:"+url);
- returnnull;
- }
- }
- }
- }
需要声明相关权限,且项目中所用到的jar有:
android-support-v4.jar
commons-codec.jar
commons-lang3-3.0-beta.jar
javabase64-1.3.1.jar
locSDK_3.1.jar
Android中计算地图上两点距离的算法
项目中目前尚有部分不健全的地方,如:
1.在行驶等待时间较长后,使用TimerTask 、Handler刷新界面是偶尔会出现卡住的现象,车仍在行驶,
但是数据不动了,通过改善目前测试近7次未出现此问题。
2.较快的消耗电量
源码下载地址
【转载注明gao_chun的Blog:http://blog.csdn.net/gao_chun/article/details/38229339】
更多相关文章
- android中如何获取经纬度?
- Android下载文件时对MediaScanner的调用
- Android(安卓)SQLite数据库判断某张表是否存在的语句
- Android中使用SQLiteDatabase对数据库进行操作
- 经纬度转度分秒 Java/Android
- Android数据库编程:SqLiteOpenHelper的使用
- Android/J2SE计算两个位置坐标之间的距离
- Android(安卓)获取经纬度同时获取当前具体城市信息