2012年2月29日星期三

在新的Android工程中调用已经编译好的so库文件代码

依旧是Android,NDK,JNI相关的问题。
  照着ndk帮助的说明,运行<ndk>/samples目录下的hello-jni工程后,你一定想知道如何在自己的项目里调用别人已经编译好的.so库文件。于是新建一个Android工程,将hello-jni例子中ndk-build后的结果:libs文件夹(包括其下所有内容)拷贝到新建工程的根目录下,仿照hello-jni的代码,调用native方法:
   1: package com.my.SoTest;
   2:  
   3: import android.app.Activity;
   4: import android.os.Bundle;
   5: import android.widget.TextView;
   6:  
   7: public class SoTestActivity extends Activity {
   8:     /** Called when the activity is first created. */
   9:     @Override
  10:     public void onCreate(Bundle savedInstanceState) {
  11:         super.onCreate(savedInstanceState);
  12:         
  13:         TextView tv=new TextView(this);
  14:         tv.setText(stringFromJNI());
  15:         setContentView(tv);
  16:     }
  17:     public native String stringFromJNI();
  18:     static {
  19:         System.loadLibrary("hello-jni");
  20:     }
  21: }

  运行后,logcat记录以下错误:


No implementation found for native Lcom/my/SoTest/SoTestActivity;.stringFromJNI ()Ljava/lang/String;
...
java.lang.UnsatisfiedLinkError: stringFromJNI

  在hello-jni的代码注释里明确说明,UnsatisfiedLinkError错误是由于native代码中没有该方法的实现而引起的,但stringFromJNI这个方法,在c代码中是有的;前一个错误就比较明确了,是在Lcom/my/SoTest/SoTestActivity中去找这个方法实现的,打开hello-jni.c文件看看该函数头,其中有这样的字样:Java_com_example_hellojni_HelloJni_stringFromJNI,而com.example.hellojni恰好是例子工程的package name,HelloJni则是class的名称。也就是说,我们.so中函数声明涉及到的package name和class name与调用它的package name和class name不符。google一下印证了这个猜测,但提供现成解决办法的不多,总不能自己工程的package name必须和.so里的一样吧。
  搜索看到了一篇NDK的教程,Using NDK to Call C code from Android Apps(需翻墙),里面写明了在一个普通Android工程中,如何从零开始组装自己的native代码。大致的步骤是,首先写一个java类,里面声明一些需要实现native方法(函数签名即可),然后用javah命令来生成对应的header文件,里面的内容是自动生成的native函数声明,然后照着这个声明实现各个函数,最后编译,用最初的java类调用。
  受此启发,我们在拿到别人已经编译好的.so文件后,可首先新建一个java类,所在package的名称和class名称都与.so文件中函数签名提示的一致,在这个类中加入native方法的声明。这样在别处就可以用这个wrapper调用so库中的函数了。

2012年2月27日星期一

在Android上利用NDK编译并使用Spatialite库(Windows环境)

  Sqlite数据库因其体积便携(基于单个文件,跨平台),功能完整(类似RDBMS)受到非常广泛的欢迎,在iOS,Android平台上都提供原生的支持。Spatialite是Sqlite的一个空间扩展,根据官方介绍,Spatialite之于Sqlite,相当于PostGIS之于PostgreSQL。
  Spatialite扩展提供了一系列用于空间操作的SQL函数,例如基础的空间查询,缓冲区生成等,也有高级的类似ArcObjects中ITopologicalOperator,IRelationalOperator等接口的功能。
  在Android平台上使用Spatialite资料比较少,主要是google上的Spatialite for Android和github上的android-spatialite两个工程。前者带有完整的编译步骤,我尝试了这个,其中遇到了些问题,记录下来,方便需要用到的朋友。后者工程似乎带有编译好的libjsqlite.so库,可直接使用。
  我机器的环境是Windows 7 64bit,已经搭建好了Android Eclipse开发环境。由于Spatialite及Sqlite是C/C++的native语言编写,而Android的默认开发语言是Java,所以在Android上编译Spatialite必须用到google提供的NDK(Native Development Kit)环境。NDK环境的搭建比较简单,按照google的文档进行即可:

  1. 下载NDK压缩包(android-ndk-r7b-windows.zip)。我下载的是最新的android-ndk-r7b版本。将其解压到任意目录,下文以<ndk>指代。注意:由于后面cygwin中配置环境变量时不支持带空格的路径,所以建议将其解压到某个盘符的根目录下,比如d:\android-ndk-r7b\;
  2. 设置Windows的环境变量。主要是将NDK的路径添加到PATH中去,方便在任何位置调用ndk-build命令。详细操作不再赘述。此步骤只是为cmd提供了便利;
  3. 由于NDK需求中说明,必须用到GNU Make命令,所以在Windows环境上还需要提前安装Cygwin。这是在Windows上模拟Linux环境的工具。安装步骤参考此贴:Windows环境下Android NDK环境搭建。注意在cygwin的linux环境下,配置$NDK环境变量,方便在任何地方调用ndk-build编译命令。我在安装Cygwin时,只额外勾选了Devel库。

  完成这个步骤后,可尝试编译运行NDK中自带的hello-jni例子,验证NDK编译环境已经搭好。具体步骤参见google的文档。可以发现,native代码需要放在与src同级的jni目录下,该目录下必须至少有Android.mk文件,向NDK系统描述源文件;编译时需要在工程的根目录下运<ndk>/ndk-build命令;利用NDK编译后,Android工程文件夹中会多出libs目录,里面会生成lib<something>.so共享库,这就是我们要用到类库,该文件也会一并打包到apk文件中去。更多的ndk知识请参考<ndk>/documentation.html文件,非常详尽。注:JNI相当于.net平台的p/invoke。
  接下来就要按照Spatialite for Android上的提示,在cygwin环境中编译spatialite库了。

  1. 在windows下利用svn工具对整个project checkout(这次作者用的是Spatialite 3.0.1,PROJ 4.7.0,geos 3.2.2几个库)。会发现有两个工程spatialite-android和spatialite-android-test。我们主要对spatialite-android工程进行编译;
  2. 由于spatialite用到了geos和proj两个库,所以首先需要为该工程下载,配置两个库。具体步骤如下,在cygwin环境中执行:
       1: $ cd jni
       2: $ wget http://download.osgeo.org/geos/geos-3.2.2.tar.bz2
       3: $ tar xvjf geos-3.2.2.tar.bz2
       4: $ cd geos-3.2.2
       5: $ ./configure --build=x86_64-pc-linux-gnu --host=arm-linux-eabi
       6: $ cd ..
       7: $ wget ftp://ftp.remotesensing.org/proj/proj-4.7.0.tar.gz
       8: $ tar xvzf proj-4.7.0.tar.gz
       9: $ cd proj-4.7.0
      10: $ ./configure --build=x86_64-pc-linux-gnu --host=arm-linux-eabi
      11: $ touch src/empty.cpp
      12: $ cd ..

  3. 按照作者的提示,下来就可以直接对工程进行ndk-build了(注:此时仍然在spatialite-android/jni目录下,而非spatialite-android目录)。如果你这么做了,会出现一个错误:


       1: ...
       2: Compile thumb  : proj <= rtodms.c
       3: Compile thumb  : proj <= vector1.c
       4: StaticLibrary  : libproj.a
       5: Compile++ thumb  : geos <= geos_c.cpp
       6: In file included from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/geom/Coordinate.h:20,
       7:                  from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/g                                                                                                                eom/Envelope.h:26,
       8:                  from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/i                                                                                                                ndex/strtree/STRtree.h:27,
       9:                  from D:/spatialite-android/jni/geos-3.2.2/capi/geos_c.cpp:19:
      10: D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/platform.h:29:20: error                                                                                                                : ieeefp.h: No such file or directory
      11: /cygdrive/d/android-ndk-r7b/build/core/build-binary.mk:243: recipe for target `/                                                                                                                cygdrive/d/spatialite-android/obj/local/armeabi/objs-debug/geos/geos-3.2.2/capi/                                                                                                                geos_c.o' failed
      12: make: *** [/cygdrive/d/spatialite-android/obj/local/armeabi/objs-debug/geos/geos                                                                                                                -3.2.2/capi/geos_c.o] Error 1
    image
    提示ieeefp.h: No such file or directory。要修正这个错误,你需要在spatialite-android/jni/geos-3.2.2/source/headers/geos/platform.h文件中大约15行的位置,找到“#define HAVE_IEEEFP_H 1”这句话,并在其下面添加一行“#undef HAVE_IEEEFP_H”,取消对HAVE_IEEEFP_H的定义。保存该文件;

  4. 再次ndk-build,这回发现上面的错误消失了。但又产生了新的错误:


       1: ...
       2: Compile++ thumb  : geos <= InteriorPointLine.cpp
       3: Compile++ thumb  : geos <= InteriorPointPoint.cpp
       4: Compile++ thumb  : geos <= LineIntersector.cpp
       5: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In st                                                                                                                atic member function 'static double geos::algorithm::LineIntersector::interpolat                                                                                                                eZ(const geos::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geo                                                                                                                m::Coordinate&)':
       6: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:224: e                                                                                                                rror: expected unqualified-id before '(' token
       7: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:232: e                                                                                                                rror: expected unqualified-id before '(' token
       8: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In me                                                                                                                mber function 'void geos::algorithm::LineIntersector::computeIntersection(const                                                                                                                 geos::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geom::Coordi                                                                                                                nate&)':
       9: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:304: e                                                                                                                rror: expected unqualified-id before '(' token
      10: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:306: e                                                                                                                rror: expected unqualified-id before '(' token
      11: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In me                                                                                                                mber function 'int geos::algorithm::LineIntersector::computeIntersect(const geos                                                                                                                ::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geom::Coordinate                                                                                                                &, const geos::geom::Coordinate&)':
      12: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:426: e                                                                                                                rror: expected unqualified-id before '(' token
    image在很多位置都提示 error: expected unqualified-id before '(' token,google一下这个错误,发现大概是由于在头文件中对某个函数重复声明冲突引起的。于是打开spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp文件查看,发现基本都与一个名为“ISNAN”的函数有关。在Windows下搜索包含此字符串的文件,又来到了刚才修改过的platform.h文件中,发现这个文件中有与此函数相关的语句。为了解决此问题,尝试对此文件进行再次修改。在大约25行的位置,可以找到“/* #undef HAVE_ISNAN */”这句话,在这句话下面添加一句“#define HAVE_ISNAN 1”。这样做的目的是改变该文件后续的一些编译处理。

  5. 最后再次ndk-build,成功输出libjsqlite.so库。
    image

  现在我们在eclipse中运行spatialite-android这个工程(需要将test-2.3.sqlite数据库adb push到模拟器的sd卡上),就可以看到预期的结果了。
image  关于如何利用java语言调用spatialite的功能,可以详细参考这个工程中jsqlite包下的封装代码。至此我们就完成了在Android平台上使用spatialite库的准备工作。
  此次编译中遇到的问题可参考:http://code.google.com/p/spatialite-android/issues/detail?id=3

2012年2月25日星期六

心力

  心力,可以理解为和通常所讲的“体力”相对而言。是指一个人心理的力量,精神上的能量,或者意念。当然你不可能用意念杀死别人,但你可以用心力拯救你自己。
  从前有一片茫茫大海,比地球上所有大陆都变成海洋后的海洋还要大。这片海面上漂浮着唯一一个木环,直径只有杯口大小;海里面有一只乌龟,双目失明,它一直在水中游动,一万年才浮到水面上呼吸一次。
  我们每个人的生命是有限的,但内心是永恒的。今生今世只不过是内心灵魂的一段短暂旅程,现在的身体只是它寄存的一种形式。上辈子你有可能是只猪,是只考拉,甚至阿凡达,下辈子你有可能是格桑花,是面包树。这些表现形式都不真正属于你,生不带来死不带去,只有内心才是真正的你。但这辈子你能获得这个人身,几率和上面的盲龟浮出海面时,头恰好能钻进木环的几率是一样的。之所以能这么幸运,是前几百世修来的福气。
  每做一件好事或者一件坏事,都是你自己的“业”,好的业力多了,就会有好报。不管你在哪一世,万般带不走,唯有业随身。我们活着,除了要积攒业力,最重要的就是锻炼心力,使自己变得正直,善良,坚韧不拔,温柔如水。
  路上碰到瘸腿的乞丐了,不要鄙视,给些零钱,他们或许就能多吃一碗救命的米饭;拥挤的地铁上碰到不讲卫生的农民工了,不要移动脚步躲开,他们和你一样有父母有儿女,他们盖起的高楼能让你们安居乐业,而你们做出的价值千万的系统也许没有一点实用价值,只是在浪费纳税人的血汗钱,为腐败提供了温床。
  锻炼心力是需要决心和勇气的,但可以从一点一滴做起,比如早晨不赖床,或者开始学一些对人生有意义的东西。最好的办法就是挫折和困难。菩萨和天使不总是带给你好处,还可能是挫折和困难,越是逆境,就越要珍惜。因为在你得到这个磨练机会的时候,别人可能还在把时间挥霍在享乐上。享乐的时间和心力的积累通常是成反比的。
  有一些人等意识到,可能也就心有余,力不足了。与各位共勉。

2011年12月19日星期一

ArcGIS移动客户端中可以自动离线的底图图层

  我们都知道,在使用ArcGIS移动客户端(已更名为ArcGIS Runtime SDK for iOS/Windows Phone/Android)API进行开发时,一般对于底图数据,需要用ArcGISTiledMapServiceLayer这个类来加载,指向一个在线的缓存地图服务。而对于移动GIS应用,通常我们又有非常强烈的离线需求。目前来说,ArcGIS移动客户端底图离线有两种实现办法:自定义图层加载离线数据或等待新版本api来加载10.1的TilePackage底图离线包(iOS API已经实现)。
  这次我们来讨论另一种可以让底图离线的办法。在手机上使用过离线地图程序的朋友都知道,它们一般都会提供自动离线的功能,即当我们连接到互联网,浏览在线地图时,程序会自动将浏览过的数据缓存到本地,以便下次没有网络环境时可离线使用。新版本的Goolge Maps移动版,Android上的RMaps,OruxMaps等程序都有类似的功能。
  那么如何为ArcGIS的移动程序实现类似的功能呢?因为这个图层既要能够加载在线的地图服务,又要能够自动离线,所以我们选择创建一个派生自ArcGISTiledMapServiceLayer的类。当有网络连接时,我们浏览地图的过程中,将切片自动存储到本地,没有网络连接时就可以浏览这些切片了。而且可以给用户提供若干选项,比如是否允许自动下载缓存切片,是否允许自动更新已有的缓存切片等。
  请注意本文讨论内容与之前讨论的自定义图层加载离线数据不同,前者具有自动下载缓存切片的功能,下载后没有网络连接的情况下会自动变成离线图层加载离线数据,有网络连接的情况下可当做正常的在线图层使用;后者只能当做离线图层使用,不能自动下载缓存切片。
  这里我以Windows Phone为例,写好了一个扩展类:OfflineableTiledMapServiceLayer(下载地址见最后)。下面对它进行一些说明。
功能:
  OfflineableTiledMapServiceLayer是一个继承自ArcGISTiledMapServiceLayer的自定义类,在线浏览地图的过程中,会自动将缓存切片保存在Sqlite数据库文件中(存储在应用程序的IsolatedStorage空间内),无需人工干预;这样在没有网络连接的情况下,就自动加载先前保存过的切片,作为离线图层使用(程序代码无需做任何修改)。

如何使用:
  与ArcGISTiledMapServiceLayer用法一致,只需额外设置一些参数即可。
<esri:Map x:Name="map1">
    <my:OfflineableTiledMapServiceLayer Url="http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer" EnableOffline="True"  SaveOfflineTiles="True" SaveTilesMode="SaveOnly"
DeleteSavedOfflineTiles="False" LoadOfflineTileFirst="True" />
</esri:Map>

工作流程:
  除了ArcGISTiledMapServiceLayer已有的功能外,OfflineableTiledMapServiceLayer可在浏览地图的过程中,自动将缓存切片保存到Sqlite数据库中。当下次加载该服务时(以URL地址识别),就可从离线的数据库中加载切片数据(没有网络连接的情况下)。
  OfflineableTiledMapServiceLayer目前有以下5个属性可设置:

  • EnableOffline: 默认为True。当设置为True时,OfflineableTiledMapServiceLayer具有自动离线的能力;当设置成False时,OfflineableTiledMapServiceLayer完全和ArcGISTiledMapServiceLayer一样。
  • SaveOfflineTiles: 默认为True。当设置为True时,OfflineableTiledMapServiceLayer会将浏览过的缓存切片保存到本地的Sqlite数据库中(存储在应用程序的IsolatedStorage空间);当设置为False时,OfflineableTiledMapServiceLayer不会保存任何缓存切片。只有当LoadOfflineTileFirst==false时才会生效。
  • LoadOfflineTileFirst: 默认为False。当设置为True时,OfflineableTiledMapServiceLayer会优先从本地Sqlite数据库中加载缓存切片(即使有Internet连接),如果处于离线状态,则会直接从本地Sqlite数据库中加载缓存切片(如果之前没有保存过任何切片,则会抛出异常);当设置为False时,则会优先从在线服务中加载缓存切片,如果处于离线状态,则只能从本地Sqlite中加载数据。
  • SaveTilesMode: 默认为'SaveOnly'。当设置为'SaveOnly'时,OfflineableTiledMapServiceLayer只会存储新的缓存切片(Sqlite数据库中没有的);当设置为'SaveOrUpdate'时,OfflineableTiledMapServiceLayer会存储新的缓存切片,并且更新已有的缓存切片。当在线的缓存地图服务内容更新时,此选项比较有用。只有当LoadOfflineTileFirst==false && SaveOfflineTiles==true时有效。
  • DeleteSavedOfflineTiles: 默认为False。当设置成True时,会首先删除Sqlite数据库中已有的本图层数据,之后重新初始化本图层。只有当EnableOffline==True时有效。


Sqlite文件结构:
  Sqlite在移动设备上具有广泛的应用基础,iOS和Android对其均提供原生支持。OfflineableTiledMapServiceLayer使用Sqlite Client for Windows Phone来读写Sqlite数据库。可以使用Windows Phone 7 Isolated Storage Explorer或者Isolated Storage Explorer Tool将Sqlite文件从Windows Phone的IsolatedStorage中导出,以供其它移动程序使用,比如ArcGIS Runtime SDK for iOS/Android。
  OfflineableTiledMapServiceLayer创建的Sqlite数据库名称为"OfflineTiles.db",它的内容由一张或多张表组成。

  • 'MapServices'表:此表是OfflineableTiledMapServiceLayer必须使用的。具有四个字段:'url'(text), 'spatialreference'(text), 'fullextent'(text), 'tileinfo'(text)。存储在Sqlite中不同的OfflineableTiledMapServiceLayer由其Url属性区分(假设不同的Url代表不同的缓存地图服务)。另外三个字段分别以JSON格式存储了缓存服务对应的信息,这些信息是在离线状态下初始化图层所需要的。创建该表的SQL语句: CREATE TABLE "MapServices" ("url" TEXT PRIMARY KEY  NOT NULL  UNIQUE , "spatialreference" TEXT NOT NULL , "fullextent" TEXT NOT NULL , "tileinfo" TEXT NOT NULL )
  • 其它表:如果Sqlite中存储过任何OfflineableTiledMapServiceLayer缓存图片,则除了上述MapServices表外还会有其它表。其余每张表以该缓存地图服务的Url字符串命名。这些表包含有4个字段:'level'(integer), 'row'(integer), 'column'(integer), 'tile'(blob)。一目了然。创建该表的SQL语句:CREATE TABLE "HereIsYourServiceURL" ("level" INTEGER NOT NULL , "row" INTEGER NOT NULL , "column" INTEGER NOT NULL , "tile" BLOB NOT NULL ), 创建索引的SQL语句: CREATE UNIQUE INDEX 'idx_ HereIsYourServiceURL ' ON ' HereIsYourServiceURL ' ('level' ASC, 'row' ASC, 'column' ASC)


OfflineableTiledMapServiceLayer的下载地址(包括源码,示例程序,示例Sqlite离线文件):http://www.arcgis.com/home/item.html?id=d2b40d7f553947a2b575556b057f5dcf

2011年11月2日星期三

解读ArcGIS Runtime SDKs

  本文试图解读新的ArcGIS Runtime SDKs及其本质,与ArcGIS移动SDK,for iOS/Windows Phone/Android,之间的关系,以及这三种移动SDK后续发展的一些猜想。
  ArcGIS Runtime SDKs是随ArcGIS 10.1 beta一起发布的一套横跨桌面和移动端的,跨平台,轻量级的GIS开发SDK的总称。
image

  从上图中我们可以看出,ArcGIS Runtime SDKs家族包括了以下内容:

  • ArcGIS Runtime SDK for Android
  • ArcGIS Runtime SDK for iOS
  • ArcGIS Runtime SDK for Windows Phone
  • ArcGIS Runtime SDK for Windows Mobile
  • ArcGIS Runtime SDK for Java
  • ArcGIS Runtime SDK for Qt
  • ArcGIS Rutnime SDK for WPF

  之所以说ArcGIS Runtime SDKs横跨桌面和移动端,是因为它既包含了iOS/Windows Phone/Android等移动平台的开发SDK,也包含了可以开发传统桌面程序(C/S程序)的WPF、Java、QT等SDK;而后三种SDK则可开发出Windows和Linux平台下的具有丰富交互效果和良好体验的应用程序。其实ArcGIS Runtime还包括了一些现成的应用程序,比如iOS/Windows Phone/Android各自市场上都能下载到的“ArcGIS”程序,ArcGIS Mobile中的“ArcGIS”程序等。
  说起轻量级,首先要看看ArcGIS 10.1产品架构的一些变化。

image

  ArcGIS 10.1中,产品的划分更加明确和简单。主要分为四个部分,桌面GIS(传统的ArcMap,ArcGlobe等),ServerGIS(全新架构的ArcGIS Server),轻量级GIS(ArcGIS Explorer,Runtime SDKs及其对应的应用程序)和ArcGIS Online。前三部分都是围绕ArcGIS Online这个云GIS平台的,在不同程度上都与ArcGIS Online有交互,或可将数据直接部署上去,或作为它的客户端(云+端)。而轻量级GIS就是为了能够在任何地点,任何平台,任何设备上访问云平台提供的GIS能力。
  ArcGIS Runtime SDKs正是在这种背景下诞生的。其实它也分为狭义和广义上的两种解释。狭义的ArcGIS Runtime SDK是指桌面上的WPF,Java和Qt,它们的消息早在半年前就已经流出。它们的出现,是为了逐步替代强大而相对臃肿的ArcGIS Engine这个产品。做过Engine开发的朋友都知道,即使是最简单的显示地图的需求,理论上都必须在客户机上安装ArcGIS Engine Runtime这个运行时(注意不是ArcGIS Runtime),安装包通常400m左右。而利用新的ArcGIS Runtime SDK for WPF/Java/Qt开发出的程序,完全是绿色程序,不需要在客户机上安装任何部件(.Net Framework和JRE不计)即可运行,因为所有的依赖库直接和程序拷贝在一起即可。如果你喜欢ArcGIS Runtime SDK开发的程序的部署过程——拷贝到u盘里/插入目标计算机/运行,那么你一定也喜欢它的卸载过程——关闭程序/拔掉u盘。
  广义上的ArcGIS Runtime SDKs是上述列表中,诸多SDKs的统称。除了包含狭义的ArcGIS Runtime SDK,可以看到加入了移动端部分:ArcGIS Runtime SDK for iOS/Windows Phone/Android/Windows Mobile,而它们其实是新瓶装旧酒,分别对应以前的ArcGIS SDK for iOS/Windows Phone/Android和ArcGIS Mobile,只是换了产品名称而已。为什么会换名称?为什么还有ArcGIS Mobile这个“另类”的东西?这得从ArcGIS Runtime SDK的功能说起。
  做过ArcGIS Web API(ArcGIS API for Javascript/Flex/Silverlight)开发的朋友,应该可以很快理解狭义Runtime这个产品的所有功能。目前ArcGIS Runtime的功能与ArcGIS Web API,ArcGIS移动API(iOS/Windows Phone/Android)基本相同,都是基于ArcGIS REST API的。比如地图服务(动态/缓存)的加载,GraphicsLayer/FeatureLayer,基于FeatureService的数据编辑,Identify/Find/Query操作,GeometryService,Geoprocessing Service的调用等。它们的开发思路和代码编写几乎是一样的,比如ArcGIS API for Silverlight和目前的ArcGIS Runtime SDK for WPF,如出一辙(后者的前身就是ArcGIS API for WPF)。但狭义上的ArcGIS Runtime SDK与ArcGIS Web API不同之处在于,前者可以加载本地数据,包括Map Package/Tile Package/Locator Package/Geoprocessing Package。Map Package是包括.mxd文档和所有引用数据在内的压缩包,其余类似。最早的Package是9.3.1产品中的LayerPackage,它不仅包含了.lyr图层配置信息,还打包了图层所引用的实际数据。早期的ArcGIS Online平台允许用户上传LayerPackage以便分享,现在看来,直到此时Runtime产品的出现才将Package的概念发扬光大,并且为以后的所谓云GIS提供了更多的数据共享途径。对本地数据的读取是否破坏了基于ArcGIS REST API的框架呢?其实没有。ArcGIS API for WPF/Java/QT中,都内置了一个c++写的Web Server,读取本地数据后,会自动发布成这个Web Server上的REST服务来供Runtime SDK使用,所以一切功能,还是由REST API提供的。收起题外话,ArcGIS Runtime SDK的框架,是针对轻量级GIS产品(不包括ArcGIS Web API)的,它拥有统一的编程模型,可以用一致的开发思路,做出C/S,及移动端应用(Web API开发出B/S应用),为开发人员提供了极大的便利。这也是为什么将它们统称为ArcGIS Runtime SDKs的原因。
  在来说说ArcGIS Runtime for Windows Mobile(原来的ArcGIS Mobile)这个产品。之所以将它也归为ArcGIS Runtime SDK,是不无道理的。使用过ArcGIS Mobile的朋友一定知道,它的一个核心理念就是将数据划分为Basemap Layer和Operational Layer。前者是指起可视化参考作用的数据,一般是栅格底图或地图服务切片;后者是包含矢量信息在内的业务数据。ArcGIS Runtime中所涉及到Package,恰好与之对应。Tile Package对应Basemap Layer,Map Package对应Operational Layer。可以说,ArcGIS Mobile的设计理念,在整个ArcGIS Runtime产品家族中,得到了很好的延续。其实ArcGIS Mobile是个十分优秀的产品……
  到此,新的三个移动SDK(iOS/Windows Phone/Android)也归为ArcGIS Runtime SDKs的原因也就明了了,主要原因是都基于REST API的框架。另外基本可以肯定,它们都会使用相同的数据模型,即各种Package。由此不难推断,以后iOS/Windows Phone/Android平台上移动SDK的离线功能,也会依赖于Tile Package和Map Package。上周刚刚发布的ArcGIS API for iOS 2.1(以后要叫ArcGIS Runtime SDK for iOS)版本中,已经印证了这种猜测——加入了对Tile Package的支持,实现了原生的底图数据离线功能。其它两个移动平台的离线功能,敬请期待。
  由iPad这个产品引发的变革已经展开,移动设备数量正在爆炸性的增长,桌面设备和移动设备之间的概念会越来越模糊。Windows 8的发布预示着微软已经做出了改变,它即可以运行在桌面电脑上,也可以运行在平板设备中。有人说Windows 8的意义对于微软不亚于当年的Windows 95,我同意这个观点。Esri也做出了积极的改变,新的产品体系,新的产品命名,新的ArcGIS Runtime。