Android系列之Wifi定位
2013-01-30 15:22
363 查看
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调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。
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:
无论是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.下载源代码
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调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。
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:
无论是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测试的位置关联起来。
相关文章推荐
- Android系列之Wifi定位
- (转帖)Android系列之Wifi定位
- Android系列之Wifi定位
- Android系列之Wifi定位
- Android系列之Wifi定位
- Android系列之Wifi定位
- 羊皮书APP(Android版)开发系列(九)通过Wifi调试程序
- 在Android里完美实现基站和WIFI定位
- Appium+Genymotion+robotframework+python系列二:Android元素定位
- Appium python自动化测试系列之Android UIAutomator终极定位(七)
- Android填坑系列:在小米系列等机型上放开定位权限后的定位请求弹框示例
- 凯立德超快定位,Android平板变导航之Wifi篇
- Android官方开发文档Training系列课程中文版:连接无线设备之通过WIFI创建P2P连接
- android gps wifi 基站 定位集合
- Android附近基站+Wifi+IP+GPS多渠道定位方案
- 在Android里完美实现基站和WIFI定位
- 在Android里完美实现基站和WIFI定位
- 在Android里完美实现基站和WIFI定位
- Android 使用基站、wifi实现定位
- android gps wifi 基站定位