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测试的位置关联起来。

更多相关文章

  1. Android(安卓)-Recovery
  2. Android中自定义View仿京东秒杀倒计时
  3. Android监听手机网络变化
  4. Android(安卓)5.0有哪些变化
  5. 根据文字的多少,自动适应变化的表格...
  6. Android(安卓)基于GeolocationAPI的基站定位
  7. android 开源项目(城市定位)
  8. Android(安卓)百度地图定位指针(系列2)
  9. Android(安卓)高德地图点击地图获取经纬度

随机推荐

  1. Android中文API(98)―― ContextMenu.Conte
  2. Android(安卓)bitmap.recycle()导致tryin
  3. Android布局优化ViewStub源码分析
  4. 关于下载最新版本Android(安卓)Studio却
  5. android 使用xutils3 https详解
  6. android相关知识简介
  7. Android(安卓)Installation error: INSTA
  8. Android摄像头采集Demo
  9. Linux/Android启动之Machine-Init函数
  10. Android(安卓)从外部网页拉起跳转到App