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电话的状态变化。当服务状态、信号强度和基站变化时,就会用下面代码获取小区信息:
RadioData radioData = new RadioData();
GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
// Extract the cell id, LAC, and signal strength.
radioData.cellId = gsmCellLocation.getCid();
radioData.locationAreaCode = gsmCellLocation.getLac();
radioData.signalStrength = signalStrength;
// Extract the home MCC and home MNC.
String operator = telephonyManager.getSimOperator();
radioData.setMobileCodes(operator, true);
if (serviceState != null) {
// Extract the carrier name.
radioData.carrierName = serviceState.getOperatorAlphaLong();
// Extract the MCC and MNC.
operator = serviceState.getOperatorNumeric();
radioData.setMobileCodes(operator, false);
}
// Finally get the radio type.
int type = telephonyManager.getNetworkType();
if (type == TelephonyManager.NETWORK_TYPE_UMTS) {
radioData.radioType = RADIO_TYPE_WCDMA;
} else if (type == TelephonyManager.NETWORK_TYPE_GPRS
|| type == TelephonyManager.NETWORK_TYPE_EDGE) {
radioData.radioType = RADIO_TYPE_GSM;
}
然后调用用C代码实现的onUpdateAvailable函数。
2.Native函数onUpdateAvailable是在 radio_data_provider_android.cc里实现的。
声明Native函数
JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {
{"onUpdateAvailable",
"(L" GEARS_JAVA_PACKAGE "/AndroidRadioDataProvider$RadioData;J)V",
reinterpret_cast<void*>(AndroidRadioDataProvider::OnUpdateAvailable)
},
};
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);
}
}
先判断基站信息有没有变化,如果有变化则通知相关的监听者。
void AndroidRadioDataProvider::NewRadioDataAvailable(
RadioData* new_radio_data) {
bool is_update_available = false;
data_mutex_.Lock();
if (new_radio_data && !radio_data_.Matches(*new_radio_data)) {
radio_data_ = *new_radio_data;
is_update_available = true;
}
// Avoid holding the mutex locked while notifying observers.
data_mutex_.Unlock();
if (is_update_available) {
NotifyListeners();
}
}
接下来的过程,基站定位和WIFI定位是一样的,后面我们再来介绍。下面我们先看 WIFI定位。
3.关注android平台中的WIFI变化。
JAVA类AndroidWifiDataProvider扩展了 BroadcastReceiver类,它关注WIFI扫描结果:
IntentFilter filter = new IntentFilter();
filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mContext.registerReceiver(this, filter, null, handler);
当收到WIFI扫描结果后,调用Native函数 onUpdateAvailable,并把WIFI的扫描结果传递过去。
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
if (Config.LOGV) {
Log.v(TAG, "Wifi scan resulst available");
}
onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);
}
}
4.Native函数onUpdateAvailable是在 wifi_data_provider_android.cc里实现的。
JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {
{"onUpdateAvailable",
"(Ljava/util/List;J)V",
reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
},
};
void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /* env */,
jclass /* cls */,
jobject wifi_data,
jlong self) {
assert(self);
AndroidWifiDataProvider *self_ptr =
reinterpret_cast<AndroidWifiDataProvider*>(self);
WifiData new_wifi_data;
if (wifi_data) {
InitFromJava(wifi_data, &new_wifi_data);
}
// We notify regardless of whether new_wifi_data is empty
// or not. The arbitrator will decide what to do with an empty
// WifiData object.
self_ptr->NewWifiDataAvailable(&new_wifi_data);
}
void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
assert(supported_);
assert(new_wifi_data);
bool is_update_available = false;
data_mutex_.Lock();
is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);
wifi_data_ = *new_wifi_data;
// Avoid holding the mutex locked while notifying observers.
data_mutex_.Unlock();
if (is_update_available) {
is_first_scan_complete_ = true;
NotifyListeners();
}
#if USING_CCTESTS
// This is needed for running the WiFi test on the emulator.
// See wifi_data_provider_android.h for details.
if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {
first_callback_made_ = true;
NotifyListeners();
}
#endif
}
从以上代码可以看出,WIFI定位和基站定位的逻辑差不多,只是前者获取的WIFI的扫描结果,而后者获取的基站信息。后面代码的基本上就统一起来了,接下来我们继续看。
5.把变化(WIFI/基站)通知给相应的监听者。
AndroidWifiDataProvider和AndroidRadioDataProvider都是继承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。
static DeviceDataProvider *Register(ListenerInterface *listener) {
MutexLock mutex(&instance_mutex_);
if (!instance_) {
instance_ = new DeviceDataProvider();
}
assert(instance_);
instance_->Ref();
instance_->AddListener(listener);
return instance_;
}
static bool Unregister(ListenerInterface *listener) {
MutexLock mutex(&instance_mutex_);
if (!instance_->RemoveListener(listener)) {
return false;
}
if (instance_->Unref()) {
delete instance_;
instance_ = NULL;
}
return true;
}
6.谁在监听变化(WIFI/基站)
NetworkLocationProvider在监听变化(WIFI/基站):
radio_data_provider_ = RadioDataProvider::Register(this);
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:
void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {
timestamp_ = GetCurrentTimeMillis();
// Signal to the worker thread that new data is available.
is_new_data_available_ = true;
thread_notification_event_.Signal();
}
这里面只是发了一个signal,通知另外一个线程去处理。
7.谁在等待thread_notification_event_
线程函数NetworkLocationProvider::Run在一个循环中等待 thread_notification_event,当有变化(WIFI/基站)时,就准备请求服务器查询位置。
先等待:
if (remaining_time > 0) {
thread_notification_event_.WaitWithTimeout(
static_cast<int>(remaining_time));
} else {
thread_notification_event_.Wait();
}
准备请求:
if (make_request) {
MakeRequest();
remaining_time = 1;
}
再来看MakeRequest的实现:
先从cache中查找位置:
const Position *cached_position =
position_cache_->FindPosition(radio_data_, wifi_data_);
data_mutex_.Unlock();
if (cached_position) {
assert(cached_position->IsGoodFix());
// Record the position and update its timestamp.
position_mutex_.Lock();
position_ = *cached_position;
position_.timestamp = timestamp_;
position_mutex_.Unlock();
// Let listeners know that we now have a position available.
UpdateListeners();
return true;
}
如果找不到,再做实际的请求
return request_->MakeRequest(access_token,
radio_data_,
wifi_data_,
request_address_,
address_language_,
kBadLatLng, // We don't have a position to pass
kBadLatLng, // to the server.
timestamp_);
7.客户端协议包装
前面的request_是NetworkLocationRequest实例,先看 MakeRequest的实现:
先对参数进行打包:
if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,
request_address, address_language, latitude, longitude,
is_reverse_geocode_, &post_body_)) {
return false;
}
通知负责收发的线程
thread_event_.Signal();
8.负责收发的线程
void NetworkLocationRequest::Run() {
while (true) {
thread_event_.Wait();
if (is_shutting_down_) {
break;
}
MakeRequestImpl();
}
}
void NetworkLocationRequest::MakeRequestImpl() {
WebCacheDB::PayloadInfo payload;
把打包好的数据通过HTTP请求,发送给服务器
scoped_refptr<BlobInterface> payload_data;
bool result = HttpPost(url_.c_str(),
false, // Not capturing, so follow redirects
NULL, // reason_header_value
HttpConstants::kMimeApplicationJson, // Content-Type
NULL, // mod_since_date
NULL, // required_cookie
true, // disable_browser_cookies
post_body_.get(),
&payload,
&payload_data,
NULL, // was_redirected
NULL, // full_redirect_url
NULL); // error_message
MutexLock lock(&is_processing_response_mutex_);
// is_aborted_ may be true even if HttpPost succeeded.
if (is_aborted_) {
LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n"));
return;
}
if (listener_) {
Position position;
std::string response_body;
if (result) {
// If HttpPost succeeded, payload_data is guaranteed to be non-NULL.
assert(payload_data.get());
if (!payload_data->Length() ||
!BlobToString(payload_data.get(), &response_body)) {
LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n"));
}
}
解析出位置信息
std::string16 access_token;
GetLocationFromResponse(result, payload.status_code, response_body,
timestamp_, url_, is_reverse_geocode_,
&position, &access_token);
通知位置信息的监听者。
bool server_error =
!result || (payload.status_code >= 500 && payload.status_code < 600);
listener_->LocationResponseAvailable(position, server_error, access_token);
}
}
有人会问,请求是发哪个服务器的?当然是google了,缺省的URL是:
static const char16 *kDefaultLocationProviderUrl =
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测试的位置关联起来。
参考资料:
Gears:[url] http://gears.googlecode.com/[/url]
Google 地图 API:[url] http://code.google.com/intl/zh-CN/apis/maps/documentation/reference.html[/url]
wifi定位技术:[url] http://blog.csdn.net/NewMap/archive/2009/03/17/3999337.aspx
[/url]
分享到:
相关推荐
要研究和移植Gears的地理位置功能到Android,首先需要从指定的URL下载源代码。在Gears的源码中,定位相关的代码位于`gears/geolocation`目录。这里重点关注`AndroidRadioDataProvider`类,它是`PhoneStateListener`...
在运行gears演示之前,可能需要进行编译和链接步骤,将源代码转换为可执行文件。这涉及到对Makefile的配置,指定编译器、链接器选项,以及依赖的库路径。在QNX系统上,可能需要使用QNX Momentics IDE或命令行工具来...
### Google Gears 入门教程知识点详解 #### 一、Google Gears 概述 - **定义**:Google Gears 是由 Google 推出的一款开源浏览器插件,旨在通过为 Web 应用程序提供一系列 JavaScript API 来增强其功能与性能。 - ...
最后,从标签"源码"我们可以推测,"uabTest"项目可能提供了源代码供其他开发者参考学习,这对于开源社区来说是一份宝贵的资源。通过阅读和研究源码,开发者可以了解如何在实际项目中运用Google Gears,以及如何解决...
2. PCHome_download.html - 这可能是来自 PCHome(一个知名的科技资讯网站)的下载页面源代码或缓存副本,用户可能通过该页面了解到 Google Gears 的下载信息和使用指南。 3. GoogleGearsforWindows__PCHome软件介绍...
4. **广泛的兼容性**:Gears支持包括Windows、Windows Mobile、Mac(Firefox 和 Safari)、Linux 和 Android在内的多种操作系统和浏览器环境,这极大地增强了其适用范围。 #### 安装与体系结构 - **安装**:Gears...
Gears 2 windows7 主题
Google Gears做为2007年的一个创新产品,令人失望的是目前只有极少数的应用程序使用了Google的这项技术,Google Gears仍然是一个早期的产品,大多数文章提到Google Gears只是说其提供离线应用,甚至Google也这么说,...
Gears鼠标鼠标鼠标鼠标鼠标鼠标鼠标鼠标鼠标鼠标鼠标
这将自动下载并解压mesa-demos的源代码,其中包括mesa-demos-8.4.0目录,其中包含了各种演示程序的源代码。这些源代码可以帮助开发者了解如何组织一个完整的OpenGL ES应用程序,包括设置上下文、加载着色器、处理...
5. **跨平台兼容性**:Gears支持多种操作系统和浏览器,如Windows、Windows Mobile、Mac(Firefox和Safari)、Linux以及Android,确保了广泛的应用场景。 Gears的安装和应用架构设计: - **安装过程**:Gears作为...
gears
technology_of_gears
gearsInMesh(g1,g2) create gearInMesh object with gears g1 and g2 in mesh Input arguments g1, g2 - gear object. Module of the gears must be equal Properties G1, G2 - gear objects a - center distance ...
在本文中,我们将深入探讨基于Laravel框架的"Gears"库,这是一款专为开发者设计的设置和用户首选项管理工具。Laravel是PHP社区中最受欢迎的Web应用程序框架之一,以其优雅的语法、强大的功能和丰富的生态系统而闻名...
【Google Gears 开发者指南】是一份详细阐述Google Gears技术的文档,该文档主要针对软件开发人员,旨在帮助他们理解和使用Google Gears来构建离线Web应用程序。Google Gears是一个于2007年发布的实验性软件,它允许...
Browser部分基于WebKit,不支持插件,内置了Google Gears(无源代码)。在2.x版本中,增加了对HTML5特性的支持,如本地存储。Dalvik VM的诞生是出于规避Sun公司授权的商业考虑,它的大小只有500KB,特别适合移动设备...