设为首页 收藏本站
查看: 1162|回复: 0

[经验分享] (转帖)Android系列之Wifi定位

[复制链接]

尚未签到

发表于 2015-10-1 07:22:25 | 显示全部楼层 |阅读模式
  原创地址: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.下载源代码
svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only
定位相关的源代码在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调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。
  


DSC0000.gif DSC0001.gif 代码



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和基站定位过程如下:
DSC0002.jpg
  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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-121074-1-1.html 上篇帖子: Android教程:wifi热点问题 下篇帖子: WinCE 5.0 WIFI 无线网卡的配置和建立连接(转)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表