轉載請標明出處:http://blog.csdn.net/android_ls/article/details/10179013html
1、定位模塊的需求:咱們想知道使用咱們應用的用戶的大概位置,每隔五分鐘上傳一次用戶當前所在地的經緯度值到服務器端。 java
2、需求分析android
A、UML流程圖以下:git
B、定位服務,功能具體分析:緩存
啓動方式:手動、開機自啓動。
關閉方式:用戶在設置裏強制中止應用、關閉手機。(用戶使用其餘軟件殺死掉咱們的服務,用戶從新啓動應用服務纔會開啓。)
一、開機自啓動服務,等1分鐘後開始檢測網絡狀態和GPS是否開啓,並經過通知欄提醒用戶。(未開啓時,提醒三次,5分鐘提醒一次)
二、直接啓動應用,當即開始檢測網絡狀態和GPS是否開啓,並經過彈Dialog提示用戶。若用戶不肯意開啓網絡,即網絡不可用時,直接退出應用。
三、用戶在設置-->應用程序-->正在運行的服務裏面手動中止掉服務後,服務自動重啓。
四、網絡檢測可用,開始檢測GPS。用戶不開啓GPS時,使用基站定位(WLAN、3G/2G)。
五、網絡檢測可用,啓動百度地圖定位服務,每隔五分鐘確認一次當前我所在的位置,並將經緯度值上傳服務器端。
六、網絡檢測可用,可是在發送定位數據時,網絡斷開了,以Toast形式提醒用戶。
七、網絡檢測可用,可是在定位過程當中,網絡斷開了,而且目前打開的不是咱們的應用(也就是說服務在後臺運行),以通知的形式提醒用戶。
八、服務運行過程當中,意外中止了。當用戶重啓應用後,服務從新啓動。
九、添加了開機自啓動後,檢測網絡和經過通知欄提醒用戶當前的網絡、GPS狀態。
十、服務運行過程當中,網絡檢測返回的標識的處理。服務器
3、編碼實現:網絡
完整源碼下載地址:http://download.csdn.net/detail/android_ls/5993623,核心類的源碼以下,app
A、服務類的源碼以下:ide
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>
前段時間因爲忙於公司的項目,因此很久沒更新博客了,後面我會繼續更新的,謝謝你們的支持!