针对Android的C++ REST SDK

[原文发表地址]:Targeting Android with the C++ REST SDK

[原文发表时间]:2015/1/6 10:22 PM

刚好在新年假期前我们发布了C++ REST SDK的一个新的版本,V2.4.0。其中有一个新添加到类库里面的特性是通过使用Visual Studio 2015跨平台移动开发来支持Android和x86 Android模拟器。在这篇文章里面我打算大致展示一下如何创建一个应用了C++ REST SDK的Android Native Activity。

创建一个新的Android Native Activity

先创建一个新的Android应用程序,首先要同通过在Visual Studio的Visual C++ -> Cross Platform下面选择使用Native-Activity Application(Android)工程。

一旦这个模板完成,你就会得到一个Android应用程序,这个程序运行时会在屏幕上循环显示纯色,如果触摸一次屏幕就会改变一次颜色。

接下来让我们创建C++ REST SDK来和这个应用一起使用。

添加C++ REST SDK NuGet软件包

为了能够在制作针对Android上有一个轻松的用户体验,我们已经为了Android发布了一个C++ REST SDK的NuGet软件包。这个软件包包含了二进制的针对ARM设备和x86设备的模拟器。你可以通过在Native Activity项目上右键点击“Manage NuGet Packages…”添加一个引用到NuGet软件包。一旦对话框显示搜索到的C++ REST SDK,然后选择安卓软件包:

在你点击安装之后,Visual Studio会自动下载C++ REST SDK软件包,以及它的一些依赖文件,还会自动设置Include Paths,以及链接到一些正确的类库。

接下来你将需要给你的工程中C++属性做一点点修改,这是为了让它利用C++ REST SDK。默认的Android模板不会启用所有的C++ REST SDK将用到的C++特性。打开你添加过NuGet软件包的工程的配置属性对话框,你需要做一下的改动:

  • 更新为支持C++11标准:C++ -> Language -> C++ Language Standard = C++11
  • 打开C++异常:C++ -> Code Generation -> Enable C++ Exceptions = Yes
  • 改为使用GNU标准库:General -> Use of STL = gnustl_static
  • 开启RTTI:C++ -> Language -> Enable Run-Time Type Information = Yes 

C++ REST SDK将会连接服务以至于让我们的Android应用程序能够链接到互联网,权限需要添加到manifest文件。打开AndroidManifest.xml文件然后插入一个<uses-permission>标签:

  <!-- This is the platform API where NativeActivity was introduced. -->
 <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
 
 <!-- Internet permissions -->
 <uses-permission android:name="android.permission.INTERNET" />
 
 <!-- This .apk has no Java code itself, so set hasCode to false. -->
 <application android:label="@string/app_name" android:hasCode="false" android:debuggable="true">
 ...

在调用任何类库里面的API接口之前,最后一个需要做的改变是用Java虚拟机(JVM)去初始化C++ REST SDK。需要调用cpprest_init()方法来实现这个。对于Native Activity来说,最简单的方法是在android_main入口函数中添加cpprest_init()。 你可以从android_app结构来访问JAVM。添加一下方法调用到你的android_main里面:

 

 /**
 * This is the main entry point of a native application that is using
 * android_native_app_glue. It runs in its own thread, with its own
 * event loop for receiving input events and doing other things.
 */
 void android_main(struct android_app* state) {
 
 cpprest_init(state->activity->vm);
 
 struct engine engine;
 ...

 

如果你正在创建的不是Native Activity而是一个Shared Library, 你只需要在你的JNI_OnLoad方法里面简单得调用cpprest_init(…)。

现在这个应用程序已经全部设置好了,可以开始使用C++ REST SDK了。

演示如何通过C++ REST SDK发起一个请求

为了这个简单的演示,我们需要修改应用程序,当屏幕被触摸时,让它给服务器发起一个HTTP请求。 这里我们将会使用http_client,所以我们在 main.cpp的顶部添加以下的include和using namespace语句:

  #include <cpprest/http_client.h>
 
 using namespace web::http;
 using namespace web::http::client;

 

我们用一个布尔变量来跟踪是否这个HTTP请求失败了。在文件的顶部,找到结构体saved_state声明,然后添加一个新的布尔变量requestFailed:

 /**
 * Our saved state data.
 */
 struct saved_state {
 float angle;
 int32_t x;
 int32_t y;
 bool requestFailed;
 }; 

现在如果这个HTTP请求失败了,而不是在屏幕上继续循环显示不同的颜色,我们将整个屏幕设置为红色。找到engine_draw_frame方法,然后在引擎上检查保存的状态。如果有一个请求失败地调用glClearColor去设置颜色为红色,engine_draw_frame方法应该按照以下代码来写:

 /**
 * Just the current frame in the display.
 */
 static void engine_draw_frame(struct engine* engine) {
 if (engine->display == NULL) {
 // No display.
 return;
 }
 
 if (engine->state.requestFailed)
 {
 // Fill screen with RED color.
 glClearColor(1, 0, 0, 1);
 }
 else
 {
 // Just fill the screen with a color.
 glClearColor(((float) engine->state.x) / engine->width, engine->state.angle,((float) engine->state.y) / engine->height, 1);
 }
 
 glClear(GL_COLOR_BUFFER_BIT);
 eglSwapBuffers(engine->display, engine->surface);
 }    

接下来添加代码来发起HTTP请求。如果有一个移动地按下事件触发,找到engine_handle_input方法,然后发起一个HTTP请求给https://www.bing.com。这里给出类似的代码:

 

 /**
 * Process the next input event.
 */
 static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
 struct engine* engine = (struct engine*)app->userData;
 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
 engine->state.x = AMotionEvent_getX(event, 0);
 engine->state.y = AMotionEvent_getY(event, 0);
 
 // Only make an HTTP request if the motion even is a press down
 // and a prior request hasn't failed.
 if (!engine->state.requestFailed && AMotionEvent_getAction(event) == AMOTION_EVENT_ACTION_DOWN)
 {
 try
 {
 http_client client("https://www.bing.com");
 
 // Wait for response headers and check status code.
 http_response response = client.request(methods::GET).get();
 if (response.status_code() != status_codes::OK)
 {
 engine->state.requestFailed = true;
 LOGW("Received HTTP status code other than 200.");
 }
 
 // Wait for entire response body.
 response.content_ready().wait();
 } 
 catch (const http_exception &e)
 {
 engine->state.requestFailed = true;
 LOGW(e.what());
 }
 }
 
 return 1;
 }
 return 0;
 }
 

 

说明:在这个实例里面我们正在Event Handdler里面的HTTP请求上执行一个同步的中断等待。 在一个真实的应用程序里面,调用get()和wait()可能不应该这样写,为了连续性应该写为异步处理。

 

最后在engine_init_display一定要确保把requestFailed变量初始化为false:

 

  ...
 engine->display = display;
 engine->context = context;
 engine->surface = surface;
 engine->width = w;
 engine->height = h;
 engine->state.angle = 0;
 engine->state.requestFailed = false;
 ...
  

现在,需要确保当前解决方案的配置和架构是Debug和X86,然后build应用程序,保证不会有编译的错误。

在X86模拟器里面调试

拿着已经编译好的应用程序,你就可以在X86模拟器里面运行它了。在HTTP请求生成的地方加一个断点,位置大概在engine_handle_input()方法169行这里。确保已经选择了VS模拟器Android手机,然后开始调试。

 

当模拟器以及应用程序已经启动起来,就可以在手机上点击这个应用程序来模拟一个触摸的事件。
调试器应该可以停到你设置断点的位置然后你就能一步一步发起一个HTTP请求了。

 

除了在X86模拟器调试,如果一个Android设备连接着,你也可以部署到这个设备。 

关于本文章内的说明和如何在Android上使用C++ REST SDK的更多信息,请看这里的文档:在CodePlex上的文档。这里有文中提到的一个包含完整的工程的Visual Studio解决方案的压缩包文件。

额外的Android NuGet软件包

作为我们针对Android而为C++ REST SDK创建NuGet软件包的一部分工作,我们同时也为所有的他的依赖文件创建了NuGet软件包。为了和Android一起使用,这里有以下的NuGet软件包,Boost.AtomicBoost.ChronoBoost.Date_timeBoost.FilesystemBoost.LocaleBoost.RandomBoost.SystemBoost.ThreadLibiconv,和 OpenSSL。 即使你没有在用C++ REST SDK,你依然可以在你的Android应用程序里面简单地方便地使用这些类库。

CppRestAndroid.zip