转载请标明出处:http://blog.csdn.net/android_ls/article/details/10179013
一、定位模块的需求:我们想知道使用我们应用的用户的大概位置,每隔五分钟上传一次用户当前所在地的经纬度值到服务器端。
二、需求分析
A、UML流程图如下:
B、定位服务,功能具体分析:
启动方式:手动、开机自启动。
关闭方式:用户在设置里强制停止应用、关闭手机。(用户使用其他软件杀死掉我们的服务,用户重新启动应用服务才会开启。)
1、开机自启动服务,等1分钟后开始检测网络状态和GPS是否开启,并通过通知栏提醒用户。(未开启时,提醒三次,5分钟提醒一次)
2、直接启动应用,立即开始检测网络状态和GPS是否开启,并通过弹Dialog提示用户。若用户不愿意开启网络,即网络不可用时,直接退出应用。
3、用户在设置-->应用程序-->正在运行的服务里面手动停止掉服务后,服务自动重启。
4、网络检测可用,开始检测GPS。用户不开启GPS时,使用基站定位(WLAN、3G/2G)。
5、网络检测可用,启动百度地图定位服务,每隔五分钟确认一次当前我所在的位置,并将经纬度值上传服务器端。
6、网络检测可用,但是在发送定位数据时,网络断开了,以Toast形式提醒用户。
7、网络检测可用,但是在定位过程中,网络断开了,并且目前打开的不是我们的应用(也就是说服务在后台运行),以通知的形式提醒用户。
8、服务运行过程中,意外停止了。当用户重启应用后,服务重新启动。
9、添加了开机自启动后,检测网络和通过通知栏提醒用户当前的网络、GPS状态。
10、服务运行过程中,网络检测返回的标识的处理。
三、编码实现:
完整源码下载地址:http://download.csdn.net/detail/android_ls/5993623,核心类的源码如下,
A、服务类的源码如下:
package com.android.mobile.locator; import java.io.IOException; import java.io.InputStream; import java.util.Timer; import java.util.TimerTask; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Handler; import android.os.IBinder; import android.os.Message; import com.android.mobile.locator.net.HttpRequester; import com.android.mobile.locator.utils.Constant; import com.android.mobile.locator.utils.FileUtil; import com.android.mobile.locator.utils.GPSUtil; import com.android.mobile.locator.utils.LogUtil; import com.android.mobile.locator.utils.NetWorkUtil; import com.android.mobile.locator.utils.NotificationUtil; import com.android.mobile.locator.utils.ServiceUtil; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.easi.mobile.locator.R; /** * 类名:MobileLocatorService * 功能描述:定位服务类。 * @author android_ls * 创建日期:2013-2-18 */ public class MobileLocatorService extends Service { /** * Service action. */ public static final String ACTION_MOBILE_LOCATOR_SERVICE = "com.easi.mobile.locator.MobileLocatorService"; /** * 间隔时间5分钟 */ private static final int DELAY_TIME = 5*60*1000; /** * 开机一分钟后开始检测网络 */ private static final int CHECK_NETWORK_DELAY_TIME = 1 * 60 * 1000; private Context mContext; /** * 定位SDK的核心类 */ private LocationClient mLocationClient; /** * 定位结果处理器 # class MyLocationListener implements BDLocationListener{} */ private MyLocationListener mLocationListener; /** * 通知工具类 */ private NotificationUtil mNotificationUtil; /** * 服务的启动方式,开机自启动/手动启动 */ private int startingMode = -1; /** * 当前网络是否可用的标志 */ private boolean isOpenNetwork = false; /** * 检测网络的次数(5分钟一次,检测三次) */ private int checkNetworkNumber = 0; /** * 定时器 */ private Timer mTimer; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { LogUtil.e("--------------MobileLocatorService onCreate()----------------"); mNotificationUtil = new NotificationUtil(this); mContext = MobileLocatorService.this; // 设置为前台进程,尽量避免被系统干掉。 // MobileLocatorService.this.setForeground(true); // 初始化定位服务,配置相应参数 initLocationService(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtil.e("--------------MobileLocatorService onStartCommand()----------------"); if (intent != null) { startingMode = intent.getIntExtra("startingMode", -1); LogUtil.i("startingMode = " + startingMode); if (startingMode == Constant.HANDLER_START_SERVICE) { LogUtil.e("-------------手动启动---------------"); // 判断服务是否已开启 boolean isRun = ServiceUtil.isServiceRun(getApplicationContext(), "com.baidu.location.f"); LogUtil.i("isRun = " + isRun); if (isRun == false) { LogUtil.e("MobileLocatorService start Location Service"); // 没启动,开启定位服务 mLocationClient.start(); } } else { // 关闭手机,再次开启手机。这种情况下,startingMode的值获取不到。 // 关机重启,这种情况下,startingMode的值可以拿到。 // if (startingMode == Constant.BOOT_START_SERVICE) { LogUtil.e("-------------开机自启动---------------"); checkNetworkNumber++; // 第一次,1分钟后检测网络 mHandler.postDelayed(new Runnable() { @Override public void run() { LogUtil.e("--------------第一次检测网络---------------"); checkNetwork(); Message msg = new Message(); msg.arg1 = Constant.CHECK_NETWORK_CONNECT_FLAG; mHandler.sendMessage(msg); } }, CHECK_NETWORK_DELAY_TIME); } } return Service.START_REDELIVER_INTENT; } /** * 检测网络是否可用 */ private void checkNetwork() { // 如果网络不可用,开启GPS就没有意义 if (NetWorkUtil.isNetworkAvailable(mContext)) { isOpenNetwork = true; if (GPSUtil.isOPen(mContext) == false) { // 通知用户GPS未开启 mNotificationUtil.sendGPSNotification(); } LogUtil.i("MobileLocatorService start Location Service"); // 开启定位服务 mLocationClient.start(); } else { // 通知用户网络不可用 mNotificationUtil.sendNetworkNotification(); } } /** * 初始化定位服务,配置相应参数 */ private void initLocationService() { mLocationClient = new LocationClient(this.getApplicationContext()); mLocationListener = new MyLocationListener(); mLocationClient.registerLocationListener(mLocationListener); LocationClientOption locationOption = new LocationClientOption(); locationOption.setOpenGps(true); locationOption.setCoorType("bd09ll"); locationOption.disableCache(true); locationOption.setPriority(LocationClientOption.GpsFirst); locationOption.setScanSpan(DELAY_TIME); locationOption.setProdName(this.getString(R.string.loaction_prod_name)); mLocationClient.setLocOption(locationOption); } Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { int result = msg.arg1; switch (result) { case Constant.CHECK_NETWORK_CONNECT_FLAG: // 第一检测网络,直接过了。(已打开) boolean isRun = ServiceUtil.isServiceRun(getApplicationContext(), "com.baidu.location.f"); LogUtil.i("isRun = " + isRun); if (isOpenNetwork && isRun) { LogUtil.i("--------------第一次检测网络,直接过了。(已打开)----------------"); return; } mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { checkNetworkNumber++; LogUtil.i("Timer checkNetworkNumber = " + checkNetworkNumber); checkNetwork(); boolean isRun = ServiceUtil.isServiceRun(getApplicationContext(), "com.baidu.location.f"); if (isOpenNetwork && isRun) { mNotificationUtil.cancelNotification(Constant.NOTIFICATIO_NETWORK_NOT_OPEN); mTimer.cancel(); return; } else { if (checkNetworkNumber == 3) { LogUtil.e("--------------第三次检测网络,还未开启,直接退出应用---------"); // 检查网络,提醒了用户三次依然未开,退出应用。 mNotificationUtil.cancelNotification(Constant.NOTIFICATIO_NETWORK_NOT_OPEN); mNotificationUtil.cancelNotification(Constant.NOTIFICATIO_GPS_NOT_OPEN); mTimer.cancel(); // System.gc(); System.exit(0); } } } }, 0, DELAY_TIME); break; case Constant.UPLOAD_LOACTION_SUCCESS: LogUtil.i("您当前的位置上传服务器成功!"); // Toast.makeText(getApplicationContext(), "您当前的位置上传服务器成功!", Toast.LENGTH_LONG).show(); break; case Constant.LOCATION_NETWORK_EXCEPTION: LogUtil.e("网络异常!请检查您的网络连接。"); // 网络异常,没有成功向服务器发起请求。 // Toast.makeText(getApplicationContext(), "网络异常!请检查您的网络连接。", Toast.LENGTH_LONG).show(); // 通知用户网络不可用 mNotificationUtil.sendNetworkNotification(); break; case Constant.LOCATION_NETWORK_CONNECT_FAIL: LogUtil.e("网络连接失败,请将网络关闭再重新打开试试!"); // 通知用户网络不可用 mNotificationUtil.sendNetworkNotification(); break; case Constant.UPLOAD_LOACTION_FAIL: // Toast.makeText(getApplicationContext(), "您当前的位置上传服务器失败!请查看下你的网络状态。", Toast.LENGTH_LONG).show(); LogUtil.e("您当前的位置上传服务器失败!"); break; default: break; } } }; class MyLocationListener implements BDLocationListener { double longitude; double latitude; @Override public void onReceiveLocation(BDLocation location) { if (location == null) { return; } LogUtil.i("BDLocationListener onReceiveLocation()"); /* location.getLocType()的返回值含义: 61 : GPS定位结果 62 : 扫描整合定位依据失败。此时定位结果无效。 63 : 网络异常,没有成功向服务器发起请求。此时定位结果无效。 65 : 定位缓存的结果。 66 : 离线定位结果。通过requestOfflineLocaiton调用时对应的返回结果 67 : 离线定位失败。通过requestOfflineLocaiton调用时对应的返回结果 68 : 网络连接失败时,查找本地离线定位时对应的返回结果 161: 表示网络定位结果 162~167: 服务端定位失败。*/ int locType = location.getLocType(); longitude = location.getLongitude(); latitude = location.getLatitude(); // TODO 调试使用 StringBuffer sb = new StringBuffer(256); sb.append(" time : "); sb.append(location.getTime()); sb.append("\n error code : "); sb.append(locType); sb.append("\n latitude : "); sb.append(longitude); sb.append("\n longitude : "); sb.append(latitude); LogUtil.i("BDLocationListene " + sb.toString()); if (locType == Constant.LOCATION_GPS || locType == Constant.LOCATION_NETWORK) { // GPS定位结果、网络定位结果 mHandler.post(new Runnable() { @Override public void run() { String userId = "bgao"; int result = send(userId, longitude, latitude); Message msg = new Message(); msg.arg1 = result; mHandler.sendMessage(msg); } }); } else if (locType == Constant.LOCATION_NETWORK_EXCEPTION || locType == Constant.LOCATION_NETWORK_CONNECT_FAIL) { // 网络异常,没有成功向服务器发起请求。 Message msg = new Message(); msg.arg1 = locType; mHandler.sendMessage(msg); } } @Override public void onReceivePoi(BDLocation arg0) { } } /** * 向服务器端当前位置的经纬度 * @param usetId 用户ID * @param longitude 经度值 * @param latitude 纬度值 */ private int send(String usetId, double longitude, double latitude) { StringBuffer params = new StringBuffer(); params.append("event=save"); params.append("¤tPointX="); params.append(longitude); params.append("¤tPointY="); params.append(latitude); params.append("&userId="); params.append(usetId); try { InputStream inputStream = HttpRequester.post(Constant.UPLOAD_GPS_URL, params); if (inputStream != null) { String result = new String(FileUtil.read(inputStream)); String time = (new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")).format(System.currentTimeMillis()); LogUtil.e("网络请求返回的结果:result = " + result + "\t 时间:" + time); if ("Y".equals(result)) { return 1; } else if ("N".equals(result)) { return 0; } else { LogUtil.e("服务器端返回的值与预先商定的不否! "); } } else { LogUtil.e("网络请求成功,但是返回的数据流为NULL"); } } catch (IOException e) { LogUtil.e("IOException 服务器访问失败!"); e.printStackTrace(); return 0; } return 0; } @Override public void onDestroy() { LogUtil.e("---------------MobileLocatorService onDestroy()----------------"); if (mLocationClient != null && mLocationClient.isStarted()) { mLocationClient.stop(); if (mLocationListener != null) { mLocationClient.unRegisterLocationListener(mLocationListener); } } SharedPreferences sp = mContext.getSharedPreferences("MobileLocator", Context.MODE_PRIVATE); String result = sp.getString("instruct", null); LogUtil.i("MobileLocatorService onDestroy() result = " + result); if ("exit".equals(result)) { sp.edit().putString("instruct", "true").commit(); LogUtil.e("---------------MobileLocatorService onDestroy()-----------1-----"); System.exit(0); return; } LogUtil.e("---------------MobileLocatorService onDestroy()---------2-------"); // 销毁时重新启动Service Intent intent = new Intent(ACTION_MOBILE_LOCATOR_SERVICE); intent.putExtra("startingMode", startingMode); this.startService(intent); } }
B、启动时系统发出的广播的接收器类源码:
package com.android.mobile.locator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import com.android.mobile.locator.utils.Constant; import com.android.mobile.locator.utils.LogUtil; /** * 类名:BootBroadcastReceiver * 功能描述:启动时系统发出的广播的接收器 * #<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> * @author android_ls */ public class BootBroadcastReceiver extends BroadcastReceiver { private static final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED"; @Override public void onReceive(Context context, Intent intent) { LogUtil.i("Boot this system , BootBroadcastReceiver onReceive()"); if (intent.getAction().equals(ACTION_BOOT)) { LogUtil.i("BootBroadcastReceiver onReceive(), MobileLocatorService Start"); Intent mIntent = new Intent(MobileLocatorService.ACTION_MOBILE_LOCATOR_SERVICE); mIntent.putExtra("startingMode", Constant.BOOT_START_SERVICE); context.startService(mIntent); } } }
C、关机时系统发出的广播的接收器类源码:
package com.android.mobile.locator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import com.android.mobile.locator.utils.LogUtil; /** * 类名:ShutdownBroadcastReceiver * 功能描述:关机时系统发出的广播的接收器 * @author android_ls */ public class ShutdownBroadcastReceiver extends BroadcastReceiver { private static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; @Override public void onReceive(Context context, Intent intent) { LogUtil.e("Shut down this system, ShutdownBroadcastReceiver onReceive()"); if (intent.getAction().equals(ACTION_SHUTDOWN)) { LogUtil.i("ShutdownBroadcastReceiver onReceive(), MobileLocatorService Stop"); SharedPreferences sp = context.getSharedPreferences("MobileLocator", Context.MODE_PRIVATE); sp.edit().putString("instruct", "exit").commit(); context.stopService(new Intent(MobileLocatorService.ACTION_MOBILE_LOCATOR_SERVICE)); } } }
D、在AndroidManifest.xml文件中的配置:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.easi.mobile.locator" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <!-- android:sharedUserId="android.uid.system" android:killAfterRestore="true" android:process=":remote" android:enabled="true" --> <application android:allowClearUserData="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name="com.android.mobile.locator.MobileLocatorActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:enabled="true" android:name="com.android.mobile.locator.MobileLocatorService" android:process=":remote" > <intent-filter > <action android:name="com.easi.mobile.locator.MobileLocatorService" /> </intent-filter> </service> <service android:enabled="true" android:name="com.baidu.location.f" android:process=":remote" /> <receiver android:name="com.android.mobile.locator.BootBroadcastReceiver" > <intent-filter > <action android:name="android.intent.action.BOOT_COMPLETED" /> <category android:name="android.intent.category.HOME" /> </intent-filter> </receiver> <receiver android:name="com.android.mobile.locator.ShutdownBroadcastReceiver" > <intent-filter > <action android:name="android.intent.action.ACTION_SHUTDOWN" /> <category android:name="android.intent.category.HOME" /> </intent-filter> </receiver> </application> <!-- 授予应用程序访问系统开机事件的权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.READ_LOGS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> </manifest>
前段时间由于忙于公司的项目,所以好久没更新博客了,后面我会继续更新的,谢谢大家的支持!