首先明确,采集GPS数据定位的方式有:GPS卫星定位、WIFI定位、基站定位、AGPS定位。GPS的应用在LBS行业软件中被视为核心。
(1)Android GPS:需要GPS硬件支持,直接和卫星交互来获取当前经纬度,这种方式需要手机支持GPS模块(现在大部分的智能机应该都有了)。通过GPS方式准确度是最高的,但是它的缺点也非常明显:1,比较耗电;2,绝大部分用户默认不开启GPS模块;3,从GPS模块启动到获取第一次定位数据,可能需要比较长的时间;4,室内几乎无法使用。这其中,缺点2,3都是比较致命的。需要指出的是,GPS走的是卫星通信的通道,在没有网络连接的情况下也能用。
(2)Android Wifi定位:根据一个固定的WifiMAC地址,通过收集到的该Wifi热点的位置,然后访问网络上的定位服务以获得经纬度坐标。因为它和基站定位其实都需要使用网络,所以在Android也统称为Network方式。
(3)Android 基站定位:Android 基站定位只要明白了基站/WIFI定位的原理,自己实现基站/WIFI定位其实不难。基站定位一般有几种,第一种是利用手机附近的三个基站进行三角定位,由于每个基站的位置是固定的,利用电磁波在这三个基站间中转所需要时间来算出手机所在的坐标;第二种则是利用获取最近的基站的信息,其中包括基站 id,location area code、mobile country code、mobile network code和信号强度,将这些数据发送到google的定位web服务里,就能拿到当前所在的位置信息,误差一般在几十米到几百米之内。
(4)AGPS定位:AGPS(AssistedGPS:辅助全球卫星定位系统)是结合GSM或GPRS与传统卫星定位,利用基地台代送辅助卫星信息,以缩减GPS芯片获取卫星信号的延迟时间,受遮盖的室内也能借基地台讯号弥补,减轻GPS芯片对卫星的依赖度。和纯GPS、基地台三角定位比较,AGPS能提供范围更广、更省电、速度更快的定位服务,理想误差范围在10公尺以内,日本和美国都已经成熟运用AGPS于LBS服务(Location Based Service,基于位置的服务)。AGPS技术是一种结合了网络基站信息和GPS信息对移动台进行定位的技术,可以在GSM/GPRS、WCDMA和CDMA2000网络中使进行用。该技术需要在手机内增加GPS接收机模块,并改造手机的天线,同时要在移动网络上加建位置服务器、差分GPS基准站等设备。AGPS解决方案的优势主要体现在其定位精度上,在室外等空旷地区,其精度在正常的GPS工作环境下,可以达到10米左右,堪称目前定位精度最高的一种定位技术。该技术的另一优点为:首次捕获GPS信号的时间一般仅需几秒,不像GPS的首次捕获时间可能要2~3分钟 。
综上所述,最优方案应该是AGPS定位,因为它省时(定位时间快)、省电、精度高,接受度更高。
(1)、实现GPS数据本地化:采用SQLite 按照一定的时间频率(一般10s、15s、20s、30s甚至可以更大,视应用数据量而定,一般同样的时间采集的点越多那么更容易描述这段时间GPS设备移动的轨迹)采集GPS到本地SQLite数据库存储之。
(2)、实现GPS数据定时上传:每隔半个小时,定期向服务器抛出线程(采用异步线程)执行本地数据向远处服务器同步操作。
(3)、注册服务并允许在APP后台:采用BroadcastReceiver来注册本地服务(数据采集)和远程服务(数据上传)。
package com.boonya.wtms.service; import com.boonya.wtms.domain.GPSLocation; import com.boonya.wtms.utils.Constant; import com.boonya.wtms.utils.DateParser; import android.app.Service; import android.content.Context; import android.content.Intent; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.IBinder; public class SystemLocalService extends Service { static LocationManager loctionManager; static String provider; private GPSLocationService gpsLocationService; Context tag = SystemLocalService.this; @Override public void onCreate() { super.onCreate(); // 获取GPS位置处理类实例 gpsLocationService = new GPSLocationService(tag); } @Override public IBinder onBind(Intent arg0) { return null; } // 位置监听器 @Override @Deprecated public void onStart(Intent intent, int startId) { // 启动GPS监听 initGPSLocationListener(); super.onStart(intent, startId); } @Override public void onDestroy() {// 重写的onDestroy方法 loctionManager.removeUpdates(locationListener); super.onDestroy(); } private final LocationListener locationListener = new LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onProviderEnabled(String provider) { } @Override public void onProviderDisabled(String provider) { } // 当位置变化时触发 @Override public void onLocationChanged(Location location) { if (location != null) { Thread thread = new Thread(new DoCollectThread(location)); thread.start(); } } }; /** * 初始化GPS位置监听 */ protected void initGPSLocationListener() { String contextService = Context.LOCATION_SERVICE; // 通过系统服务,取得LocationManager对象 loctionManager = (LocationManager) getSystemService(contextService); // 3. 得到位置提供器,通过位置提供器,得到位置信息,可以指定具体的位置提供器,也可以提供一个标准集合,让系统根据 // 标准匹配最适合的位置提供器,位置信息是由位置提供其提供的。 // a. 通过GPS位置提供器获得位置(指定具体的位置提供器) // String provider = LocationManager.GPS_PROVIDER; // Location location = loctionManager.getLastKnownLocation(provider); // b. 使用标准集合,让系统自动选择可用的最佳位置提供器,提供位置 Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE);// 高精度 criteria.setAltitudeRequired(true);// 要求海拔 criteria.setBearingRequired(true);// 要求方位 criteria.setSpeedRequired(true); // 要求速度 criteria.setCostAllowed(true);// 允许有花费 criteria.setPowerRequirement(Criteria.POWER_LOW);// 低功耗 // 从可用的位置提供器中,匹配以上标准的最佳提供器 provider = loctionManager.getBestProvider(criteria, true); // 获得最后一次变化的位置 // Location location = loctionManager.getLastKnownLocation(provider); // 最后将位置信息显示在TextView中,如图: // 监听位置的变化 // 监听位置变化,2秒一次,距离10米以上 loctionManager.requestLocationUpdates(provider, Constant.GPS_COLLECT_TIME, Constant.GPS_COLLECT_DISTANCE, locationListener); } /** * 本地GPS采集数据入库至SQLite * * @author boonya * * @having-line--------------------------------------------------------- * @filename SystemLocalService.java * @function TODO * @start-at 2014-12-11,下午4:47:14 * @having-line--------------------------------------------------------- */ class DoCollectThread implements Runnable { Location location; public DoCollectThread(Location location) { this.location = location; } @Override public void run() { GPSLocation gpsLocation = new GPSLocation(-1, location.getLatitude(), location.getLongitude(), location.getAltitude(), location.getBearing(), location.getSpeed(), DateParser.getSystemDateStr()); gpsLocationService.saveToLocal(gpsLocation); } } }
package com.boonya.wtms.service; import java.util.List; import java.util.Timer; import java.util.TimerTask; import com.boonya.wtms.domain.GPSLocation; import com.boonya.wtms.utils.Constant; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; public class SystemRemoteService extends Service { Context tag = SystemRemoteService.this; private GPSLocationService gpsLocationService; @Override public void onCreate() { super.onCreate(); gpsLocationService=new GPSLocationService(tag); } @Override public IBinder onBind(Intent arg0) { return null; } @SuppressWarnings("deprecation") @Override public void onStart(Intent intent, int startId) { doService(); super.onStart(intent, startId); } protected void doService() { // 调度任务执行 new Timer().schedule(new TimerTask() { @Override public void run() { // 保存本地的GPS数据到远程服务器 List<GPSLocation> gpsLocations=gpsLocationService.findAll(); boolean flag=gpsLocationService.saveToServer(gpsLocations); if(flag) { // 删除已经上传的GPS数据 gpsLocationService.delete(gpsLocations); } } }, 0, Constant.GPS_THREAD_TIME_WAIT); } }
package com.boonya.wtms.service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class SystemBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 调用Service context.startService(new Intent("com.boonya.wtms.service.System_LocalService")); Log.i("device-gps-local-service", "GPS本地采集服务启动成功!"); // 调用Service context.startService(new Intent("com.boonya.wtms.service.System_RemoteService")); Log.i("device-gps-remote-service", "GPS远程上传服务启动成功!"); } }
<receiver android:name="com.boonya.wtms.service.SystemBroadcastReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </receiver> <service android:name="com.boonya.wtms.service.SystemRemoteService" > <intent-filter> <action android:name="com.boonya.wtms.service.System_RemoteService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> <service android:name="com.boonya.wtms.service.SystemLocalService" > <intent-filter> <action android:name="com.boonya.wtms.service.System_LocalService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" > </uses-permission> <uses-permission android:name="android.permission.INTERNET" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" > </uses-permission> <!-- <uses-permission android:name="android.permission.WRITE_APN_SETTINGS"></uses-permission> --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" > </uses-permission> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="android.permission.WAKE_LOCK" > </uses-permission> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_GPS" />