您的位置:首页 > 移动开发 > Android开发

Android开发实现距离位置变化

2015-11-26 10:10 549 查看
Android开发实现距离位置变化(类似滴滴打车附件的车辆)

使用技术点:
1.百度定位sdk
2.sqlite数据库(用于保存经纬度和实时更新的距离)
3.通过经纬度计算距离的算法方式
4.TimerTask 、Handler

大概思路:
1)创建项目,上传应用到百度定位sdk获得应用对应key,并配置定位服务成功。
2)将配置的定位代码块放入service中,使程序在后台不断更新经纬度
3)为应用创建数据库和相应的数据表,编写 增删改查 业务逻辑方法
4)编写界面,通过点击按钮控制是否开始计算距离,并引用数据库,初始化表数据,实时刷新界面
5)在service的定位代码块中计算距离,并将距离和经纬度实时的保存在数据库(注:只要经纬度发生改变,计算出来的距离就要进行保存)
6)界面的刷新显示

以下是MainActivity中的代码,通过注释可以理解思路流程.

[java] view
plaincopyprint?





package app.ui.activity;

import java.util.Timer;

import java.util.TimerTask;

import android.content.Intent;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.view.WindowManager;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

import app.db.DistanceInfoDao;

import app.model.DistanceInfo;

import app.service.LocationService;

import app.ui.ConfirmDialog;

import app.ui.MyApplication;

import app.ui.R;

import app.utils.ConstantValues;

import app.utils.LogUtil;

import app.utils.Utils;

public class MainActivity extends Activity {

private TextView mTvDistance; //控件

private Button mButton;

private TextView mLng_lat;

private boolean isStart = true; //是否开始计算移动距离

private DistanceInfoDao mDistanceInfoDao; //数据库

private volatile boolean isRefreshUI = true; //是否暂停刷新UI的标识

private static final int REFRESH_TIME = 5000; //5秒刷新一次

private Handler refreshHandler = new Handler(){ //刷新界面的Handler

public void handleMessage(Message msg) {

switch (msg.what) {

case ConstantValues.REFRESH_UI:

if (isRefreshUI) {

LogUtil.info(DistanceComputeActivity.class, "refresh ui");

DistanceInfo mDistanceInfo = 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

private Timer refreshTimer = new Timer(true);

private TimerTask refreshTask = new TimerTask() {

@Override

public void run() {

if (isRefreshUI) {

Message msg = refreshHandler.obtainMessage();

msg.what = ConstantValues.REFRESH_UI;

refreshHandler.sendMessage(msg);

}

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,

WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //保持屏幕常亮

setContentView(R.layout.activity_expensecompute);

startService(new Intent(this,LocationService.class)); //启动定位服务

Toast.makeText(this,"已启动定位服务...", 1).show();

init(); //初始化相应控件

}

private void init(){

mTvDistance = (TextView) findViewById(R.id.tv_drive_distance);

mDistanceInfoDao = new DistanceInfoDao(this);

refreshTimer.schedule(refreshTask, 0, REFRESH_TIME);

mButton = (Button)findViewById(R.id.btn_start_drive);

mLng_lat = (TextView)findViewById(R.id.longitude_Latitude);

}

@Override

public void onClick(View v) {

super.onClick(v);

switch (v.getId()) {

case R.id.btn_start_drive: //计算距离按钮

if(isStart)

{

mButton.setBackgroundResource(R.drawable.btn_selected);

mButton.setText("结束计算");

isStart = false;

DistanceInfo mDistanceInfo = new DistanceInfo();

mDistanceInfo.setDistance(0f); //距离初始值

mDistanceInfo.setLongitude(MyApplication.lng); //经度初始值

mDistanceInfo.setLatitude(MyApplication.lat); //纬度初始值

int id = mDistanceInfoDao.insertAndGet(mDistanceInfo); //将值插入数据库,并获得数据库中最大的id

if (id != -1) {

MyApplication.orderDealInfoId = id; //将id赋值到程序全局变量中(注:该id来决定是否计算移动距离)

Toast.makeText(this,"已开始计算...", 0).show();

}else{

Toast.makeText(this,"id is -1,无法执行距离计算代码块", 0).show();

}

}else{

//自定义提示框

ConfirmDialog dialog = new ConfirmDialog(this, R.style.dialogNoFrame){

@Override

public void setDialogContent(TextView content) {

content.setVisibility(View.GONE);

}

@Override

public void setDialogTitle(TextView title) {

title.setText("确认结束计算距离 ?");

}

@Override

public void startMission() {

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 (方便程序在后台实时更新经纬度)

[java] view
plaincopyprint?





package app.service;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import app.db.DistanceInfoDao;

import app.model.GpsLocation;

import app.model.DistanceInfo;

import app.ui.MyApplication;

import app.utils.BDLocation2GpsUtil;

import app.utils.FileUtils;

import app.utils.LogUtil;

import com.baidu.location.BDLocation;

import com.baidu.location.BDLocationListener;

import com.baidu.location.LocationClient;

import com.baidu.location.LocationClientOption;

import com.computedistance.DistanceComputeInterface;

import com.computedistance.impl.DistanceComputeImpl;

public class LocationService extends Service {

public static final String FILE_NAME = "log.txt"; //日志

LocationClient mLocClient;

private Object lock = new Object();

private volatile GpsLocation prevGpsLocation = new GpsLocation(); //定位数据

private volatile GpsLocation currentGpsLocation = new GpsLocation();

private MyLocationListenner myListener = new MyLocationListenner();

private volatile int discard = 1; //Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。

private DistanceInfoDao mDistanceInfoDao;

private ExecutorService executor = Executors.newSingleThreadExecutor();

@Override

public IBinder onBind(Intent intent) {

return null;

}

@Override

public void onCreate() {

super.onCreate();

mDistanceInfoDao = new DistanceInfoDao(this); //初始化数据库

//LogUtil.info(LocationService.class, "Thread id ----------->:" + Thread.currentThread().getId());

mLocClient = new LocationClient(this);

mLocClient.registerLocationListener(myListener);

//定位参数设置

LocationClientOption option = new LocationClientOption();

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

public void onStart(Intent intent, int startId) {

super.onStart(intent, startId);

}

@Override

public void onDestroy() {

super.onDestroy();

if (null != mLocClient) {

mLocClient.stop();

}

startService(new Intent(this, LocationService.class));

}

private class Task implements Callable<String>{

private BDLocation location;

public Task(BDLocation location){

this.location = location;

}

/**

* 检测是否在原地不动

*

* @param distance

* @return

*/

private boolean noMove(float distance){

if (distance < 0.01) {

return true;

}

return false;

}

/**

* 检测是否在正确的移动

*

* @param distance

* @return

*/

private boolean checkProperMove(float distance){

if(distance <= 0.1 * discard){

return true;

}else{

return false;

}

}

/**

* 检测获取的数据是否是正常的

*

* @param location

* @return

*/

private boolean checkProperLocation(BDLocation location){

if (location != null && location.getLatitude() != 0 && location.getLongitude() != 0){

return true;

}

return false;

}

@Override

public String call() throws Exception {

synchronized (lock) {

if (!checkProperLocation(location)){

LogUtil.info(LocationService.class, "location data is null");

discard++;

return null;

}

if (MyApplication.orderDealInfoId != -1) {

DistanceInfo mDistanceInfo = mDistanceInfoDao.getById(MyApplication.orderDealInfoId); //根据MainActivity中赋值的全局id查询数据库的值

if(mDistanceInfo != null) //不为空则说明车已经开始行使,并可以获得经纬度,计算移动距离

{

LogUtil.info(LocationService.class, "行驶中......");

GpsLocation tempGpsLocation = BDLocation2GpsUtil.convertWithBaiduAPI(location); //位置转换

if (tempGpsLocation != null) {

currentGpsLocation = tempGpsLocation;

}else{

discard ++;

}

//日志

String logMsg = "(plat:--->" + prevGpsLocation.lat + " plgt:--->" + prevGpsLocation.lng +")\n" +

"(clat:--->" + currentGpsLocation.lat + " clgt:--->" + currentGpsLocation.lng + ")";

LogUtil.info(LocationService.class, logMsg);

/** 计算距离 */

float distance = 0.0f;

DistanceComputeInterface distanceComputeInterface = DistanceComputeImpl.getInstance(); //计算距离类对象

distance = (float) distanceComputeInterface.getLongDistance(prevGpsLocation.lat,prevGpsLocation.lng,

currentGpsLocation.lat,currentGpsLocation.lng); //移动距离计算

if (!noMove(distance)) { //是否在移动

if (checkProperMove(distance)) { //合理的移动

float drivedDistance = 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;

}

}

return null;

}

}

}

/**

* 定位SDK监听函数

*/

public class MyLocationListenner implements BDLocationListener {

@Override

public void onReceiveLocation(BDLocation location) {

executor.submit(new Task(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();

}

}

public void onReceivePoi(BDLocation poiLocation) {

if (poiLocation == null){

return ;

}

}

}

}

以下是应用中需要使用的DBOpenHelper数据库类 DBOpenHelper.java

[java] view
plaincopyprint?





package app.db;

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper{

private static final int VERSION = 1; //数据库版本号

private static final String DB_NAME = "distance.db"; //数据库名

public DBOpenHelper(Context context){ //创建数据库

super(context, DB_NAME, null, VERSION);

}

@Override

public void onCreate(SQLiteDatabase db) { //创建数据表

db.execSQL("CREATE TABLE IF NOT EXISTS milestone(id INTEGER PRIMARY KEY AUTOINCREMENT, distance INTEGER,longitude DOUBLE, latitude DOUBLE )");

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //版本号发生改变的时

db.execSQL("drop table milestone");

db.execSQL("CREATE TABLE IF NOT EXISTS milestone(id INTEGER PRIMARY KEY AUTOINCREMENT, distance INTEGER,longitude FLOAT, latitude FLOAT )");

}

}

以下是应用中需要使用的数据库业务逻辑封装类 DistanceInfoDao.java

[java] view
plaincopyprint?





package app.db;

import android.content.Context;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import app.model.DistanceInfo;

import app.utils.LogUtil;

public class DistanceInfoDao {

private DBOpenHelper helper;

private SQLiteDatabase db;

public DistanceInfoDao(Context context) {

helper = new DBOpenHelper(context);

}

public void insert(DistanceInfo mDistanceInfo) {

if (mDistanceInfo == null) {

return;

}

db = helper.getWritableDatabase();

String sql = "INSERT INTO milestone(distance,longitude,latitude) VALUES('"+ mDistanceInfo.getDistance() + "','"+ mDistanceInfo.getLongitude() + "','"+ mDistanceInfo.getLatitude() + "')";

LogUtil.info(DistanceInfoDao.class, sql);

db.execSQL(sql);

db.close();

}

public int getMaxId() {

db = helper.getReadableDatabase();

Cursor cursor = db.rawQuery("SELECT MAX(id) as id from milestone",null);

if (cursor.moveToFirst()) {

return cursor.getInt(cursor.getColumnIndex("id"));

}

return -1;

}

/**

* 添加数据

* @param orderDealInfo

* @return

*/

public synchronized int insertAndGet(DistanceInfo mDistanceInfo) {

int result = -1;

insert(mDistanceInfo);

result = getMaxId();

return result;

}

/**

* 根据id获取

* @param id

* @return

*/

public DistanceInfo getById(int id) {

db = helper.getReadableDatabase();

Cursor cursor = db.rawQuery("SELECT * from milestone WHERE id = ?",new String[] { String.valueOf(id) });

DistanceInfo mDistanceInfo = null;

if (cursor.moveToFirst()) {

mDistanceInfo = new DistanceInfo();

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();

return mDistanceInfo;

}

/**

* 更新距离

* @param orderDealInfo

*/

public void updateDistance(DistanceInfo mDistanceInfo) {

if (mDistanceInfo == null) {

return;

}

db = helper.getWritableDatabase();

String sql = "update milestone set distance="+ mDistanceInfo.getDistance() +",longitude="+mDistanceInfo.getLongitude()+",latitude="+mDistanceInfo.getLatitude()+" where id = "+ mDistanceInfo.getId();

LogUtil.info(DistanceInfoDao.class, sql);

db.execSQL(sql);

db.close();

}

}

以下是需要使用到的实体类 DistanceInfo.java (set数据到对应变量,以实体类作为参数更新数据库)

[java] view
plaincopyprint?





package app.model;

public class DistanceInfo {

private int id;

private float distance;

private double longitude;

private double latitude;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public float getDistance() {

return distance;

}

public void setDistance(float distance) {

this.distance = distance;

}

public double getLongitude() {

return longitude;

}

public void setLongitude(double longitude) {

this.longitude = longitude;

}

public double getLatitude() {

return latitude;

}

public void setLatitude(double latitude) {

this.latitude = latitude;

}

@Override

public String toString() {

return "DistanceInfo [id=" + id + ", distance=" + distance

+ ", longitude=" + longitude + ", latitude=" + latitude + "]";

}

}

保存经纬度信息的类 GpsLocation

[java] view
plaincopyprint?





package app.model;

public class GpsLocation {

public double lat;//纬度

public double lng;//经度

}

将从百度定位中获得的经纬度转换为精准的GPS数据 BDLocation2GpsUtil.java

[java] view
plaincopyprint?





package app.utils;

import it.sauronsoftware.base64.Base64;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.HttpURLConnection;

import java.net.URL;

import org.json.JSONObject;

import app.model.GpsLocation;

import com.baidu.location.BDLocation;

public class BDLocation2GpsUtil {

static BDLocation tempBDLocation = new BDLocation(); // 临时变量,百度位置

static GpsLocation tempGPSLocation = new GpsLocation(); // 临时变量,gps位置

public static enum Method{

origin, correct;

}

private static final Method method = Method.correct;

/**

* 位置转换

*

* @param lBdLocation 百度位置

* @return GPS位置

*/

public static GpsLocation convertWithBaiduAPI(BDLocation lBdLocation) {

switch (method) {

case origin: //原点

GpsLocation location = new GpsLocation();

location.lat = lBdLocation.getLatitude();

location.lng = lBdLocation.getLongitude();

return location;

case correct: //纠偏

//同一个地址不多次转换

if (tempBDLocation.getLatitude() == lBdLocation.getLatitude() && tempBDLocation.getLongitude() == lBdLocation.getLongitude()) {

return tempGPSLocation;

}

String url = "http://api.map.baidu.com/ag/coord/convert?from=0&to=4&"

+ "x=" + lBdLocation.getLongitude() + "&y="

+ lBdLocation.getLatitude();

String result = executeHttpGet(url);

LogUtil.info(BDLocation2GpsUtil.class, "result:" + result);

if (result != null) {

GpsLocation gpsLocation = new GpsLocation();

try {

JSONObject jsonObj = new JSONObject(result);

String lngString = jsonObj.getString("x");

String latString = jsonObj.getString("y");

// 解码

double lng = Double.parseDouble(new String(Base64.decode(lngString)));

double lat = Double.parseDouble(new String(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 (Exception e) {

e.printStackTrace();

return null;

}

tempBDLocation = lBdLocation;

return gpsLocation;

}else{

LogUtil.info(BDLocation2GpsUtil.class, "百度API执行出错,url is:" + url);

return null;

}

}

}

}

需要声明相关权限,且项目中所用到的jar有:
android-support-v4.jar
commons-codec.jar
commons-lang3-3.0-beta.jar
javabase64-1.3.1.jar
locSDK_3.1.jar

项目中目前尚有部分不健全的地方,如:
1.在行驶等待时间较长后,使用TimerTask 、Handler刷新界面是偶尔会出现卡住的现象,车仍在行驶,
但是数据不动了,通过改善目前测试近7次未出现此问题。
2.较大的消耗电量,因为定位服务本身就是个消耗电量的玩意

前段时间因为项目需求,通过百度定位adk写了一个实时更新距离的程序(类似大家坐的士时,车上的里程表),遇到很多技术点,总结了一下发表出来和大家相互学习。直接要求定位具体的位置应该是不难的,只需要引入百度定位adk,并配置相关参数就可以完成,显示百度地图也类似,但是如果需要不断的实时显示移动距离,GPS定位从一个点,到第二个点,从第二个点,到第三个点,从第三个点......,移动距离是多少呢?不得不说,要实现这种需求的确存在一定的难度。
目标:使用百度定位sdk开发实时移动距离计算功能,根据经纬度的定位,计算行驶公里数并实时刷新界面显示。
大家都知道定位有三种方式:GPS 、Wifi 、 基站 .
误差方面的话,使用GPS误差在10左右,Wifi则在20 - 300左右 ,而使用基站则误差在100 - 300左右的样子,因为在室内GPS是定位不到的,必须在室外,
而我们项目的需求正好需要使用GPS定位,所以我们这里设置GPS优先。车,不可能在室内跑吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: