为什么写这个

最近项目需要在盒子上实现内容的加解密,方案是使用机顶盒内部主芯片加解密算法对存储在内容分区的资源进行加密,然后由机顶盒平台提供本地库的方式给应用使用。所以也就有了这个事情。一般情况下,完全可以由C层代码直接实现和封装JNI本地接口然后打成so文件,然后提供给应用开发者使用。
今天要说的情况基于这样的场景:
底层代码实现者已经将C代码编译成so库,然后直接提供给上层开发者使用,这样他们便无需考虑适配不同上层使用者的具体接口。更可能是,别人只负责把本地代码实现了,然后提供一个so,具体封装由上层开发者自己完成的场景。

怎么做

首先,我们需要有知道两个:
①库文件so,
②库中自己需要调用的功能函数。
这里我的库文件是libminzip.so; 调用的接口函数如下:
bool extractZip(const char* zipFile, const char* destPath, bool zipCurrentPath, bool crypt)
这个函数实现了文件解压的功能。知道这些,那么我们就可以依据这个来进行封装接口然后提供给app调用了。

这里我使用Eclipse+NDK的方式来做:

代码目录如下:

android 组件封装 安卓封装源码_android

step1

可以在项目目录下新建jni目录,把我们已经有的libminzip.so文件放到prebuilt目录下。

step2

在preduilt目录下创建Android.mk文件:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := minzip
LOCAL_SRC_FILES := libminzip.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(PREBUILT_SHARED_LIBRARY)

step3

在java代码中创建本地接口类:

package com.tfxiaozi.contentupdater.jni;

/**
 * Created by dongqiang 2016/11/18.
 */

public class UnZipAndEncrypt {
    static {
        System.loadLibrary("minzip");
        System.loadLibrary("unzipandencrypt");
    }
    private native boolean unzipAndEncypt(String srcPath, String destPath, boolean zipCurrentPath, boolean isCrypted);

    public static void onUnzipProcess(String fileName, float progress) {
    }
}

step4

编写jni文件

#include <jni.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include <android/log.h>
#define LOG_TAG "UNZIP_AND_ENCRYPT"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

char* Jstring2CStr(JNIEnv* env, jstring jstr) {
     char* rtn = NULL;
     jclass clsstring = (*env)->FindClass(env, "java/lang/String");
     jstring strencode = (*env)->NewStringUTF(env, "UTF-8");
     jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
     jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode);
     jsize alen = (*env)->GetArrayLength(env, barr);
     jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
     if(alen > 0) {
      rtn = (char*)malloc(alen+1);         //"\0"
      memcpy(rtn, ba, alen);
      rtn[alen]=0;
     }
     (*env)->ReleaseByteArrayElements(env, barr, ba, 0);
     return rtn;
}

jboolean native_unzip_and_encrypt(JNIEnv *env, jobject jobj, jstring zipFile,
    jstring destPath, jboolean unzipCurrentPath, jboolean isCrypted) {
    const char* czipFile = (const char*)Jstring2CStr(env, zipFile);
    const char* cdestPath = (const char*)Jstring2CStr(env, destPath);
    return extractZip(czipFile, cdestPath, unzipCurrentPath, isCrypted);
}

static JNINativeMethod gMethods[] = {
    {"unzipAndEncypt", "(Ljava/lang/String;Ljava/lang/String;ZZ)Z", (void*) native_unzip_and_encrypt}
};

static const char* const gClassUnZipAndEncrypt = "com/tfxiaozi/contentupdater/jni/UnZipAndEncrypt";

//注册native方法到java中
static int registerNativeMethods(JNIEnv* env, const char* className,
                                JNINativeMethod* gMethods, int numMethods) {
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods,numMethods) < 0){
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

static int register_unZipAndEncrypt(JNIEnv* env) {
    return registerNativeMethods(env, gClassUnZipAndEncrypt,
                                     gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
}

jint JNI_OnLoad(JavaVM *vm,void *reserved){
    JNIEnv *env=NULL;
    jint result=-1;
    if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_4)!=JNI_OK){
        return -1;
    }
    assert(env !=NULL);
    if(register_unZipAndEncrypt(env)<0){
        return -1;
    }
    return JNI_VERSION_1_4;
}

step5

编写jni目录下的Android.mk文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES :=libminzip
LOCAL_MODULE    := unzipandencrypt
LOCAL_SRC_FILES := unzip_and_encrypt.c  

LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/prebuilt/Android.mk

编译

前面说了,使用的ndk,那么需要配置Ndk目录到系统环境变量中,这里就不赘述了。上个图吧:

android 组件封装 安卓封装源码_jni_02


编译通过,可以在项目目录中看到生成的文件:

android 组件封装 安卓封装源码_android_03


把这两个so加入到工程的lib目录下就可以使用了,接口已经在前面的java代码中定义,需要同时加载两个库文件:

System.loadLibrary(“minzip”);

System.loadLibrary(“unzipandencrypt”);

细心的小伙伴会发现上面的jni代码有字符串没有释放资源。

OK,到这里就结束了~~~