📜 ⬆️ ⬇️

Work with cURL on android

Why do you need it


We want to communicate with the API server to write in C ++, and then use the written library in all of 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 (addressed via a URL) to numerous 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, everything is simple.
')
Under android, I couldn’t find a single source on google where I could successfully access this cross-platform library. (Maybe I was looking bad).

And generally speaking under android getting cURL to work turned out to be a little more difficult than we would like.

What we need:


Getting the cURL library for android


If you go to the cURL website and go into the downloads, then there you can find a compiled binary (Android 7.31.0 binary SSL) which you can apparently 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.
Googling well you can find a tutorial on how to build the library you need for ndk * .a, with which you can already work from the application.

There is about porting cURL under android and on habr . As a result, we get the desired * .a library file. I myself did not collect it. I honestly downloaded it.

Farther


Then you can safely insert the resulting library into the project and use all the power of ndk to access it.
Java part part

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

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()));//    ++  } }); } } 


Let's write the Native.java class, which will be used to call the C ++ code.
 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); } } } 


You need to remember to add Internet permission to AndroidManifest
 <uses-permission android:name="android.permission.INTERNET"/> 

C ++ part

Created using the javah utility file 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 

Define the function of receiving data from the server (JNICALL Java_com_ifree_ndkNative_Native_get_1text_1from_1cpp) in 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) 


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 transferred to a separate C ++ wrapper class.

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

Source: https://habr.com/ru/post/184738/


All Articles