Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android系列之Wifi定位

Android系列之Wifi定位

編輯:Android開發實例

Broncho A1還不支持基站和WIFI定位,Android的老版本裡是有NetworkLocationProvider的,它實現了基站和WIFI定位,但從 android 1.5之後就被移除了。本來想在broncho A1裡自己實現NetworkLocationProvider的,但一直沒有時間去研究。我知道 gears(http://code.google.com/p/gears/)是有提供類似的功能,昨天研究了一下Gears的代碼,看能不能移植到 android中來

1.下載源代碼
[url]svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only[/url]
定位相關的源代碼在gears/geolocation目錄中。

2.關注android平台中的基站位置變化

JAVA類AndroidRadioDataProvider是 PhoneStateListener的子類,用來監聽Android電話的狀態變化。當服務狀態、信號強度和基站變化時,

就會用下面代碼獲取小區信息:

 

 1 RadioData radioData = new RadioData();   
2 GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
3
4 // Extract the cell id, LAC, and signal strength.
5 radioData.cellId = gsmCellLocation.getCid();
6 radioData.locationAreaCode = gsmCellLocation.getLac();
7 radioData.signalStrength = signalStrength;
8
9 // Extract the home MCC and home MNC.
10 String operator = telephonyManager.getSimOperator();
11 radioData.setMobileCodes(operator, true);
12
13 if (serviceState != null) {
14 // Extract the carrier name.
15 radioData.carrierName = serviceState.getOperatorAlphaLong();
16
17 // Extract the MCC and MNC.
18 operator = serviceState.getOperatorNumeric();
19 radioData.setMobileCodes(operator, false);
20 }
21
22 // Finally get the radio type.
23 int type = telephonyManager.getNetworkType();
24 if (type == TelephonyManager.NETWORK_TYPE_UMTS) {
25 radioData.radioType = RADIO_TYPE_WCDMA;
26 } else if (type == TelephonyManager.NETWORK_TYPE_GPRS
27 || type == TelephonyManager.NETWORK_TYPE_EDGE) {
28 radioData.radioType = RADIO_TYPE_GSM;
29 }
30



然後再調用用C代碼實現的onUpdateAvailable函數。
2.Native函數onUpdateAvailable是在 radio_data_provider_android.cc裡實現的。
聲明Native函數

 

1 JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {   
2 {"onUpdateAvailable",
3 "(L" GEARS_JAVA_PACKAGE "/AndroidRadioDataProvider$RadioData;J)V",
4 reinterpret_cast<void*>(AndroidRadioDataProvider::OnUpdateAvailable)
5 },
6 };
7

 

 

JNI調用好像只能調用靜態成員函數,把對象本身用一個參數傳進來,然後再調用對象的成員函數。

 

代碼
void AndroidRadioDataProvider::OnUpdateAvailable(JNIEnv* env,   
jclass cls,
jobject radio_data,
jlong self) {
assert(radio_data);
assert(self);
AndroidRadioDataProvider *self_ptr =
reinterpret_cast<AndroidRadioDataProvider*>(self);
RadioData new_radio_data;
if (InitFromJavaRadioData(env, radio_data, &new_radio_data)) {
self_ptr->NewRadioDataAvailable(&new_radio_data);
}
}

先判斷基站信息有沒有變化,如果有變化則通知相關的監聽者。

 

 1 void AndroidRadioDataProvider::NewRadioDataAvailable(   
2 RadioData* new_radio_data) {
3 bool is_update_available = false;
4 data_mutex_.Lock();
5 if (new_radio_data && !radio_data_.Matches(*new_radio_data)) {
6 radio_data_ = *new_radio_data;
7 is_update_available = true;
8 }
9 // Avoid holding the mutex locked while notifying observers.
10 data_mutex_.Unlock();
11
12 if (is_update_available) {
13 NotifyListeners();
14 }
15 }

 

 

接下來的過程,在基站定位和WIFI定位是一樣的,後面我們再來介紹。下面我們先看 WIFI定位

3.關注android平台中的WIFI變化。

JAVA類AndroidWifiDataProvider擴展了 BroadcastReceiver類,它關注WIFI掃描結果:

 

1 IntentFilter filter = new IntentFilter();   
2 filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
3 mContext.registerReceiver(this, filter, null, handler);

當收到WIFI掃描結果後,調用Native函數 onUpdateAvailable,並把WIFI的掃描結果傳遞過去。

 

1 public void onReceive(Context context, Intent intent) {   
2 if (intent.getAction().equals(
3 mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
4 if (Config.LOGV) {
5 Log.v(TAG, "Wifi scan resulst available");
6 }
7 onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);
8 }
9 }

Native函數onUpdateAvailable是在 wifi_data_provider_android.cc裡實現的。

 

1 JNINativeMethod AndroidWifiDataProvider::native_methods_[] = { 
2 {"onUpdateAvailable",
3 "(Ljava/util/List;J)V",
4 reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
5 },
6 };
7
8 void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /* env */,
9 jclass /* cls */,
10 jobject wifi_data,
11 jlong self) {
12 assert(self);
13 AndroidWifiDataProvider *self_ptr =
14 reinterpret_cast<AndroidWifiDataProvider*>(self);
15 WifiData new_wifi_data;
16 if (wifi_data) {
17 InitFromJava(wifi_data, &new_wifi_data);
18 }
19 // We notify regardless of whether new_wifi_data is empty
20 // or not. The arbitrator will decide what to do with an empty
21 // WifiData object.
22 self_ptr->NewWifiDataAvailable(&new_wifi_data);
23 }
24
25 void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
26 assert(supported_);
27 assert(new_wifi_data);
28 bool is_update_available = false;
29 data_mutex_.Lock();
30 is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);
31 wifi_data_ = *new_wifi_data;
32 // Avoid holding the mutex locked while notifying observers.
33 data_mutex_.Unlock();
34
35 if (is_update_available) {
36 is_first_scan_complete_ = true;
37 NotifyListeners();
38 }
39
40 #if USING_CCTESTS
41 // This is needed for running the WiFi test on the emulator.
42 // See wifi_data_provider_android.h for details.
43 if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {
44 first_callback_made_ = true;
45 NotifyListeners();
46 }
47 #endif
48 }
49
50 JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {
51 {"onUpdateAvailable",
52 "(Ljava/util/List;J)V",
53 reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
54 },
55 };
56
57 void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /* env */,
58 jclass /* cls */,
59 jobject wifi_data,
60 jlong self) {
61 assert(self);
62 AndroidWifiDataProvider *self_ptr =
63 reinterpret_cast<AndroidWifiDataProvider*>(self);
64 WifiData new_wifi_data;
65 if (wifi_data) {
66 InitFromJava(wifi_data, &new_wifi_data);
67 }
68 // We notify regardless of whether new_wifi_data is empty
69 // or not. The arbitrator will decide what to do with an empty
70 // WifiData object.
71 self_ptr->NewWifiDataAvailable(&new_wifi_data);
72 }
73
74 void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
75 assert(supported_);
76 assert(new_wifi_data);
77 bool is_update_available = false;
78 data_mutex_.Lock();
79 is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);
80 wifi_data_ = *new_wifi_data;
81 // Avoid holding the mutex locked while notifying observers.
82 data_mutex_.Unlock();
83
84 if (is_update_available) {
85 is_first_scan_complete_ = true;
86 NotifyListeners();
87 }
88
89 #if USING_CCTESTS
90 // This is needed for running the WiFi test on the emulator.
91 // See wifi_data_provider_android.h for details.
92 if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {
93 first_callback_made_ = true;
94 NotifyListeners();
95 }
96 #endif
97 }
98

 從以上代碼可以看出,WIFI定位和基站定位的邏輯差不多,只是前者獲取的WIFI的掃描結果,而後者獲取的基站信息。

後面代碼的基本上就統一起來了,接下來我們繼續看。

5.把變化(WIFI/基站)通知給相應的監聽者。

 1 AndroidWifiDataProvider和AndroidRadioDataProvider都是繼承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。   
2
3 static DeviceDataProvider *Register(ListenerInterface *listener) {
4 MutexLock mutex(&instance_mutex_);
5 if (!instance_) {
6 instance_ = new DeviceDataProvider();
7 }
8 assert(instance_);
9 instance_->Ref();
10 instance_->AddListener(listener);
11 return instance_;
12 }
13
14 static bool Unregister(ListenerInterface *listener) {
15 MutexLock mutex(&instance_mutex_);
16 if (!instance_->RemoveListener(listener)) {
17 return false;
18 }
19 if (instance_->Unref()) {
20 delete instance_;
21 instance_ = NULL;
22 }
23 return true;
24 }
25

 

 



6.誰在監聽變化(WIFI/基站)

NetworkLocationProvider在監聽變化(WIFI/基站): 

1 radio_data_provider_ = RadioDataProvider::Register(this);   
2 wifi_data_provider_ = WifiDataProvider::Register(this);


當有變化時,會調用函數DeviceDataUpdateAvailable:

 

代碼
// DeviceDataProviderInterface::ListenerInterface implementation.
void NetworkLocationProvider::DeviceDataUpdateAvailable(
RadioDataProvider *provider) {
MutexLock lock(&data_mutex_);
assert(provider == radio_data_provider_);
is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_);

DeviceDataUpdateAvailableImpl();
}

void NetworkLocationProvider::DeviceDataUpdateAvailable(
WifiDataProvider *provider) {
assert(provider == wifi_data_provider_);
MutexLock lock(&data_mutex_);
is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);

DeviceDataUpdateAvailableImpl();
}

 

 


 無論是WIFI還是基站變化,最後都會調用 DeviceDataUpdateAvailableImpl:

 

1 void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {
2 timestamp_ = GetCurrentTimeMillis();
3
4 // Signal to the worker thread that new data is available.
5 is_new_data_available_ = true;
6 thread_notification_event_.Signal();
7 }

 

 

 

這裡面只是發了一個signal,通知另外一個線程去處理。

7.誰在等待thread_notification_event_

線程函數NetworkLocationProvider::Run在一個循環中等待 thread_notification_event,當有變化(WIFI/基站)時,就准備請求服務器查詢位置。

先等待:

 

1 if (remaining_time > 0) {
2 thread_notification_event_.WaitWithTimeout(
3 static_cast<int>(remaining_time));
4 } else {
5 thread_notification_event_.Wait();
6 }

准備請求:

 

1 if (make_request) {   
2 MakeRequest();
3 remaining_time = 1;
4 }



再來看MakeRequest的實現:

先從cache中查找位置:

 

 1  const Position *cached_position =
2 position_cache_->FindPosition(radio_data_, wifi_data_);
3 data_mutex_.Unlock();
4 if (cached_position) {
5 assert(cached_position->IsGoodFix());
6 // Record the position and update its timestamp.
7 position_mutex_.Lock();
8 position_ = *cached_position;
9 position_.timestamp = timestamp_;
10 position_mutex_.Unlock();
11
12 // Let listeners know that we now have a position available.
13 UpdateListeners();
14 return true;
15 }

 

 



如果找不到,再做實際的請求

 

 

1   return request_->MakeRequest(access_token,
2 radio_data_,
3 wifi_data_,
4 request_address_,
5 address_language_,
6 kBadLatLng, // We don't have a position to pass
7 kBadLatLng, // to the server.
8 timestamp_);

 

 

7.客戶端協議包裝

前面的request_是NetworkLocationRequest實例,先看 MakeRequest的實現:

先對參數進行打包:

 

 

 

1   if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,
2 request_address, address_language, latitude, longitude,
3 is_reverse_geocode_, &post_body_)) {
4 return false;
5 }


通知負責收發的線程 

 

1 thread_event_.Signal();

 

 

8.負責收發的線程

 

 1 void NetworkLocationRequest::Run() {
2 while (true) {
3 thread_event_.Wait();
4 if (is_shutting_down_) {
5 break;
6 }
7 MakeRequestImpl();
8 }
9 }
10
11 void NetworkLocationRequest::MakeRequestImpl() {
12 WebCacheDB::PayloadInfo payload;


把打包好的數據通過HTTP請求,發送給服務器
 

 1  scoped_refptr<BlobInterface> payload_data;
2 bool result = HttpPost(url_.c_str(),
3 false, // Not capturing, so follow redirects
4 NULL, // reason_header_value
5 HttpConstants::kMimeApplicationJson, // Content-Type
6 NULL, // mod_since_date
7 NULL, // required_cookie
8 true, // disable_browser_cookies
9 post_body_.get(),
10 &payload,
11 &payload_data,
12 NULL, // was_redirected
13 NULL, // full_redirect_url
14 NULL); // error_message
15
16 MutexLock lock(&is_processing_response_mutex_);
17 // is_aborted_ may be true even if HttpPost succeeded.
18 if (is_aborted_) {
19 LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n"));
20 return;
21 }
22 if (listener_) {
23 Position position;
24 std::string response_body;
25 if (result) {
26 // If HttpPost succeeded, payload_data is guaranteed to be non-NULL.
27 assert(payload_data.get());
28 if (!payload_data->Length() ||
29 !BlobToString(payload_data.get(), &response_body)) {
30 LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n"));
31 }
32 }

 

 

解析出位置信息 

 

1 std::string16 access_token;
2 GetLocationFromResponse(result, payload.status_code, response_body,
3 timestamp_, url_, is_reverse_geocode_,
4 &position, &access_token);

通知位置信息的監聽者

 

1  bool server_error =
2 !result || (payload.status_code >= 500 && payload.status_code < 600);
3 listener_->LocationResponseAvailable(position, server_error, access_token);
4 }
5 }

 

 

有人會問,請求是發哪個服務器的?當然是google了,缺省的URL是:

 

 

1 static const char16 *kDefaultLocationProviderUrl =
2 STRING16(L"https://www.google.com/loc/json");

 

 

 回過頭來,我們再總結一下:

1.WIFI和基站定位過程如下:

2.NetworkLocationProvider和 NetworkLocationRequest各有一個線程來異步處理請求。

3.這裡的NetworkLocationProvider與android中的 NetworkLocationProvider並不是同一個東西,這裡是給gears用的,要在android的google map中使用,還得包裝成android中的NetworkLocationProvider的接口。

4.WIFI和基站定位與平台無關,只要你能拿到WIFI掃描結果或基站信息,而且能訪問google的定位服務器,不管你是Android平台,Windows Mobile平台還是傳統的feature phone,你都可以實現WIFI和基站定位。

附: WIFI和基站定位原理

無論是WIFI的接入點,還是移動網絡的基站設備,它們的位置基本上都是固定的。設備端(如手機)可以找到它們的ID,現在的問題就是如何通過這些ID找到對應的位置。網上的流行的說法是開車把所有每個位置都跑一遍,把這些設備的位置與 GPS測試的位置關聯起來。  

 

轉自:http://www.cnblogs.com/jk1001/archive/2010/07/29/1788106.html

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved