Зачем это нужно
Мы хотим общение с API сервером написать на C++, а дальше использовать написанную библиотеку во всех наших приложения под различными платформами. Конечно мы хотим, чтобы работало под android.
Libcurl — это библиотека интерфейса API для передачи, которую разработчики могут встроить в свои программы; cURL действует как автономная обёртка для библиотеки libcurl. libcurl используется, чтобы обеспечить возможность передачи файлов (адресуемых с помощью URL) многочисленным приложениям (как открытым, так и коммерческим). (wikipedia)
Для iOS можно скачать готовый пример подключения и использования cURL с сайта разработчика. И с iOS всё просто.
Под android мне на просторах google не удалось найти ни одного исходника, где бы производилось успешное обращение к этой кросс-платформенной библиотеке. (Может я плохо искал).
И вообще говоря под android заставить работать cURL оказалось немного сложнее чем хотелось бы.
Что нам понадобится:
- Установленный и настроенный для работы с android Eclipse;
- ndk и умение работы с ним;
- Скомпиленная под android библиотека cURL.
Получение библиотеки cURL для android
Если пойти на сайт cURL и зайти в загрузки, то там можно найти скомпиленный бинарник (Android 7.31.0 binary SSL) который видимо можно запускать как консольную утилиту из под девайса. Но он совершенно бесполезен, если мы хотим работать с библиотекой из своего приложения.
Хорошо погуглив можно найти туториал, как собрать нужную для ndk *.a библиотеку, с которой можно уже работать из приложения.
Есть про портирование cURL под android и на хабре. В результате мы получим желанный *.a файл библиотеки. Сам я его не собирал. Я его честно скачал.
Дальше
Дальше полученную библиотеку можно смело вставлять проект и используя всю мощь ndk обращаться к ней.
Java часть часть
Создадим MainActivity с одной кнопкой и полем для ввода адреса сайта, с которого будем получать информацию.
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <EditText android:id="@+id/server_url" android:text="@string/default_url" android:hint="@string/server_url_hint" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true"/> <Button android:id="@+id/button_curl_call" android:layout_below="@+id/server_url" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="@string/button_curl" android:layout_gravity="right" /> <TextView android:id="@+id/text" android:layout_below="@+id/button_curl_call" android:layout_weight="1" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
MainActivity.java
package com.ifree.ndkNative; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { private static final String INTENT_HTML_DATA = ".html_data"; public static final int HANDLE_CALLBACK = 0; final private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case HANDLE_CALLBACK: String html = msg.getData().getString(INTENT_HTML_DATA); SetHtmlText(html); break; } } }; private void SetHtmlText(String html){ TextView tv = (TextView) findViewById(R.id.text); tv.setText(html); } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final EditText serverUrl = (EditText) findViewById(R.id.server_url); Button btnCurl = (Button) findViewById(R.id.button_curl_call); btnCurl.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Native curl = new Native(); curl.addCurlCallbackListener(new ICurlCallbackListener() { @Override public void curlCallBack(String callback) { //для того, чтобы синхронизировать с потоком gui и отобразить полученный текст в TextView, используем handler Bundle bundle = new Bundle(); bundle.putString(INTENT_HTML_DATA, callback); Message message = handler.obtainMessage(); message.setData(bundle); handler.sendMessage(message); } }); String response = curl.get_text_from_cpp(String.valueOf(serverUrl.getText()));//Собственно само обращение к С++ части } }); } }
Напишем класс Native.java, в котором будет производиться обращение к С++ коду.
package com.ifree.ndkNative; import java.util.HashSet; public class Native { private HashSet<ICurlCallbackListener> callBackListeners = new HashSet<ICurlCallbackListener>(); public void addCurlCallbackListener(ICurlCallbackListener listener){ callBackListeners.add(listener); } public void removeCurlCallbackListener(ICurlCallbackListener listener){ callBackListeners.remove(listener); } static { System.loadLibrary("ndkNative"); } //(ключевое слово native говорит, что реализация будет на C++): public native String get_text_from_cpp(String data); private void callback(String data) { for(ICurlCallbackListener listener:callBackListeners){ listener.curlCallBack(data); } } }
Нужно не забыть добавить в AndroidManifest разрешение на интернет
<uses-permission android:name="android.permission.INTERNET"/>
С++ часть
Созданный при помощи утилиты javah файл com_ifree_ndkNative_Native.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_ifree_ndkNative_Native */ #ifndef _Included_com_ifree_ndkNative_Native #define _Included_com_ifree_ndkNative_Native #ifdef __cplusplus extern "C" { #endif /* * Class: com_ifree_ndkNative_Native * Method: get_text_from_cpp * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_ifree_ndkNative_Native_get_1text_1from_1cpp (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
Определим функцию получения данных с сервера (JNICALL Java_com_ifree_ndkNative_Native_get_1text_1from_1cpp) в ndkNative.cpp
#include <string.h> #include <stdio.h> #include "stddef.h" #include <jni.h> #include "com_ifree_ndkNative_Native.h" #include "curl/curl.h" #include "curl/easy.h" JNIEnv * gEnv; jobject gObj; void function_callback(jstring str){ jclass cls = gEnv->GetObjectClass(gObj); jmethodID mid = gEnv->GetMethodID(cls, "callback", "(Ljava/lang/String;)V");//вызов метода из java Native.callback(String data) gEnv->CallVoidMethod(gObj, mid, str); } size_t function_pt(void *ptr, size_t size, size_t nmemb, void *stream){ function_callback(gEnv->NewStringUTF((char *) ptr)); size_t written = fwrite(ptr, size, nmemb, (FILE*)stream); if(written <= 0) return written * size; } JNIEXPORT jstring JNICALL Java_com_ifree_ndkNative_Native_get_1text_1from_1cpp (JNIEnv * env, jobject obj, jstring str) { gEnv = env; gObj = obj; CURL *curl; CURLcode res; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, env->GetStringUTFChars(str, 0)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_pt); res = curl_easy_perform(curl); curl_easy_cleanup(curl); /* Check for errors */ if(res != CURLE_OK) function_callback(gEnv->NewStringUTF(curl_easy_strerror(res))); }else{ function_callback(gEnv->NewStringUTF("error")); } return env->NewStringUTF( "ok" ); }
Android.mf
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) include $(CLEAR_VARS) LOCAL_MODULE:= libcurl LOCAL_SRC_FILES := libcurl.a include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := ndkNative LOCAL_SRC_FILES := ndkNative.cpp LOCAL_STATIC_LIBRARIES := libcurl include $(BUILD_SHARED_LIBRARY)
Исходник
P.S.: Необходимо сильно видоизменить проект для использования его в реальных приложения, в частности всю работу с cURL нужно вынести в отдельный С++ класс-обвертку.
ссылка на оригинал статьи http://habrahabr.ru/post/184738/
Добавить комментарий