Android系列之Wifi定位

BronchoA1还不支持基站和WIFI定位,Android的老版本里是有NetworkLocationProvider的,它实现了基站和WIFI定位,但从android1.5之后就被移除了。本来想在bronchoA1里自己实现NetworkLocationProvider的,但一直没有时间去研究。我知道gears(http://code.google.com/p/gears/)是有提供类似的功能,昨天研究了一下Gears的代码,看能不能移植到android中来

1.下载源代码
[url]svncheckouthttp://gears.googlecode.com/svn/trunk/gears-read-only[/url]
定位相关的源代码在gears/geolocation目录中。

2.关注android平台中的基站位置变化

JAVA类AndroidRadioDataProvider是PhoneStateListener的子类,用来监听Android电话的状态变化。当服务状态、信号强度和基站变化时,

就会用下面代码获取小区信息:

1RadioDataradioData=newRadioData();2GsmCellLocationgsmCellLocation=(GsmCellLocation)cellLocation;3 4//Extractthecellid,LAC,andsignalstrength. 5radioData.cellId=gsmCellLocation.getCid();6radioData.locationAreaCode=gsmCellLocation.getLac();7radioData.signalStrength=signalStrength;8 9//ExtractthehomeMCCandhomeMNC. 10Stringoperator=telephonyManager.getSimOperator();11radioData.setMobileCodes(operator,true);12 13if(serviceState!=null){14//Extractthecarriername. 15radioData.carrierName=serviceState.getOperatorAlphaLong();16 17//ExtracttheMCCandMNC. 18operator=serviceState.getOperatorNumeric();19radioData.setMobileCodes(operator,false);20}21 22//Finallygettheradiotype. 23inttype=telephonyManager.getNetworkType();24if(type==TelephonyManager.NETWORK_TYPE_UMTS){25radioData.radioType=RADIO_TYPE_WCDMA;26}elseif(type==TelephonyManager.NETWORK_TYPE_GPRS27||type==TelephonyManager.NETWORK_TYPE_EDGE){28radioData.radioType=RADIO_TYPE_GSM;29}30



然后再调用用C代码实现的onUpdateAvailable函数。
2.Native函数onUpdateAvailable是在radio_data_provider_android.cc里实现的。
声明Native函数

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

JNI调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。

代码
voidAndroidRadioDataProvider::OnUpdateAvailable(JNIEnv*env,jclasscls,jobjectradio_data,jlongself){assert(radio_data);assert(self);AndroidRadioDataProvider*self_ptr=reinterpret_cast<AndroidRadioDataProvider*>(self);RadioDatanew_radio_data;if(InitFromJavaRadioData(env,radio_data,&new_radio_data)){self_ptr->NewRadioDataAvailable(&new_radio_data);}}

先判断基站信息有没有变化,如果有变化则通知相关的监听者。

1voidAndroidRadioDataProvider::NewRadioDataAvailable(2RadioData*new_radio_data){3boolis_update_available=false;4data_mutex_.Lock();5if(new_radio_data&&!radio_data_.Matches(*new_radio_data)){6radio_data_=*new_radio_data;7is_update_available=true;8}9//Avoidholdingthemutexlockedwhilenotifyingobservers. 10data_mutex_.Unlock();11 12if(is_update_available){13NotifyListeners();14}15}

接下来的过程,在基站定位和WIFI定位是一样的,后面我们再来介绍。下面我们先看WIFI定位

3.关注android平台中的WIFI变化。

JAVA类AndroidWifiDataProvider扩展了BroadcastReceiver类,它关注WIFI扫描结果:

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

当收到WIFI扫描结果后,调用Native函数onUpdateAvailable,并把WIFI的扫描结果传递过去。

1publicvoidonReceive(Contextcontext,Intentintent){2if(intent.getAction().equals(3mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)){4if(Config.LOGV){5Log.v(TAG,"Wifiscanresulstavailable");6}7onUpdateAvailable(mWifiManager.getScanResults(),mNativeObject);8}9}

Native函数onUpdateAvailable是在wifi_data_provider_android.cc里实现的。

1JNINativeMethodAndroidWifiDataProvider::native_methods_[]={2{"onUpdateAvailable",3"(Ljava/util/List;J)V",4reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)5},6};7 8voidAndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*/*env*/,9jclass/*cls*/,10jobjectwifi_data,11jlongself){12assert(self);13AndroidWifiDataProvider*self_ptr=14reinterpret_cast<AndroidWifiDataProvider*>(self);15WifiDatanew_wifi_data;16if(wifi_data){17InitFromJava(wifi_data,&new_wifi_data);18}19//Wenotifyregardlessofwhethernew_wifi_dataisempty20//ornot.Thearbitratorwilldecidewhattodowithanempty21//WifiDataobject. 22self_ptr->NewWifiDataAvailable(&new_wifi_data);23}24 25voidAndroidWifiDataProvider::NewWifiDataAvailable(WifiData*new_wifi_data){26assert(supported_);27assert(new_wifi_data);28boolis_update_available=false;29data_mutex_.Lock();30is_update_available=wifi_data_.DiffersSignificantly(*new_wifi_data);31wifi_data_=*new_wifi_data;32//Avoidholdingthemutexlockedwhilenotifyingobservers. 33data_mutex_.Unlock();34 35if(is_update_available){36is_first_scan_complete_=true;37NotifyListeners();38}39 40#ifUSING_CCTESTS41//ThisisneededforrunningtheWiFitestontheemulator.42//Seewifi_data_provider_android.hfordetails. 43if(!first_callback_made_&&wifi_data_.access_point_data.empty()){44first_callback_made_=true;45NotifyListeners();46}47#endif 48}49 50JNINativeMethodAndroidWifiDataProvider::native_methods_[]={51{"onUpdateAvailable",52"(Ljava/util/List;J)V",53reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)54},55};56 57voidAndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*/*env*/,58jclass/*cls*/,59jobjectwifi_data,60jlongself){61assert(self);62AndroidWifiDataProvider*self_ptr=63reinterpret_cast<AndroidWifiDataProvider*>(self);64WifiDatanew_wifi_data;65if(wifi_data){66InitFromJava(wifi_data,&new_wifi_data);67}68//Wenotifyregardlessofwhethernew_wifi_dataisempty69//ornot.Thearbitratorwilldecidewhattodowithanempty70//WifiDataobject. 71self_ptr->NewWifiDataAvailable(&new_wifi_data);72}73 74voidAndroidWifiDataProvider::NewWifiDataAvailable(WifiData*new_wifi_data){75assert(supported_);76assert(new_wifi_data);77boolis_update_available=false;78data_mutex_.Lock();79is_update_available=wifi_data_.DiffersSignificantly(*new_wifi_data);80wifi_data_=*new_wifi_data;81//Avoidholdingthemutexlockedwhilenotifyingobservers. 82data_mutex_.Unlock();83 84if(is_update_available){85is_first_scan_complete_=true;86NotifyListeners();87}88 89#ifUSING_CCTESTS90//ThisisneededforrunningtheWiFitestontheemulator.91//Seewifi_data_provider_android.hfordetails. 92if(!first_callback_made_&&wifi_data_.access_point_data.empty()){93first_callback_made_=true;94NotifyListeners();95}96#endif 97}98

从以上代码可以看出,WIFI定位和基站定位的逻辑差不多,只是前者获取的WIFI的扫描结果,而后者获取的基站信息。

后面代码的基本上就统一起来了,接下来我们继续看。

5.把变化(WIFI/基站)通知给相应的监听者。

1AndroidWifiDataProvider和AndroidRadioDataProvider都是继承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。2 3staticDeviceDataProvider*Register(ListenerInterface*listener){4MutexLockmutex(&instance_mutex_);5if(!instance_){6instance_=newDeviceDataProvider();7}8assert(instance_);9instance_->Ref();10instance_->AddListener(listener);11returninstance_;12}13 14staticboolUnregister(ListenerInterface*listener){15MutexLockmutex(&instance_mutex_);16if(!instance_->RemoveListener(listener)){17returnfalse;18}19if(instance_->Unref()){20deleteinstance_;21instance_=NULL;22}23returntrue;24}25



6.谁在监听变化(WIFI/基站)

NetworkLocationProvider在监听变化(WIFI/基站):

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


当有变化时,会调用函数DeviceDataUpdateAvailable:

代码
//DeviceDataProviderInterface::ListenerInterfaceimplementation. voidNetworkLocationProvider::DeviceDataUpdateAvailable(RadioDataProvider*provider){MutexLocklock(&data_mutex_);assert(provider==radio_data_provider_);is_radio_data_complete_=radio_data_provider_->GetData(&radio_data_);DeviceDataUpdateAvailableImpl();}voidNetworkLocationProvider::DeviceDataUpdateAvailable(WifiDataProvider*provider){assert(provider==wifi_data_provider_);MutexLocklock(&data_mutex_);is_wifi_data_complete_=wifi_data_provider_->GetData(&wifi_data_);DeviceDataUpdateAvailableImpl();}


无论是WIFI还是基站变化,最后都会调用DeviceDataUpdateAvailableImpl:

1voidNetworkLocationProvider::DeviceDataUpdateAvailableImpl(){2timestamp_=GetCurrentTimeMillis();3 4//Signaltotheworkerthreadthatnewdataisavailable. 5is_new_data_available_=true;6thread_notification_event_.Signal();7}



这里面只是发了一个signal,通知另外一个线程去处理。

7.谁在等待thread_notification_event_

线程函数NetworkLocationProvider::Run在一个循环中等待thread_notification_event,当有变化(WIFI/基站)时,就准备请求服务器查询位置。

先等待:

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

准备请求:

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



再来看MakeRequest的实现:

先从cache中查找位置:

1constPosition*cached_position=2position_cache_->FindPosition(radio_data_,wifi_data_);3data_mutex_.Unlock();4if(cached_position){5assert(cached_position->IsGoodFix());6//Recordthepositionandupdateitstimestamp. 7position_mutex_.Lock();8position_=*cached_position;9position_.timestamp=timestamp_;10position_mutex_.Unlock();11 12//Letlistenersknowthatwenowhaveapositionavailable. 13UpdateListeners();14returntrue;15}



如果找不到,再做实际的请求

1returnrequest_->MakeRequest(access_token,2radio_data_,3wifi_data_,4request_address_,5address_language_,6kBadLatLng,//Wedon'thaveapositiontopass 7kBadLatLng,//totheserver. 8timestamp_);

7.客户端协议包装

前面的request_是NetworkLocationRequest实例,先看MakeRequest的实现:

先对参数进行打包:

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


通知负责收发的线程

1thread_event_.Signal();

8.负责收发的线程

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


把打包好的数据通过HTTP请求,发送给服务器

1scoped_refptr<BlobInterface>payload_data;2boolresult=HttpPost(url_.c_str(),3false,//Notcapturing,sofollowredirects 4NULL,//reason_header_value 5HttpConstants::kMimeApplicationJson,//Content-Type 6NULL,//mod_since_date 7NULL,//required_cookie 8true,//disable_browser_cookies 9post_body_.get(),10&payload,11&payload_data,12NULL,//was_redirected 13NULL,//full_redirect_url 14NULL);//error_message 15 16MutexLocklock(&is_processing_response_mutex_);17//is_aborted_maybetrueevenifHttpPostsucceeded. 18if(is_aborted_){19LOG(("NetworkLocationRequest::Run():HttpPostrequestwascancelled.\n"));20return;21}22if(listener_){23Positionposition;24std::stringresponse_body;25if(result){26//IfHttpPostsucceeded,payload_dataisguaranteedtobenon-NULL. 27assert(payload_data.get());28if(!payload_data->Length()||29!BlobToString(payload_data.get(),&response_body)){30LOG(("NetworkLocationRequest::Run():Failedtogetresponsebody.\n"));31}32}

解析出位置信息

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

通知位置信息的监听者

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

有人会问,请求是发哪个服务器的?当然是google了,缺省的URL是:

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

回过头来,我们再总结一下:

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

2.NetworkLocationProvider和NetworkLocationRequest各有一个线程来异步处理请求。

3.这里的NetworkLocationProvider与android中的NetworkLocationProvider并不是同一个东西,这里是给gears用的,要在android的googlemap中使用,还得包装成android中的NetworkLocationProvider的接口。

4.WIFI和基站定位与平台无关,只要你能拿到WIFI扫描结果或基站信息,而且能访问google的定位服务器,不管你是Android平台,WindowsMobile平台还是传统的featurephone,你都可以实现WIFI和基站定位。

附:WIFI和基站定位原理

无论是WIFI的接入点,还是移动网络的基站设备,它们的位置基本上都是固定的。设备端(如手机)可以找到它们的ID,现在的问题就是如何通过这些ID找到对应的位置。网上的流行的说法是开车把所有每个位置都跑一遍,把这些设备的位置与GPS测试的位置关联起来。

更多相关文章

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

随机推荐

  1. Study on Android【六】--消息机制,异步和
  2. 【Android】利用安卓的数据接口、多媒体
  3. Android重温--触屏事件
  4. Android_个人中心_修改个人信息
  5. android 防止对控件的重复点击和同时点击
  6. Android(安卓)UI性能优化—过度绘制篇
  7. Android——UI篇:android视频压缩:兼容7.0
  8. Android(安卓)studio怎么删除依赖包? And
  9. 腾讯微博获得oauth_verifier后跳转失败
  10. Android中添加商品的购物车