Работа с cURL на android

от автора

Зачем это нужно

Мы хотим общение с 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *