Broncho A1还不支持基站和WIFI定位,Android的老版本里是有NetworkLocationProvider的,它实现了基站和WIFI定位,但从 android 1.5之后就被移除了。本来想在broncho A1里自己实现NetworkLocationProvider的,但一直没有时间去研究。我知道 gears(http://code.google.com/p/gears/)是有提供类似的功能,昨天研究了一下Gears的代码,看能不能移植到 android中来 1.下载源代码 svn checkout [url]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调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。 ![]() ![]() 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: ![]() ![]() 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和基站定位。 |