Ijkplayer(二)

本文详述了IJKPlayer中JNI的应用,通过JNI4Android实现Java与Native层交互,重点介绍了Parcel类的JNI封装过程,展示了如何利用JNI4Android生成C语言接口,以便在Native层调用Android系统的Parcel类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要介绍ijkplayer中的jni。Ijkplayer的native层代码是脱离android源码编译的,所以当native层要用到android的Bundle,AudioTrack等类时,需要从native层调用java接口。这里就运用到了jni4android。jni4android是bilibili的一个开源工程。代码路径是https://github.com/Bilibili/jni4android。它的作用是根据java伪代码生成对应的c语言代码,这样就可以直接通过调用c语言接口而调用到对应java类的接口。下面以Parcel这类为例。Parcel这个类Ijkplayer里面没有,如果项目要用到,可以如下操作。

1、先定义Parcel.java的伪代码。如下:

package android.os;

@SimpleCClassName
public final class Parcel {
    
    public static Parcel obtain();
    
    public final void recycle();
    
    public final int dataSize();
    
    public final int dataAvail();
    
    public final int dataPosition();
    
    public final int dataCapacity();
    
    public final void setDataSize(int size);
    
    public final void setDataPosition(int pos);
    
    public final void setDataCapacity(int size);
    
    public final void writeInt(int val);
    
    public final void writeLong(long val);
    
    public final void writeFloat(float val);
    
    public final void writeDouble(double val);
    
    public final void writeString(String val);
    
    public final int readInt();
    
    public final long readLong();
    
    public final float readFloat();
    
    public final double readDouble();
    
    public final String readString();    
}

2、执行j4a -c Parcel.java,就会生成四个文件:Parcel.c,Parcel.h,Parcel.include.j4a,Parcel.loader.j4a。其中,Parcel.c,Parcel.h就是我们native code要用到的接口。这里分析一下Parcel.c。它通过jni实现了Parcel.java里面的所有接口,调用它里面的c接口就和java层调用Parcel类的api一样。它里面最重要的函数如下:

int J4A_loadClass__J4AC_android_os_Parcel(JNIEnv *env)
{
    int         ret                   = -1;
    const char *J4A_UNUSED(name)      = NULL;
    const char *J4A_UNUSED(sign)      = NULL;
    jclass      J4A_UNUSED(class_id)  = NULL;
    int         J4A_UNUSED(api_level) = 0;

    if (class_J4AC_android_os_Parcel.id != NULL)
        return 0;

    sign = "android/os/Parcel";
    class_J4AC_android_os_Parcel.id = J4A_FindClass__asGlobalRef__catchAll(env, sign);
    if (class_J4AC_android_os_Parcel.id == NULL)
        goto fail;

    class_id = class_J4AC_android_os_Parcel.id;
    name     = "obtain";
    sign     = "()Landroid/os/Parcel;";
    class_J4AC_android_os_Parcel.method_obtain = J4A_GetStaticMethodID__catchAll(env, class_id, name, sign);
    if (class_J4AC_android_os_Parcel.method_obtain == NULL)
        goto fail;

    class_id = class_J4AC_android_os_Parcel.id;
    name     = "recycle";
    sign     = "()V";
    class_J4AC_android_os_Parcel.method_recycle = J4A_GetMethodID__catchAll(env, class_id, name, sign);
    if (class_J4AC_android_os_Parcel.method_recycle == NULL)
        goto fail;

    class_id = class_J4AC_android_os_Parcel.id;
    name     = "dataSize";
    sign     = "()I";
    class_J4AC_android_os_Parcel.method_dataSize = J4A_GetMethodID__catchAll(env, class_id, name, sign);
    if (class_J4AC_android_os_Parcel.method_dataSize == NULL)
        goto fail;

    class_id = class_J4AC_android_os_Parcel.id;
    name     = "dataAvail";
    sign     = "()I";
    class_J4AC_android_os_Parcel.method_dataAvail = J4A_GetMethodID__catchAll(env, class_id, name, sign);
    if (class_J4AC_android_os_Parcel.method_dataAvail == NULL)
        goto fail;

    class_id = class_J4AC_android_os_Parcel.id;
    name     = "dataPosition";
    sign     = "()I";
    class_J4AC_android_os_Parcel.method_dataPosition = J4A_GetMethodID__catchAll(env, class_id, name, sign);
    if (class_J4AC_android_os_Parcel.method_dataPosition == NULL)
        goto fail;

   ......此处省略剩余代码。

可以看出J4A_loadClass__J4AC_android_os_Parcel函数主要就是生成Parcel类的各个methodId并保存下来,供各个c接口使用。所以使用Parcel.c前必须要先执行J4A_loadClass__J4AC_android_os_Parcel。而这个函数是通过J4A_LOAD_CLASS这个宏来执行的。J4A_LOAD_CLASS定义如下:

#define J4A_LOAD_CLASS(class__) \
    do { \
        ret = J4A_loadClass__J4AC_##class__(env); \
        if (ret) \
            goto fail; \
    } while (0)

所以还需要在j4a_allclasses.loader.h里面添加J4A_LOAD_CLASS(android_os_Parcel);这行代码,这样就能在native层使用Parcel类了。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值