原创地址:http://www.cnblogs.com/jk1001/archive/2010/07/29/1788106.html

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获取当前位置经纬度(非第三方地图集成)
  2. Android(安卓)通过GPS进行定位
  3. android 网络状态监听
  4. android 获取原生gps坐标
  5. Android(安卓)高德地图 定位+围栏
  6. android 按钮按下效果(文字颜色和按钮同时变化)
  7. android中一些view处理(拖动,定位,显示,图层)
  8. Android之如何判断定位是否开启及定位模式
  9. Android(安卓)软键盘在有scollview,纵向viewpager+recyclview实现

随机推荐

  1. Android ListView里设置默认Item的背景颜
  2. android中的Inflater
  3. JS判断客户端是否是iOS或者Android手机移
  4. android EditText+ListView的组合(类似于A
  5. Android调用js的坑
  6. Manpower代招-高级Android开发工程师@成
  7. android 读取assets指定文件
  8. Android(安卓)Activity之间跳转出现短暂
  9. android关于加载本地html5的问题(解决办
  10. Android 实现仿Window7图片预览窗格效果