从这篇开始,我会逐步介绍Moblin2 SDK中引入的API与中间件,希望能对大家有所帮助。
在详细介绍之前,先讲讲GeoClue的来龙去脉。大家知道,原先在应用程序中要获取设备的当前位置,最常见的做法是通过GPS定位,程序员往往需要直接访问串口读取GPS设备发来的NMEA帧,然后通过分析获得需要的位置数据。这种做法有2个缺点:第一,工作量大;第二,GPS为某个进程独占,其他应用如果也有类似需求就只能说抱歉了。在这种情况下,引入了GPS Daemon,就是Daemon独占了GPS设备,分析NMEA帧获取位置信息,并把位置信息提供给应用程序。这样GPS Daemon就可以同时为多个进程服务,并且大大减轻了程序员的工作量,不用再为编写分析NMEA数据的代码而头大了。目前Linux上比较流行的GPS Daemon有gpsd和gypsy。不过使用GPS定位有一定的局限性,首先带有GPS的设备并不普及,其次,室内无法收到GPS信号。随着技术的发展,越来越多的定位方式出现在世人面前,如IP地址定位,wifi定位,基站定位等,这些新出现的定位技术对传统的定位方式起到了补充的作用。于是程序员想在程序中采用多种定位技术结合,自动选取最优定位技术来获取位置信息。问题又来了:又要自己写代码分析位置了,程序里要加不少代码来判断最优,如果需要支持新的定位技术怎么办?在这种情况下,GeoClue出现了。
GeoClue是架构在D-BUS上的一套提供位置相关信息的中间件,遵循LGPL。这里的位置相关信息包括了位置、地址和运动速度等等。GeoClue下有若干Provider提供位置相关信息。同一个Provider可能提供若干种不同类型的信息,比如gypsy可以提供位置信息与速度;不同的Provider也可能提供相同类型的信息,比如gypsy与hostip都可以提供位置信息,所不同的是前者的信息是通过GPS获得,而后者则是通过互联网上IP转经纬度的服务而获得。GeoClue提供了2套API供应用程序使用,一套是C语言API,另一套是D-BUS API,用户可以根据实际情况选择。
GeoClue API通过Provider Interface让用户访问Provider,目前支持的Interface有:
下面贴一段示例代码:
#include <geoclue/geoclue-position.h>
/ * device name or bluetooth address * /
#define GPS_DEVICE_NAME "00:02:76:C5:81:BF"
static void
position_changed (GeocluePosition *position,
GeocluePositionFields fields,
int timestamp,
double latitude,
double longitude,
double altitude,
GeoclueAccuracy *accuracy,
gpointer userdata)
{
g_print ("Position changed:/n");
if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
g_print ("/t%f, %f/n/n", latitude, longitude);
} else {
g_print ("/tLatitude and longitude not available./n/n");
}
}
int main()
{
GMainLoop *loop;
GHashTable *options;
GeocluePosition *pos;
GError *error = NULL;
g_type_init ();
/ * Create the position object * /
pos = geoclue_position_new ("org.freedesktop.Geoclue.Providers.Gypsy",
"/org/freedesktop/Geoclue/Providers/Gypsy");
/ * Set GPS device name option for Gypsy * /
options = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (options, "org.freedesktop.Geoclue.GPSDevice", GPS_DEVICE_NAME);
if (!geoclue_provider_set_options (GEOCLUE_PROVIDER (pos), options, &error)) {
g_printerr ("Error setting options: %s/n", error->message);
g_error_free (error);
g_hash_table_destroy (options);
g_object_unref (pos);
return 1;
}
g_hash_table_destroy (options);
/ * connect to signal * /
g_signal_connect (G_OBJECT (pos), "position-changed",
G_CALLBACK (position_changed), NULL);
g_print ("Waiting for position change signals.../n");
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_main_loop_unref (loop);
g_object_unref (pos);
return 0;
}
通过这段代码我们发现,还是需要显式制定要访问的Provider。可对于应用程序的程序员来说,他们更关心的是与自身应用、业务相关的内容,他们所要的是一个位置信息,而并不需要太关心数据的具体来源。有没有更好的方法?GeoClue的开发团队也想到了这一点,他们在API中提供了称为Master Provider的功能,我的理解是Provider总管J。程序员只需在代码中告诉总管所需要的数据精度和数据大致来源,总管就能自动的返回一个最优的Provider来。我们再来看一段示例代码:
#include <geoclue/geoclue-master.h>
static void
position_changed (GeocluePosition *position,
GeocluePositionFields fields,
int timestamp,
double latitude,
double longitude,
double altitude,
GeoclueAccuracy *accuracy,
gpointer userdata)
{
g_print ("Position changed:/n");
if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
g_print ("/t%f, %f/n/n", latitude, longitude);
} else {
g_print ("/tLatitude and longitude not available./n/n");
}
}
int main()
{
GMainLoop *loop;
GeoclueMaster *master;
GeoclueMasterClient *client;
GeocluePosition *pos;
GeocluePositionFields fields;
double lat, lon;
GError *error = NULL;
g_type_init ();
/ * create a MasterClient using Master * /
master = geoclue_master_get_default ();
client = geoclue_master_create_client (master, NULL, &error);
g_object_unref (master);
if (!client) {
g_printerr ("Error creating GeoclueMasterClient: %s/n", error->message);
g_error_free (error);
return 1;
}
/ * Set our requirements: We want at least city level accuracy, require signals,
and allow the use of network (but not e.g. GPS) * /
if (!geoclue_master_client_set_requirements (client,
GEOCLUE_ACCURACY_LEVEL_LOCALITY,
0, TRUE,
GEOCLUE_RESOURCE_NETWORK,
&error)){
g_printerr ("set_requirements failed: %s", error->message);
g_error_free (error);
g_object_unref (client);
return 1;
}
/ * Get a Position object * /
pos = geoclue_master_client_create_position (client, NULL);
if (!pos) {
g_printerr ("Failed to get a position object");
g_object_unref (client);
return 1;
}
/ * call get_position. We do not know which provider actually provides
the answer (although we could find out using MasterClient API) * /
fields = geoclue_position_get_position (pos, NULL,
&lat, &lon, NULL,
NULL, &error);
if (error) {
g_printerr ("Error in geoclue_position_get_position: %s./n", error->message);
g_error_free (error);
error = NULL;
} else {
if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
g_print ("We're at %.3f, %.3f./n", lat, lon);
}
}
g_signal_connect (G_OBJECT (pos), "position-changed",
G_CALLBACK (position_changed), NULL);
g_print ("Waiting for position change signals.../n");
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_main_loop_unref (loop);
g_object_unref (pos);
g_object_unref (client);
return 0;
}
无论是直接使用Provider Interface还是Master Provider,使用GeoClue开发时需要注意以下两点:
1. 不能在程序中假设函数调用能够立即返回或者返回的数据均是有效数据。比如GPS初次定位需要一定时间;网络上的数据请求可能有延迟等等。
2. 尽量使用Signal或者异步调用。同步调用会阻塞线程。
需要了解更多有关GeoClue的内容,可以访问http://folks.o-hand.com/jku/geoclue-docs/index.html。