libcurl在android下的移植、编译与测试以及java接口的封装

curl是利用URL语法在命令行方式下工作的文件传输工具

它支持很多协议:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。

curl同样支持HTTPS认证,HTTP POST方法, HTTP PUT方法, FTP上传, kerberos认证,HTTP上传, 代理服务器, cookies, 用户名/密码认证, 下载文件断点续传,上载文件断点续传,,http代理服务器管道( proxy tunneling), 甚至它还支持IPv6, socks5代理服务器,,通过http代理服务器上传文件到FTP服务器等等,功能十分强大。

移植之前需做的准备工作:

1、直接到网站上下载 curl 源码

2、利用tar在android编译环境下,一般放在 external 目录下

一.移植Curl工具到Android环境步骤

1.修改cURL源码下的mk文件。源码下面的Android.mk文件最后生成的是静态库libcurl.a,做如下修改(编译成动态库)。

LOCAL_PRELINK_MODULE := false

LOCAL_MODULE:= libcurl

LOCAL_MODULE_TAGS := optional



# Copy the licence to a place where Android will find it.

# Actually, this doesn't quite work because the build system searches

# for NOTICE files before it gets to this point, so it will only be seen

# on subsequent builds.

ALL_PREBUILT += $(LOCAL_PATH)/NOTICE

$(LOCAL_PATH)/NOTICE: $(LOCAL_PATH)/COPYING | $(ACP)

$(copy-file-to-target)

#include $(BUILD_STATIC_LIBRARY)

include $(BUILD_SHARED_LIBRARY)

#########################

2.配置编译环境(cd 到Android.mk同一目录,直接在控制台输入下列代码或者把下面代码弄成sh脚本执行)红色部分根据自己源码情况

ANDROID_HOME=/home/zhoulc/android/ && \

NDK_HOME=/home/zhoulc/android/ndk && \

PATH="$ANDROID_HOME/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin:$PATH" \

./configure --host=arm-linux CC=arm-eabi-gcc --with-random=/dev/urandom \

CPPFLAGS="-I$NDK_HOME/platforms/android-8/arch-arm/usr/include \

-I $ANDROID_HOME/external/curl/include/  \

-I $ANDROID_HOME/external/curl/3rd/include   \

-I $ANDROID_HOME/external/curl   \

-I $ANDROID_HOME/out/target/product/generic/obj/STATIC_LIBRARIES/libcurl_intermediates   \

-I $ANDROID_HOME/dalvik/libnativehelper/include/nativehelper   \

-I $ANDROID_HOME/system/core/include   \

-I $ANDROID_HOME/hardware/libhardware/include   \

-I $ANDROID_HOME/hardware/libhardware_legacy/include   \

-I $ANDROID_HOME/hardware/ril/include   \

-I $ANDROID_HOME/dalvik/libnativehelper/include   \

-I $ANDROID_HOME/frameworks/base/include   \

-I $ANDROID_HOME/frameworks/base/opengl/include   \

-I $ANDROID_HOME/frameworks/base/native/include   \

-I $ANDROID_HOME/external/skia/include   \

-I $ANDROID_HOME/out/target/product/generic/obj/include   \

-I $ANDROID_HOME/bionic/libc/arch-arm/include   \

-I $ANDROID_HOME/bionic/libc/include   \

-I $ANDROID_HOME/bionic/libstdc++/include   \

-I $ANDROID_HOME/bionic/libc/kernel/common   \

-I $ANDROID_HOME/bionic/libc/kernel/arch-arm   \

-I $ANDROID_HOME/bionic/libm/include   \

-I $ANDROID_HOME/bionic/libm/include/arch/arm   \

-I $ANDROID_HOME/bionic/libthread_db/include \

-include $ANDROID_HOME/system/core/include/arch/linux-arm/AndroidConfig.h \

-I $ANDROID_HOME/system/core/include/arch/linux-arm/ \

-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID -DNDEBUG -DNDEBUG -DHAVE_CONFIG_H" \

CFLAGS="-fno-exceptions -Wno-multichar -msoft-float -fpic -ffunction-sections \

-funwind-tables -fstack-protector -Wa,--noexecstack -Werror=format-security \

-fno-short-enums -march=armv5te -mtune=xscale  -Wno-psabi -mthumb-interwork  \

-fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith \

-Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point  \

-g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once \

-fgcse-after-reload -frerun-cse-after-loop -frename-registers  -UDEBUG \

-mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64   \

-Wpointer-arith -Wwrite-strings -Wunused -Winline -Wnested-externs \

-Wmissing-declarations -Wmissing-prototypes -Wno-long-long -Wfloat-equal \

-Wno-multichar -Wsign-compare -Wno-format-nonliteral -Wendif-labels \

-Wstrict-prototypes -Wdeclaration-after-statement -Wno-system-headers"  \

LIBS="-nostdlib -Bdynamic -Wl,-T,$ANDROID_HOME/build/core/armelf.x \

-Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc \

-L$ANDROID_HOME/out/target/product/generic/obj/lib -Wl,-z,noexecstack \

-Wl,-rpath-link=$ANDROID_HOME/out/target/product/generic/obj/lib \

-lc -llog -lcutils -lstdc++ \

-Wl,--no-undefined $ANDROID_HOME/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0/libgcc.a  \

$ANDROID_HOME/out/target/product/generic/obj/lib/crtend_android.o \

-lm $ANDROID_HOME/out/target/product/generic/obj/lib/crtbegin_dynamic.o \

-L$ANDROID_HOME/external/curl/3rd/libs"

3.编译libcurl.so库

cd进入android/external/curl源码目录

mm-》编译生成libcurl.so库

4.编写测试case 以及Android.mk文件并生成可执行文件

新建一个测试案例curl_test.cpp

#include "curl/curl.h"

#include <stdio.h>;

int main() { 

    CURL *curl; 

    CURLcode res;

    curl_global_init(CURL_GLOBAL_ALL);

    curl = curl_easy_init();

    if (curl) {     

        curl_easy_setopt(curl, CURLOPT_URL, "http://www.baidu.com/"); 

        res = curl_easy_perform(curl); 

        if (0!=res) {      

            printf("curl error: %d\n", res);         

        }        

        curl_easy_cleanup(curl);     

    }   

    curl_global_cleanup();

    return 0;

}

在同一目录下写一个Android.mk文件生成curl_test可执行文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES += \

    $(TOP)/external/curl/include/ \

LOCAL_SRC_FILES:= curl_test.cpp

# No shared libraries.

# No static libraries.

LOCAL_SHARED_LIBRARIES := libcurl

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE := curl_test

include $(BUILD_EXECUTABLE)


生成可执行文件:curl_test

4.运行查看测试结果

运行测试case:curl_test

5.(补充)移植libcurl到android4.0,修改两个地方

1)把生成的路径改一下,一般默认为out/target/product/generic下面,我们根据系统不同(根据lunch选择不同,最终生成的路径不一样)改为系统的全局变量,

把$ANDROID_HOME/out/target/product/generic替换成$ANDROID_PRODUCT_OUT

ANDROID_HOME_CURL=../../ && \

NDK_HOME_CURL=../../prebuilt/ndk && \

PATH="$ANDROID_HOME_CURL/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin:$PATH" \

./configure --host=arm-linux CC=gcc --with-random=/dev/urandom \

CPPFLAGS="-I$NDK_HOME_CURL/platforms/android-8/arch-arm/usr/include \

-I $ANDROID_HOME_CURL/external/curl/include/  \

-I $ANDROID_HOME_CURL/external/curl/3rd/include   \

-I $ANDROID_HOME_CURL/external/curl   \

-I $ANDROID_HOME_CURL/out/target/product/generic/obj/STATIC_LIBRARIES/libcurl_intermediates   \

-I $ANDROID_HOME_CURL/dalvik/libnativehelper/include/nativehelper   \

-I $ANDROID_HOME_CURL/system/core/include   \

-I $ANDROID_HOME_CURL/hardware/libhardware/include   \

-I $ANDROID_HOME_CURL/hardware/libhardware_legacy/include   \

-I $ANDROID_HOME_CURL/hardware/ril/include   \

-I $ANDROID_HOME_CURL/dalvik/libnativehelper/include   \

-I $ANDROID_HOME_CURL/frameworks/base/include   \

-I $ANDROID_HOME_CURL/frameworks/base/opengl/include   \

-I $ANDROID_HOME_CURL/frameworks/base/native/include   \

-I $ANDROID_HOME_CURL/external/skia/include   \

-I $ANDROID_HOME_CURL/out/target/product/generic/obj/include   \

-I $ANDROID_HOME_CURL/bionic/libc/arch-arm/include   \

-I $ANDROID_HOME_CURL/bionic/libc/include   \

-I $ANDROID_HOME_CURL/bionic/libstdc++/include   \

-I $ANDROID_HOME_CURL/bionic/libc/kernel/common   \

-I $ANDROID_HOME_CURL/bionic/libc/kernel/arch-arm   \

-I $ANDROID_HOME_CURL/bionic/libm/include   \

-I $ANDROID_HOME_CURL/bionic/libm/include/arch/arm   \

-I $ANDROID_HOME_CURL/bionic/libthread_db/include \

-include $ANDROID_HOME_CURL/system/core/include/arch/linux-arm/AndroidConfig.h \

-I $ANDROID_HOME_CURL/system/core/include/arch/linux-arm/ \

-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID -DNDEBUG -DNDEBUG -DHAVE_CONFIG_H" \

CFLAGS="-fno-exceptions -Wno-multichar -msoft-float -fpic -ffunction-sections \

-funwind-tables -fstack-protector -Wa,--noexecstack -Werror=format-security \

-fno-short-enums -march=armv5te -mtune=xscale  -Wno-psabi -mthumb-interwork  \

-fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith \

-Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point  \

-g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once \

-fgcse-after-reload -frerun-cse-after-loop -frename-registers  -UDEBUG \

-mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64   \

-Wpointer-arith -Wwrite-strings -Wunused -Winline -Wnested-externs \

-Wmissing-declarations -Wmissing-prototypes -Wno-long-long -Wfloat-equal \

-Wno-multichar -Wsign-compare -Wno-format-nonliteral -Wendif-labels \

-Wstrict-prototypes -Wdeclaration-after-statement -Wno-system-headers"  \

LIBS="-nostdlib -Bdynamic -Wl,-T,$ANDROID_HOME_CURL/build/core/armelf.x \

-Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc \

-L$ANDROID_PRODUCT_OUT/obj/lib -Wl,-z,noexecstack \

-Wl,-rpath-link=$ANDROID_PRODUCT_OUT/obj/lib \

-lc -llog -lcutils -lstdc++ \

-Wl,--no-undefined $ANDROID_HOME_CURL/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0/libgcc.a  \

$ANDROID_PRODUCT_OUT/obj/lib/crtend_android.o \

-lm $ANDROID_PRODUCT_OUT/obj/lib/crtbegin_dynamic.o \

-L$ANDROID_HOME_CURL/external/curl/3rd/libs"

2)修改Android.mk

#ALL_PREBUILT += $(LOCAL_PATH)/NOTICE

#$(LOCAL_PATH)/NOTICE: $(LOCAL_PATH)/COPYING | $(ACP)

# $(copy-file-to-target)

把关于ALL_PREBUILT模块全部注释调

二.编译过程中出现的错误以及修改方法

错误1:考入源码,修改android.mk改成生成.so动态库(默认curl.a静态库)

============================================

PLATFORM_VERSION_CODENAME=REL

PLATFORM_VERSION=2.3.1

TARGET_PRODUCT=generic

TARGET_BUILD_VARIANT=eng

TARGET_SIMULATOR=

TARGET_BUILD_TYPE=release

TARGET_BUILD_APPS=

TARGET_ARCH=arm

HOST_ARCH=x86

HOST_OS=linux

HOST_BUILD_TYPE=release

BUILD_ID=GRH78

============================================

make: Entering directory `/home/zhoulc/android'

build/core/base_rules.mk:151: *** external/curl: LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles.  Stop.

解决方法:在android.mk里面把生成的静态库命令注释掉。

错误2:提示没有头文件

============================================

PLATFORM_VERSION_CODENAME=REL

PLATFORM_VERSION=2.3.1

TARGET_PRODUCT=generic

TARGET_BUILD_VARIANT=eng

TARGET_SIMULATOR=false

TARGET_BUILD_TYPE=release

TARGET_BUILD_APPS=

TARGET_ARCH=arm

HOST_ARCH=x86

HOST_OS=linux

HOST_BUILD_TYPE=release

BUILD_ID=GRH78

============================================

make: Entering directory `/home/zhoulc/android'

make: *** No rule to make target `external/curl/include/curl/curlbuild.h', needed by `out/target/product/generic/obj/include/libcurl/curl/curlbuild.h'.  Stop.

make: Leaving directory `/home/zhoulc/android'

解决方法:2)cd 到 external/curl目录下,输入(红色字部分根据自己的环境做相应的更改):

参照执行步骤2

错误3:动态库生成,但出现stop问题,检测一下是不是静态库到动态库的改变,改写彻底没有。

错误4:出现超出 ALL_PREBUILT.定义

build/core/main.mk:537: *** Some files have been added to ALL_PREBUILT.

build/core/main.mk:538: *

build/core/main.mk:539: * ALL_PREBUILT is a deprecated mechanism that

build/core/main.mk:540: * should not be used for new files.

build/core/main.mk:541: * As an alternative, use PRODUCT_COPY_FILES in

build/core/main.mk:542: * the appropriate product definition.

build/core/main.mk:543: * build/target/product/core.mk is the product

build/core/main.mk:544: * definition used in all products.

build/core/main.mk:545: *

build/core/main.mk:546: * unexpected NOTICE in ALL_PREBUILT

build/core/main.mk:546: * unexpected NOTICE in ALL_PREBUILT

build/core/main.mk:547: *

build/core/main.mk:548: *** ALL_PREBUILT contains unexpected files.  Stop.

make: Leaving directory `/home/zhoulc/HAndroid_4.0'

解决方法:注释掉相关信息

#ALL_PREBUILT += $(LOCAL_PATH)/NOTICE

#$(LOCAL_PATH)/NOTICE: $(LOCAL_PATH)/COPYING | $(ACP)

# $(copy-file-to-target)

三.java层通过jni调用libcurl API学习摸索经验(CURL API对应java接口封装)

1.jni模块的实现与学习

 1jvm加载c的动态库

    应用层的Java类是在虚拟机(VM: Vitual Machine)上执行的,而C件不是在VM上执行,Java程式通过下面方法要求VM去载入(Load)所指定的C组件。

System.loadLibrary(*.so的档案名);

(1)告诉VM此C组件使用那一个JNI版本。如果你的*.so档没有提供JNI_OnLoad()函数,VM会默认该*.so档是使用最老的JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM。

(2)由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization) 。

执行过程如下

在jni里面找到JNI_OnLoad方法-》在JNI_OnLoad里面对不同的分支进行注册,比如register_join_easy_natives-》在该方法中把native方法与c方法一一对应registerNativeMethods在该方法中找到java对应的类以及注册对应方法


static int registerNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, int numMethods) {

jclass clazz;



clazz = env->FindClass(className);

if (clazz == NULL) {

return JNI_FALSE;

}

if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {

return JNI_FALSE;

}

return JNI_TRUE;

}

int register_join_multi_natives(JNIEnv *env) {



LOGI("register_join_easy_natives");

return registerNativeMethods(env, g_class_name, g_cls_methods,

sizeof(g_cls_methods) / sizeof(g_cls_methods[0]));



}


2)jni参数设置

其中比较难以理解的是第二个参数,例如

"()V"

"(II)V"

"(Ljava/lang/String;Ljava/lang/String;)V"

实际上这些字符是与函数的参数类型一一对应的。

"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();

"(II)V" 表示 void Func(int, int);

具体的每一个字符的对应关系如下

字符   Java类型     C类型

V      void         void

Z      jboolean     boolean

I       jint         int

J       jlong        long

D      jdouble       double

F      jfloat            float

B      jbyte            byte

C      jchar           char

S      jshort          short

数组则以"["开始,用两个字符表示

[I     jintArray       int[]

[F     jfloatArray     float[]

[B     jbyteArray     byte[]

[C    jcharArray      char[]

[S    jshortArray      short[]

[D    jdoubleArray    double[]

[J     jlongArray      long[]

[Z    jbooleanArray    boolean[]

上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

Ljava/lang/String; String jstring

Ljava/net/Socket; Socket jobject

如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"

3)在c中访问java的属性和方法

在此,针对andorid中c++与java中的方法互调,引用参考说明如下:

步骤1).andorid CPP调用java函数和访问其成员:原理 => CPP代码找到java的那个class里面的函数的入口地址,然后在CPP代码中调用java代码

步骤1) 用FindClass()函数找到该java类(如android.os.Binder)的实例对象的引用:

  jclass clazz = env->FindClass(kBinderPathName) = env->FindClass("android.os.Binder")

步骤2) 用GetFieldID()函数获取到要访问的域(field: 实际上就是该java class中的某个成员变量的名字)的ID:

  gBinderOffsets.mObject = env->GetFieldID(clazz, "mObject", "I") // mObject为java class "Binder"里的一个成员变量

  -> 注意,这里将要访问的那个java对象的成员mObject的ID保存到了全局变量gBinderOffsets.mObject中,这样做的前提和优点如下:

  前提: android里面,每个java进程中只允许有一个java虚拟机(sun公司原始的java架构中,一个进程中可以有多个java虚拟机)

  优点: 除了第一次,以后每次要访问该java对象的成员mObject就非常快了(不用再去FindClass()和GetFieldID())

步骤3) 用GetMethodID()函数获取到要访问的方法(Method: 实际上就是该java class中的某个成员函数的名字)的ID:

  gBinderOffsets.mExecTransact = env->GetMethodID(clazz, "execTransact", "(IIII)Z") // execTransact为java class "Binder"里的一个成员函数

步骤4) 用类似于GetIntField()的函数获取到该java对象的那个域(即成员)的值:

  IBinder* target = (IBinder*)env->GetIntField(obj,gBinderProxyOffsets.mObject)

     // 获取java android.os.Binder类型对象里面的成员mObject的值 
步骤5) 用类似于CallBooleanMethod()的函数调用到该java对象的那个成员函数:

  jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, code, (int32_t)&data, (int32_t)reply, flags)

4)数组,字符串在jni中的定义与使用

要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:

NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、 NewBooleanArray、NewStringUTF、NewCharArray、NewByteArray、NewString,访问通过 GetBooleanArrayElements、GetIntArrayElements等函数。

5libcurl APIjava API转变过程中修改部分

     curl_easy_setopt中根据第二个参数不同,第三个参数传递格式不同,在C中该方法第三个参数有四种类型,int、long、回调方法、回调方法参数,想在java里面一一对应,只能使用方法的重载,通过不同的参数值传递,对应上c里面的方法,至于回调函数和回调函数参数,通过在java层传递一个Object对象过去,在JNI层实现对回调函数的注册和回调函数参数的设置。

2.java部分的设计

1.java模块划分

 1curl模块有三个部分

easy_handle:为libcurl的最基础部分,所有的操作都是在easy_handle上进行的,比如发送、请求数据都是在其上进行的。如果直接在easy_handle执行操作 curl_easy_perform 函数是阻塞的(即需要等到完成才返回)

multi_handle:libcurl为异步操作提供的接口,允许调用方在一个线程中处理多个操作(就是easy_handle上的操作,注意是单线程下的),内部multi_handle采用堆栈的方式保存多个easy_handle,然后在一个线程中可以同时对多个easy_handle进行处理,multi_handle的执行操作 curl_multi_perform 函数是立即返回的,不会阻塞

share_handle:有时候多个easy_handle需要分享一些信息,比如cookie,当一个连接获取一个新的cookie,就可以将这个cookie共享到所有的连接上

分别对应java的三个类,Easy类、Multi类、Share类(实体类)

除去个别API,其他接口基本和curl提供API一一对应

 2setopt所使用的第二个参数,对应C里面的模板类

OptValue.java里面初始化libcurl里面的常量

比如经常用到的CURLOPT_URL等

 3)工具类,在进行文件上传下载、断点下载的时候,对文件经行相应操作

上传的时候,需判断需要上传的文件是否存在。

下载的时候,判断是否下载完成,如果没有完成,择可以选择断点下载,重新下载都行。如果选择重新下载,或者经过自动判断是第一次下载,则会删掉以前的文件,重新创建一个用来存放下载数据。

3.规划过程中出现的问题与解决方案

  1)网络不通

在AndroidManifest.xml里面添加

<uses-permission android:name="android.permission.INTERNET" /> 

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

一个对应能否访问网络,另一个SDcard写权限

  2)文件无法下载

首先查看网络是否正常。

然后看下下载流程每一步是不是正常执行返回值一般为0。

Perform是否调用并且调用成功。

在进行回调函数注册之后,注册回调函数参数,看设置是否正确。

看网络访问路径是否正确。

  3)文件无法读写

观察文件打开的方式,查看sdcard是否添加可操作权限。

  4) 对象从java传到C出现问题。

cls = env->GetObjectClass(object);

在java层经行向上类型转换,传递Object类型到C层,然后通过JNI直接获取自动封装和解析,在java层提供的是公共接口,既所有类的父类Object。对整个模块提供扩展。

  5)乱码问题(相当给力)

最开始提供了两个方法,一个从String类型到char*类型的转换,一个从char*到jstring类型的转换,把原始数据进行了编码格式的转换。

//jstring to char*

char* jstringTostring(JNIEnv* env, jstring jstr) {

char* rtn = NULL;

jclass clsstring = env->FindClass("java/lang/String");

jstring strencode = env->NewStringUTF("utf-8");

jmethodID mid = env->GetMethodID(clsstring, "getBytes",

"(Ljava/lang/String;)[B");

jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);

jsize alen = env->GetArrayLength(barr);

LOGI("barr = %d", alen);

jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);

if (alen > 0) {

rtn = (char*) malloc(alen + 1);

memcpy(rtn, ba, alen);

rtn[alen] = 0;

LOGI("sizeof(rtn) = %d", strlen(rtn));

}

env->ReleaseByteArrayElements(barr, ba, 0);

return rtn;

}

//char* to jstring

jstring stoJstring(JNIEnv* env, const char* pat) {

jclass strClass = env->FindClass("java/lang/String");

jmethodID ctorID = env->GetMethodID(strClass, "<init>",

"([BLjava/lang/String;)V");

jbyteArray bytes = env->NewByteArray(strlen(pat));

env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);

jstring encoding = env->NewStringUTF("GB2312");

return (jstring) env->NewObject(strClass, ctorID, bytes, encoding);

}

这个两个功能比较实用。

在整个设计改革之前,是通过c层传递流到java层,然后再由java层进行文件的读写操作,这个过程中出现了一些乱码,通过以上提供的两种方法能够解决。Java默认的String类型是UTF-8编码格式,而下下来的文件好多是用GBK编码方式,所以在java层进行文件读写的时候容易出现问题。

后来把整个模块经行了改写,读写文件是由c层完成的,没有了编码问题,乱码问题迎刃而解。

6)断点续传

断点续传需要对服务器上的文件经行比较,在文件操作的工具中提供了两个构造函数,一个需要源文件大小,如果调用这个方法当size不为0的时候,可以进行判断是否下载完成,然后再下载过程中设置断点续传,就可以实现断点续传功能,如果不初始化size或直接赋值为0,则默认为重新下载。

注:源文件大小,就是需要通过http,ftp下载的文件,需要获取服务器上文件的大小,暂时没有提供接口如何获取。

7)并发执行上传下载

由于网上multi模块资料较少,暂时只实现了multi一些简单功能,由于easy模块是单线程添加一个select接口,添加下载上传操作的handle,然后进行multi_perform

四.Libcurl使用案例与接口说明

1.http从头下载以及测试下载速度doubleValue

//http 下载以及测试下载速度

        String url = "http://www.sina.com.cn/";

        result = easy.easySetopt(handle, OptValue.CURLOPT_URL, url);

//      easy.easySetopt(handle, OptValue.CURLOPT_HEADER, 1);

        System.out.println("easySetopt state is "+result);

        easy.easySetopt(handle, OptValue.CURLOPT_WRITEFUNCTION, operate);

        result = easy.easyPerform(handle);

        double doubleValue = easy.easyGetinfo(handle, OptValue.CURLINFO_SPEED_DOWNLOAD, 0.0);

        System.out.println("dobleValue = "+doubleValue);

        easy.easyCleanUp(handle);

2.ftp上传

//ftp 上传

        String url = "ftp://192.168.18.37/sina.html";

        result = easy.easySetopt(handle, OptValue.CURLOPT_URL, url);



        easy.easySetopt(handle, OptValue.CURLOPT_USERPWD, "ipanel:ipanel123");

        easy.easySetopt(handle, OptValue.CURLOPT_UPLOAD, 1L);

        easy.easySetopt(handle, OptValue.CURLOPT_INFILESIZE, file.length());

        easy.easySetopt(handle, OptValue.CURLOPT_READFUNCTION, operate);

        result = easy.easyPerform(handle);

        easy.easySetopt(handle, OptValue.CURLOPT_INFILESIZE_LARGE, file.length());

        System.out.println("easyPerform state is "+result

3.tcp_ip 通信

//        //tcp_ip 通信

        String url = "192.168.21.13";

        result = easy.easySetopt(handle, OptValue.CURLOPT_URL, url);

        System.out.println("easySetopt state is "+result);

        easy.easySetopt(handle, OptValue.CURLOPT_PORT, 6666);

        easy.easySetopt(handle, OptValue.CURLOPT_CONNECT_ONLY, 1L);

        result = easy.easyPerform(handle);

        System.out.println("easyPerform state is "+result);

        String buf = "zhoulong潮";

        int len = easy.easySend(handle, buf, buf.length());

        for (int i = 1; i <=1; i++) {

        String value = null;

        value = easy.easyRecv(handle,len);

        System.out.println(value);

}

        easy.easyCleanUp(handle);

        Log.d("","清除成功");

4.并发执行下载任务

int http_handle = easy.easyInit();

       int  http_handle2 = easy.easyInit();

       easy.easySetopt(http_handle, OptValue.CURLOPT_URL, "http://www.sohu.com/"); 

       easy.easySetopt(http_handle2, OptValue.CURLOPT_URL, "http://www.sina.com.cn/");      

       int multi_handle = multi.multiInit();

       multi.multiAddHandle(multi_handle, http_handle);

       multi.multiAddHandle(multi_handle, http_handle2);

       easy.easySetopt(http_handle, OptValue.CURLOPT_WRITEFUNCTION, operate);

       easy.easySetopt(http_handle2, OptValue.CURLOPT_WRITEFUNCTION, operate2);  

       /* 

        * 调用curl_multi_perform函数执行curl请求 

        * url_multi_perform返回CURLM_CALL_MULTI_PERFORM(-1)时,表示需要继续调用该函数直到返回值不是CURLM_CALL_MULTI_PERFORM为止 

        * running_handles变量返回正在处理的easy curl数量,running_handles为0表示当前没有正在执行的curl请求 
          */  

       int still_running = 0;

       while(true){

         int perform[] = multi.multiPerform(multi_handle);

         System.out.println("perform step 1"+"         "+perform[0]+"      "+perform[1]);

         if(perform[0] != -1){

          still_running = perform[1];

          break;

         }

       }   

       while(still_running > 0) {

        if (-1 == multi.multiSelect(multi_handle))  

           {  

               break;  

           } else {  

               // select监听到事件,调用curl_multi_perform通知curl执行相应的操作 //  

            while(true){

                int perform[] = multi.multiPerform(multi_handle);

                System.out.println("perform step 2"+"         "+perform[0]+"      "+perform[1]);

                if(perform[0] != -1){

                 still_running = perform[1];

                 break;

                }

              }

           }  

        }

       multi.multiRemoveHandle(multi_handle, http_handle);

       multi.multiRemoveHandle(multi_handle, http_handle2);

       multi.multiCleanUp(multi_handle);

       easy.easyCleanUp(http_handle);

       easy.easyCleanUp(http_handle2);

5.断点续传 

File file = new File("sdcard/"+path);

//判断是否下载完全

          FileOperate operate = new FileOperate(file,size);

          int handle = -1;

          int result = -1;

          handle = easy.easyInit();

          if(handle != -1){

           Log.d("", "初始化成功");

           System.out.println(handle);

          }

          String url = "ftp://192.168.18.37/"+path;

          easy.easySetopt(handle, OptValue.CURLOPT_URL, url);

          easy.easySetopt(handle, OptValue.CURLOPT_USERPWD, "ipanel:ipanel123");

//断点续传声明

          easy.easySetopt(handle, OptValue.CURLOPT_RESUME_FROM_LARGE, file.length());

          easy.easySetopt(handle, OptValue.CURLOPT_WRITEFUNCTION, operate);

          result = easy.easyPerform(handle);

          easy.easySetopt(handle, OptValue.CURLOPT_INFILESIZE_LARGE, file.length());

          System.out.println("easyPerform state is "+result);

6.相关参数资料

下列选项的值将被作为长整形使用(在option参数中指定):    
•    CURLOPT_INFILESIZE : 当你上传一个文件到远程站点,这个选项告诉PHP你上传文件的大小。
•    CURLOPT_VERBOSE : 如果你想CURL报告每一件意外的事情,设置这个选项为一个非零值。
•    CURLOPT_HEADER : 如果你想把一个头包含在输出中,设置这个选项为一个非零值。
•    CURLOPT_NOPROGRESS: 如果你不会PHP为CURL传输显示一个进程条,设置这个选项为一个非零值。注意:PHP自动设置这个选项为非零值,你应该仅仅为了调试的目的来改变这个选项。
•    CURLOPT_NOBODY : 如果你不想在输出中包含body部分,设置这个选项为一个非零值。
•    CURLOPT_FAILONERROR : 如果你想让PHP在发生错误(HTTP代码返回大于等于300)时,不显示,设置这个选项为一人非零值。默认行为是返回一个正常页,忽略代码。
•    CURLOPT_UPLOAD: 如果你想让PHP为上传做准备,设置这个选项为一个非零值。
•    CURLOPT_POST : 如果你想PHP去做一个正规的HTTP POST,设置这个选项为一个非零值。这个POST是普通的 application/x-www-from-urlencoded 类型,多数被HTML表单使用。
•    CURLOPT_FTPLISTONLY : 设置这个选项为非零值,PHP将列出FTP的目录名列表。
•    CURLOPT_FTPAPPEND : 设置这个选项为一个非零值,PHP将应用远程文件代替覆盖它。
•    CURLOPT_NETRC : 设置这个选项为一个非零值,PHP将在你的 ~./netrc 文件中查找你要建立连接的远程站点的用户名及密码。
•    CURLOPT_FOLLOWLOCATION : 设置这个选项为一个非零值(象 “Location: “)的头,服务器会把它当做HTTP头的一部分发送(注意这是递归的,PHP将发送形如 “Location: “的头)。
•    CURLOPT_PUT : 设置这个选项为一个非零值去用HTTP上传一个文件。要上传这个文件必须设置CURLOPT_INFILE和CURLOPT_INFILESIZE选项.
•    CURLOPT_MUTE : 设置这个选项为一个非零值,PHP对于CURL函数将完全沉默。
•    CURLOPT_TIMEOUT : 设置一个长整形数,作为最大延续多少秒。
•    CURLOPT_LOW_SPEED_LIMIT: 设置一个长整形数,控制传送多少字节。
•    CURLOPT_LOW_SPEED_TIME : 设置一个长整形数,控制多少秒传送CURLOPT_LOW_SPEED_LIMIT规定的字节数。
•    CURLOPT_RESUME_FROM : 传递一个包含字节偏移地址的长整形参数,(你想转移到的开始表单)。
•    CURLOPT_SSLVERSION: 传递一个包含SSL版本的长参数。默认PHP将被它自己努力的确定,在更多的安全中你必须手工设置。
•    CURLOPT_TIMECONDITION : 传递一个长参数,指定怎么处理CURLOPT_TIMEVALUE参数。你可以设置这个参数为TIMECOND_IFMODSINCE 或 TIMECOND_ISUNMODSINCE。这仅用于HTTP。
•    CURLOPT_TIMEVALUE : 传递一个从1970-1-1开始到现在的秒数。这个时间将被CURLOPT_TIMEVALUE选项作为指定值使用,或被默认TIMECOND_IFMODSINCE使用。

下列选项的值将被作为字符串:
•    CURLOPT_URL: 这是你想用PHP取回的URL地址。你也可以在用curl_init()函数初始化时设置这个选项。
•    CURLOPT_USERPWD : 传递一个形如[username]:[password]风格的字符串,作用PHP去连接。
•    CURLOPT_PROXYUSERPWD : 传递一个形如[username]:[password] 格式的字符串去连接HTTP代理。
•    CURLOPT_RANGE : 传递一个你想指定的范围。它应该是”X-Y”格式,X或Y是被除外的。HTTP传送同样支持几个间隔,用逗句来分隔(X-Y,N-M)。
•    CURLOPT_POSTFIELDS : 传递一个作为HTTP “POST”操作的所有数据的字符串。
•    CURLOPT_REFERER: 在HTTP请求中包含一个”referer”头的字符串。
•    CURLOPT_USERAGENT : 在HTTP请求中包含一个”user-agent”头的字符串。
•    CURLOPT_FTPPORT: 传递一个包含被ftp “POST”指令使用的IP地址。这个POST指令告诉远程服务器去连接我们指定的IP地址。这个字符串可以是一个IP地址,一个主机名,一个网络界面名(在UNIX下),或是‘-’(使用系统默认IP地址)。
•    CURLOPT_COOKIE : 传递一个包含HTTP cookie的头连接。
•    CURLOPT_SSLCERT : 传递一个包含PEM格式证书的字符串。
•    CURLOPT_SSLCERTPASSWD : 传递一个包含使用CURLOPT_SSLCERT证书必需的密码。
•    CURLOPT_COOKIEFILE : 传递一个包含cookie数据的文件的名字的字符串。这个cookie文件可以是Netscape格式,或是堆存在文件中的HTTP风格的头。
•    CURLOPT_CUSTOMREQUEST : 当进行HTTP请求时,传递一个字符被GET或HEAD使用。为进行DELETE或其它操作是有益的,更Pass a string to be used instead of GET or HEAD when doing an HTTP request. This is useful for doing or another, more obscure, HTTP request. 注意: 在确认你的服务器支持命令先不要去这样做。下列的选项要求一个文件描述(通过使用fopen()函数获得):
•    CURLOPT_FILE: 这个文件将是你放置传送的输出文件,默认是STDOUT.
•    CURLOPT_INFILE : 这个文件是你传送过来的输入文件。
•    CURLOPT_WRITEHEADER : 这个文件写有你输出的头部分。
•    CURLOPT_STDERR : 这个文件写有错误而不是stderr。用来获取需要登录的页面的例子,当前做法是每次或许都登录一次,有需要的人再做改进了.

最后附带Easy模块的部分代码:

java接口:

public class Easy {
// create the handle 创建一个easy handle
public int easyInit() {
int res = -1;
res = native_join_curl_easy_init();
if (res == -1) {
Log.d("", "easyInit faild!");
}
return res;
}

……
native int native_join_curl_easy_init();

……

}

jni接口:

joineasy.cpp

……

jint join_curl_easy_init(JNIEnv *env, jobject thiz) {
int res = -1;
res = (int)curl_easy_init();
// LOGE("error is the cleanup res %d" ,res);
// curl_easy_cleanup((void*)res);
if (res != -1) {
LOGE("join_curl_easy_init convert success %d", res);
}
return res;
}
……

static JNINativeMethod g_cls_methods[] = { //
{ "native_join_curl_easy_init", "()I", (void*) join_curl_easy_init }
}

……

注意:在写测试case的时候记得加载动态库

static {
try {
Log.i("","load library");
System.loadLibrary("curl_runtime");//jni部分生成了libcurl_runtime.so动态库
} catch (java.lang.Error e) {
e.printStackTrace();
}
test.apk 依赖 libcurl_runtime.so同时 libcurl_runtime.so 依赖 libcurl.so


   





转载于:https://my.oschina.net/zhoulc/blog/111929

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值