Work with cURL on android

  • Tutorial

Why is it necessary


We want to write communication with the API server in C ++, and then use the written library in all our applications under various platforms. Of course we want to work under android.

Libcurl is a transfer API library that developers can embed in their programs; cURL acts as a standalone wrapper for the libcurl library. libcurl is used to provide the ability to transfer files (addressable via URLs) to multiple applications (both open and commercial). ( wikipedia )

For iOS, you can download a ready-made example of connecting and using cURL from the developer's site. And with iOS it's simple.

Under android, on google spaces, I could not find a single source where a successful access to this cross-platform library would be made. (Maybe I was looking badly).

And generally speaking, making android work with cURL turned out to be a bit more complicated than we would like.

What we need:

  • Installed and configured to work with android Eclipse ;
  • ndk and the ability to work with him;
  • The cURL library compiled for android .

Getting cURL library for android


If you go to the cURL website and go into downloads, then you can find a compiled binary (Android 7.31.0 binary SSL) that can apparently be run as a console utility from under the device. But it is completely useless if we want to work with the library from our application.
Good google, you can find a tutorial on how to build the library you need for ndk * .a, which you can already work with from the application.

There are about porting cURL for android and Habré . As a result, we get the desired * .a library file. I myself did not collect it. I honestly downloaded it.

Further


Further, the resulting library, you can safely embed the project and use all the power of ndk to access it.
Java part part

Create MainActivity with one button and a field for entering the address of the site from which we will receive information.

activity_main.xml :




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()));//Собственно само обращение к С++ части
				}
			});   
	    }
}


We will write the Native.java class , in which the call to C ++ code will be performed.
package com.ifree.ndkNative;
import java.util.HashSet;
public class Native {
	private HashSet callBackListeners = new HashSet();
	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);
		}
	}
}


Do not forget to add Internet permission to AndroidManifest

C ++ part

Created by the utility javah file com_ifree_ndkNative_Native.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* 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

Define the function of receiving data from the server (JNICALL Java_com_ifree_ndkNative_Native_get_1text_1from_1cpp) in ndkNative.cpp
#include 
#include 
#include "stddef.h"
#include 
#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)


Source code




Ps: You need to strongly modify the project to use it in real applications, in particular, all work with cURL needs to be moved to a separate C ++ wrapper class.

Pss: code convention in the project is a little lame.

Also popular now: