Android JNI:深入分析安卓JNI原理

ANDROID JNI 底层原理详解 JNI(Java Native Interface)即 Java 本地接口,它允许 Java 代码与本地代码(如 C 和 C++)进行交互。在 Android 开发中,JNI 扮演着重要角色,它让 Java 代码(位于 Framework 层)能够调用 NATIVE 层的 C/C++ 代码,以实现高性能计算、访问底层硬件等功能。注册 JNI 方法:将 Java 方法与本地的 C/C++ 函数关联起来。Java 代码调用本地方法:在 Java 代码中调用声明为native的方法。JNI 层查找本地函数。 阅读详情

引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码:

frameworks/base/core/jni/AndroidRuntime.cpp

libcore/luni/src/main/java/java/lang/System.java
libcore/luni/src/main/java/java/lang/Runtime.java
libnativehelper/JNIHelp.cpp
libnativehelper/include/nativehelper/jni.h

frameworks/base/core/java/android/os/MessageQueue.java
frameworks/base/core/jni/android_os_MessageQueue.cpp

frameworks/base/core/java/android/os/Binder.java
frameworks/base/core/jni/android_util_Binder.cpp

frameworks/base/media/java/android/media/MediaPlayer.java
frameworks/base/media/jni/android_media_MediaPlayer.cpp

一、JNI概述

JNI(Java Native Interface,Java本地接口),用于打通Java层与Native(C/C++)层。这不是Android系统所独有的,而是Java所有。众所周知,Java语言是跨平台的语言,而这跨平台的背后都是依靠Java虚拟机,虚拟机采用C/C++编写,适配各个系统,通过JNI为上层Java提供各种服务,保证跨平台性。

相信不少经常使用Java的程序员,享受着其跨平台性,可能全然不知JNI的存在。在Android平台,让JNI大放异彩,为更多的程序员所熟知,往往为了提供效率或者其他功能需求,就需要NDK开发。上一篇文章Linux系统调用(syscall)原理,介绍了打通android上层与底层kernel的枢纽syscall,那么本文的目的则是介绍打通android上层中Java层与Native的纽带JNI。

二、JNI查找方式

Android系统在启动启动过程中,先启动Kernel创建init进程,紧接着由init进程fork第一个横穿Java和C/C++的进程,即Zygote进程。Zygote启动过程中会AndroidRuntime.cpp中的startVm创建虚拟机,VM创建完成后,紧接着调用startReg完成虚拟机中的JNI方法注册。

1. main

 [->App_main.cpp ]


int main(int argc, char* const argv[])
{

	//生成一个AppRuntime对象,把main传递过来的参数传给AppRuntime
    //class AppRuntime : public AndroidRuntime
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ......

    if (zygote) {//见1.1分析
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if 
    ......
}

1.1 start

[-->AndroidRuntime.cpp]

​
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{ 
    ......

    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);
    if (startReg(env) < 0) {//见分析2.1
        ALOGE("Unable to register all android natives\n");
        return;
    }
   ...

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {//ZygoteInit 通过反射找到zygoteInit.main的方法ID。
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {//执行zygoteinit.main方法,zygoteinit类为java类
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
    ......
}

​

2.1 startReg

[–>AndroidRuntime.cpp]

int AndroidRuntime::startReg(JNIEnv* env)
{
    //设置线程创建方法为javaCreateThreadEtc
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    env->PushLocalFrame(200);
    //进程NI方法的注册
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);
    return 0;
}

register_jni_procs(gRegJNI, NELEM(gRegJNI), env)这行代码的作用就是就是循环调用gRegJNI数组成员所对应的方法。

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
            return -1;
        }
    }
    return 0;
}

gRegJNI数组,有100多个成员变量,定义在AndroidRuntime.cpp:

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_os_MessageQueue),
    REG_JNI(register_android_os_Binder),
    ...
};

该数组的每个成员都代表一个类文件的jni映射,其中REG_JNI是一个宏定义,在Zygote中介绍过,该宏的作用就是调用相应的方法。

2.2 如何查找native方法

当大家在看framework层代码时,经常会看到native方法,这是往往需要查看所对应的C++方法在哪个文件,对应哪个方法?下面从一个实例出发带大家如何查看java层方法所对应的native方法位置。

2.2.1 实例(一)

当分析Android消息机制源码,遇到MessageQueue.java中有多个native方法,比如:

 private native void nativePollOnce(long ptr, int timeoutMillis);

步骤1: MessageQueue.java的全限定名为android.os.MessageQueue.java,方法名:android.os.MessageQueue.nativePollOnce(),而相对应的native层方法名只是将点号替换为下划线,可得android_os_MessageQueue_nativePollOnce()。 Tips: nativePollOnce ==> android_os_MessageQueue_nativePollOnce()

步骤2: 有了native方法,那么接下来需要知道该native方法所在那个文件。前面已经介绍过Android系统启动时就已经注册了大量的JNI方法,见AndroidRuntime.cpp的gRegJNI数组。这些注册方法命令方式:

register_[包名]_[类名]

那么MessageQueue.java所定义的jni注册方法名应该是register_android_os_MessageQueue,的确存在于gRegJNI数组,说明这次JNI注册过程是有开机过程完成的。 该方法在AndroidRuntime.cpp申明为extern方法:

extern int register_android_os_MessageQueue(JNIEnv* env);

这些extern方法绝大多数位于/framework/base/core/jni/目录,大多数情况下native文件命名方式:

[包名]_[类名].cpp
[包名]_[类名].h

Tips: MessageQueue.java ==> android_os_MessageQueue.cpp

打开android_os_MessageQueue.cpp文件,搜索android_os_MessageQueue_nativePollOnce方法,这便找到了目标方法:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

到这里完成了一次从Java层方法搜索到所对应的C++方法的过程。

2.2.2 实例(二)

对于native文件命名方式,有时并非[包名]_[类名].cpp,比如Binder.java

Binder.java所对应的native文件:android_util_Binder.cpp

public static final native int getCallingPid();

根据实例(一)方式,找到getCallingPid ==> android_os_Binder_getCallingPid(),并且在AndroidRuntime.cpp中的gRegJNI数组中找到register_android_os_Binder。

按实例(一)方式则native文名应该为android_os_Binder.cpp,可是在/framework/base/core/jni/目录下找不到该文件,这是例外的情况。其实真正的文件名为android_util_Binder.cpp,这就是例外,这一点有些费劲,不明白为何google要如此打破规律的命名。

static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
{
    return IPCThreadState::self()->getCallingPid();
}

有人可能好奇,既然如何遇到打破常规的文件命令,怎么办?这个并不难,首先,可以尝试在/framework/base/core/jni/中搜索,对于binder.java,可以直接搜索binder关键字,其他也类似。如果这里也找不到,可以通过grep全局搜索android_os_Binder_getCallingPid这个方法在哪个文件。

jni存在的常见目录:

  • /framework/base/core/jni/
  • /framework/base/services/core/jni/
  • /framework/base/media/jni/

2.2.3 实例(三)

前面两种都是在Android系统启动之初,便已经注册过JNI所对应的方法。 那么如果程序自己定义的jni方法,该如何查看jni方法所在位置呢?下面以MediaPlayer.java为例,其包名为android.media:

public class MediaPlayer{
    static {
        System.loadLibrary("media_jni");
        native_init();
    }

    private static native final void native_init();
    ...
}

通过static静态代码块中System.loadLibrary方法来加载动态库,库名为media_jni, Android平台则会自动扩展成所对应的libmedia_jni.so库。 接着通过关键字native加在native_init方法之前,便可以在java层直接使用native层方法。

接下来便要查看libmedia_jni.so库定义所在文件,一般都是通过Android.mk文件定义LOCAL_MODULE:= libmedia_jni,可以采用grep或者mgrep来搜索包含libmedia_jni字段的Android.mk所在路径。

搜索可知,libmedia_jni.so位于/frameworks/base/media/jni/Android.mk。用前面实例(一)中的知识来查看相应的文件和方法名分别为:

android_media_MediaPlayer.cpp
android_media_MediaPlayer_native_init()

再然后,你会发现果然在该Android.mk所在目录/frameworks/base/media/jni/中找到android_media_MediaPlayer.cpp文件,并在文件中存在相应的方法:

  static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
    jclass clazz;
    clazz = env->FindClass("android/media/MediaPlayer");
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    ...
}

Tips:MediaPlayer.java中的native_init方法所对应的native方法位于/frameworks/base/media/jni/目录下的android_media_MediaPlayer.cpp文件中的android_media_MediaPlayer_native_init方法。

2.3 小结

JNI作为连接Java世界和C/C++世界的桥梁,很有必要掌握。看完本文,至少能掌握在分析Android源码过程中如何查找native方法。首先要明白native方法名和文件名的命名规律,其次要懂得该如何去搜索代码。 JNI方式注册无非是Android系统启动过程中Zygote注册以及通过System.loadLibrary方式注册,对于系统启动过程注册的,可以通过查询AndroidRuntime.cpp中的gRegJNI是否存在对应的register方法,如果不存在,则大多数情况下是通过LoadLibrary方式来注册。

三、 JNI原理分析

再进一步来分析,Java层与native层方法是如何注册并映射的,继续以MediaPlayer为例。

在文件MediaPlayer.java中调用System.loadLibrary("media_jni")把libmedia_jni.so动态库加载到内存。接下来,以loadLibrary为起点展开JNI注册流程的过程分析。

3.1 loadLibrary

[System.java]

public static void loadLibrary(String libName) {
    //接下来调用Runtime方法
    Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}

[Runtime.java]

void loadLibrary(String libraryName, ClassLoader loader) {
    //loader不会空,则进入该分支
    if (loader != null) {
        //查找库所在路径
        String filename = loader.findLibrary(libraryName);
        if (filename == null) {
            throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                           System.mapLibraryName(libraryName) + "\"");
        }
        //加载库
        String error = doLoad(filename, loader);
        if (error != null) {
            throw new UnsatisfiedLinkError(error);
        }
        return;
    }

    //loader为空,则会进入该分支
    String filename = System.mapLibraryName(libraryName);
    List<String> candidates = new ArrayList<String>();
    String lastError = null;
    for (String directory : mLibPaths) {
        String candidate = directory + filename;
        candidates.add(candidate);
        if (IoUtils.canOpenReadOnly(candidate)) {
             //加载库
            String error = doLoad(candidate, loader);
            if (error == null) {
                return;//加载成功
            }
            lastError = error;
        }
    }
    if (lastError != null) {
        throw new UnsatisfiedLinkError(lastError);
    }
    throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}

真正加载的工作是由doLoad(),该方法内部增加同步锁,保证并发时一致性。

private String doLoad(String name, ClassLoader loader) {
    ...
    synchronized (this) {
        return nativeLoad(name, loader, ldLibraryPath);
    }
}

nativeLoad()这是一个native方法,再进入ART虚拟机java_lang_Runtime.cc,再细讲就要深入剖析虚拟机内部,这里就不再往下深入了,后续博主有空再展开art虚拟机系列的文章,这里直接说结论:

  • 调用dlopen函数,打开一个so文件并创建一个handle;
  • 调用dlsym()函数,查看相应so文件的JNI_OnLoad()函数指针,并执行相应函数。

总之,System.loadLibrary()的作用就是调用相应库中的JNI_OnLoad()方法。接下来说说JNI_OnLoad()过程。

3.2 JNI_OnLoad

[-> android_media_MediaPlayer.cpp]

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    //【见3.3】 注册JNI方法
    if (register_android_media_MediaPlayer(env) < 0) {
        goto bail;
    }
    ...
}

3.3 register_android_media_MediaPlayer

[-> android_media_MediaPlayer.cpp]

static int register_android_media_MediaPlayer(JNIEnv *env) {
    //【见3.4】
    return AndroidRuntime::registerNativeMethods(env,
                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
}

其中gMethods,记录java层和C/C++层方法的一一映射关系。

static JNINativeMethod gMethods[] = {
    {"prepare",      "()V",  (void *)android_media_MediaPlayer_prepare},
    {"_start",       "()V",  (void *)android_media_MediaPlayer_start},
    {"_stop",        "()V",  (void *)android_media_MediaPlayer_stop},
    {"seekTo",       "(I)V", (void *)android_media_MediaPlayer_seekTo},
    {"_release",     "()V",  (void *)android_media_MediaPlayer_release},
    {"native_init",  "()V",  (void *)android_media_MediaPlayer_native_init},
    ...
};

这里涉及到结构体JNINativeMethod,其定义在jni.h文件:

typedef struct {
    const char* name;  //Java层native函数名
    const char* signature; //Java函数签名,记录参数类型和个数,以及返回值类型
    void*       fnPtr; //Native层对应的函数指针
} JNINativeMethod;

关于函数签名signature在下一小节展开说明。

3.4 registerNativeMethods

[-> AndroidRuntime.cpp]

int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    //【见3.5】
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

jniRegisterNativeMethods该方法是由Android JNI帮助类JNIHelp.cpp来完成。

3.5 jniRegisterNativeMethods

[-> JNIHelp.cpp]

extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    scoped_local_ref<jclass> c(env, findClass(env, className));
    if (c.get() == NULL) {
        e->FatalError("");//无法查找native注册方法
    }
    //【见3.6】 调用JNIEnv结构体的成员变量
    if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
        e->FatalError("");//native方法注册失败
    }
    return 0;
}

3.6 RegisterNatives

[-> jni.h]

struct _JNIEnv {
    const struct JNINativeInterface* functions;

    jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods) { return functions->RegisterNatives(this, clazz, methods, nMethods); }
    ...
}

functions是指向JNINativeInterface结构体指针,也就是将调用下面方法:

struct JNINativeInterface {
    jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
    ...
}

再往下深入就到了虚拟机内部吧,这里就不再往下深入了。 总之,这个过程完成了gMethods数组中的方法的映射关系,比如java层的native_init()方法,映射到native层的android_media_MediaPlayer_native_init()方法。

虚拟机相关的变量中有两个非常重要的量JavaVM和JNIEnv:

  • JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
  • JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例,

四、JNI资源

JNINativeMethod结构体中有一个字段为signature(签名),再介绍signature格式之前需要掌握各种数据类型在Java层、Native层以及签名所采用的签名格式。

4.1 数据类型

4.1.1 基本数据类型

Signature格式JavaNative
Bbytejbyte
Ccharjchar
Ddoublejdouble
Ffloatjfloat
Iintjint
Sshortjshort
Jlongjlong
Zbooleanjboolean
Vvoidvoid

4.1.2 数组数据类型

数组简称则是在前面添加[:

Signature格式JavaNative
[Bbyte[]jbyteArray
[Cchar[]jcharArray
[Ddouble[]jdoubleArray
[Ffloat[]jfloatArray
[Iint[]jintArray
[Sshort[]jshortArray
[Jlong[]jlongArray
[Zboolean[]jbooleanArray

4.1.3 复杂数据类型

对象类型简称:L+classname +;

Signature格式JavaNative
Ljava/lang/String;Stringjstring
L+classname +;所有对象jobject
[L+classname +;Object[]jobjectArray
Ljava.lang.Class;Classjclass
Ljava.lang.Throwable;Throwablejthrowable

4.1.4 Signature

有了前面的铺垫,那么再来通过实例说说函数签名: (输入参数...)返回值参数,这里用到的便是前面介绍的Signature格式。

Java函数对应的签名
void foo()()V
float foo(int i)(I)F
long foo(int[] i)([I)J
double foo(Class c)(Ljava/lang/Class;)D
boolean foo(int[] i,String s)([ILjava/lang/String;)Z
String foo(int i)(I)Ljava/lang/String;

4.2 其他

(一)垃圾回收 对于Java开发人员来说无需关系垃圾回收,完全由虚拟机GC来负责垃圾回收,而对于JNI开发人员,对于内存释放需要谨慎处理,需要的时候申请,使用完记得释放内容,以免发生内存泄露。在JNI提供了三种Reference类型,Local Reference(本地引用), Global Reference(全局引用), Weak Global Reference(全局弱引用)。其中Global Reference如果不主动释放,则一直不会释放;对于其他两个类型的引用都是释放的可能性,那是不是意味着不需要手动释放呢?答案是否定的,不管是这三种类型的那种引用,都尽可能在某个内存不再需要时,立即释放,这对系统更为安全可靠,以减少不可预知的性能与稳定性问题。

另外,ART虚拟机在GC算法有所优化,为了减少内存碎片化问题,在GC之后有可能会移动对象内存的位置,对于Java层程序并没有影响,但是对于JNI程序可要小心了,对于通过指针来直接访问内存对象是,Dalvik能正确运行的程序,ART下未必能正常运行。

(二)异常处理 Java层出现异常,虚拟机会直接抛出异常,这是需要try..catch或者继续往外throw。但是对于JNI出现异常时,即执行到JNIEnv中某个函数异常时,并不会立即抛出异常来中断程序的执行,还可以继续执行内存之类的清理工作,直到返回到Java层时才会抛出相应的异常。

另外,Dalvik虚拟机有些情况下JNI函数出错可能返回NULL,但ART虚拟机在出错时更多的是抛出异常。这样导致的问题就可能是在Dalvik版本能正常运行的程序,在ART虚拟机上由于没有正确处理异常而崩溃。

总结

本文主要通过实例,基于Android 6.0源码来分析JNI原理,讲述JNI核心功能:

  • 介绍了如何查找JNI方法,让大家明白如何从Java层跳转到Native层;
  • 分析了JNI函数注册流程,进一步加深对JNI的理解;
  • 列举Java与native以及函数签名方式。
  •  
[Android源码解析]Eventloop在jni层的分析 3 eventloop在jni层的详细分析          Eventloop在整个bluetooth的jni层和bluez之间的交互,以及jni和framework层之间的交互过程中有着举足轻重的作用。所以,本文仍然需要花费一定的笔墨来分析它,当然由于它更像是一个桥梁,我们的分析有可能就不是那么的深入了,会从比较宏观地角度来看待这个东西的作用。          当然,一切的一切还是要 阅读详情

相关推荐

安卓逆向工程入门:APK结构与动态分析技术

Android逆向工程是分析应用代码与行为的关键技术,广泛应用于安全研究、漏洞挖掘等领域。其核心在于解析APK文件结构,包括AndroidManifest.xml、classes.dex等组件,并通过JADX等工具实现静态反编译。动态分析则依赖Frida等框架进行运行时Hook和脱壳处理,特别适用于对抗加固保护。在移动安全领域,掌握逆向技术能有效检测应用潜在风险,分析恶意软件行为模式。本文以APK文件解析为切入点,详解如何结合JADX静态分析与Frida动态调试技术,实现对Android应用从资源文件到Na

weixin_34082695的博客 1488

Android JNI详解,让你彻底了解JNI (上)

Android开发中,随着对移动程序的安全性、性能等方面的重视,JNI技术也越发重要。如今,多数企业在招聘中、高级程序员时,基本上都要求熟悉JNI开发,所以,掌握JNI技术,也是我们迈进心仪企业的必备条件。本套课程共分为三部分,当前为第一部分,主要包括内容:1、JNI名词释义2、JNI环境搭建3、Native项目解析4、编译工具使用5、边学边实战,从易到难,循序渐进适合人群:1、有一定Android基础,零JNI基础的学员;2、接触过JNI,但却不甚了解的学员;3、使用过JNI,希望解其原理的学员;后面两部分课程,将带领大家深入了解JNI交互原理,Native实战开发,让你真正掌握JNI技术。

JNI的本质解析:Android Framework视角下的Java-Native衔接机制

本文基于Android 14源码深入分析JNI机制,从Android Framework视角解析Java-Native衔接原理。主要内容包括:JNI的双轨制注册机制(静态与动态注册),核心数据结构(JNIEnv与JavaVM)的实现原理,以及完整的调用链路剖析。文章通过MessageQueue的JNI实现案例,展示Android Framework如何利用JNI实现Java层与Native层的高效交互,并提供了性能优化建议。分析表明,JNI作为Android系统架构的关键桥梁,其设计充分考虑了性能、安全性

保持专注,保持热爱 1211

Android JNI详解

Android使用JNI的原理解析

疯过无痕的博客 8504

Matlab中eig内置函数转为C语言

本文记录一下如何将MATLAB中的eig函数转为C语言,即通过C语言求解矩阵的特征值与特征向量。[V,D]=eig(Rx)中V为矩阵Rx的特征向量,D为矩阵的特征值。其中Rx维度为12*12的复矩阵。 首先在MATLAB端创建入口函数保存为eigvalue.m: 打开MATLAB Coder,添加入口函数: 3.在定义输入类型窗口中,需要添加输入数据来训练...

u011441044的博客 3516

【MagicDrive环境配置】新手配俩星期版

1.创建一个新的环境conda create -n newdrive python==3.82.激活该环境conda activate newdrive3.下载MagicDrive源码 git clone --recursive https://github.com/cure-lab/MagicDrive.git,如果出现时间超时八成是网的问题,直接自己下载解压就好3.我的cuda版本是11.7,但11.7导致后续安装有冲突,因此进入。

m0_46261292的博客 2203

Android之 JNI详解

Android JNI详解

qq_29848853的博客 6443

别再只盯着MES了!半导体/面板厂CIM系统全家桶(EAP/YMS/SPC)保姆级入门指南

本文深入解析半导体制造中的CIM系统,涵盖EAP、YMS和SPC等关键子系统的协同工作。通过实战案例和技术细节,展示如何利用CIM系统提升生产效率、良率管理和设备维护,为半导体/面板厂提供全面的实施指南和优化策略。

weixin_33693070的博客 464

Android中的JNI

JNI(Java Native Interface)是Java语言中用于与本地C/C++代码交互的特性,允许Java代码调用本地代码。JNI全称Java Native Interface,是Java语言与本地C/C++代码交互的标准方法。通过JNI,Java代码可以调用本地方法,这些方法以C或C++编写。这种机制使得Java程序能够充分利用底层系统功能,如图形处理、音频处理等。JNI的主要优点包括扩展Java程序的功能、提高性能以及保护代码安全。JNI也是Android的重要组成部分。

qq_55893245的博客 3735

第五章-AllData集成DataSophon作为数据平台功能

🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。全新会员商业版 X 微信群:https://docs.qq.com/doc/DVHlkSEtvVXVCdEFo

AllDataDC的博客 1638

SAP-ABAP:SAP HANA性能神器FDA:是极速查询的引擎,也是CPU过载的元凶

在SAP S/4HANA的世界里,有一个被ABAP开发者们既爱又怕的特性——Fast Data Access(FDA),它能将性能提升百倍,也能让CPU瞬间飙升。本文将带您全面解剖这一核心机制。

baidu_35680696的博客 849

Android NDK编译原理

前言 做Android APP开发,可以基本不用care java的编译过程,java的依赖关系,并且现在Android studio也已经支持了cmake开发,也就是说大部分情况下,就不用关心系统是如何编译的,不过对于想要用以一些第三方库,这就需要对整个编译框架有所了解了,并且这个东东还是很简单的。 正文 Android APP jni 编译控制有四种主要的编译控制系统, Andr...

白嫩豆腐 1377

[QMT量化交易小白入门]-二十八、有了回测年化收益好的策略,如何在QMT量化实盘呢(附python代码解析)

本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步,自己淋过雨了,希望大家都有一把伞。

木头大左的博客 2927

深入探究 Android WebView 的 H5 定位机制与最佳实践

深入剖析了 Android WebView 中 H5 定位的完整机制。结合 Chromium 源码解析了 onGeolocationPermissionsShowPrompt 回调中 allow 与 retain 参数的行为差异。随后追踪了从 callback.invoke 到前端 success 回调的完整数据链路,揭示了引擎如何绕过 App 直接调用系统定位服务。最后,针对第三方定位 SDK 集成需求,提供了 JSBridge 和 Hook 劫持两种实践方案

喻志强的博客 1471

Hermes接入IMA知识库全攻略:将知识库装进你的Hermes智能体

本文详细介绍了如何将IMA知识库功能接入Hermes Agent平台,实现一站式笔记管理和知识库操作。主要内容包括:获取IMA访问凭证、下载技能包、配置凭据(支持文件和环境变量两种方式)、验证配置有效性,以及实际应用演示(查询知识库列表、创建笔记并添加到知识库)。文章还提供了常见问题解决方案,如认证失败处理、版本更新等。通过该集成,用户可在Hermes中直接调用IMA的笔记创建、知识库管理等强大功能,提升工作效率。

小石头的专栏 641

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Android16 蓝牙 APEX 模块调试替换总结

从 Android 10 开始,Google 引入了 APEX(Android Pony EXpress)格式用于系统组件模块化更新。 在 Android 16 中,蓝牙模块(Bluetooth APK)已经被完全打包到 `com.android.bt` 这个 APEX 模块中,而不是像旧版本那样直接放在 `/system/priv-app/` 下。 这导致了一个开发调试的痛点: **修改蓝牙代码后,无法像以前那样直接 `adb push` 替换 APK 文件**,直接安装 APK 甚至会导致系统启动

wenzhi的博客 4129

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

【安卓逆向系列】安卓的启动流程

【最后步骤很关键,重新启动init的过程也讲加载的权限进一步降低,在整个第一阶段都是以权限极高的kernel执行,转化为init域后权限降低,更为安全,这个就是SELinux安全策略的作用】主要的功能是设置了虚拟机的参数,利用了很多的parseRuntimeOption函数(对于不同的软硬件环境,函数的参数往往需要调整、优化,从而使系统达到最佳性能)所有的app进程都是由zygote进程fork而来的。进程(翻译为受精卵,此进程也称为安卓的应用进程孵化器,是所有应用进程的父进程)对于此孵化进程,他会加载。

xianyu_yuan的博客 1318

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

深入分析Android SensorService

四个多月前就想写这个了文章了,一直没空写,今天把有空就写一下吧。本篇文章主要内容如下 目录 1,Android 各模块关系基本流程图 2, SensorService启动 3, 应用注册一个sensor流程 4, SensorService如何将sensor数据给到应用 5,待机后SensorService行为 6,融合sensor(SensorFusion) 7,...

goodnight1994的博客 1万+

Unity安卓打包RT-Voice无声?配置Google TTS引擎全流程解决

语音合成(TTS)技术是实现人机语音交互的核心,其原理是将文本信息转换为可理解的语音信号。在移动应用开发中,TTS技术能极大提升用户体验与信息可达性。然而,安卓平台的碎片化问题,尤其是系统TTS引擎的缺失与不一致,常导致语音功能在真机环境失效,成为开发中的典型痛点。本文聚焦Unity引擎下的AR应用开发场景,针对RT-Voice插件在安卓打包后无声的普遍问题,深入剖析了其根源在于对设备预装TTS引擎的依赖。文章提供了以集成Google TTS服务为核心的工程化解决方案,通过创建Android原生插件、配置G

389

深入浅出 - Android系统移植与平台开发(十二)- Android JNI机制

第五章、JNI机制4.1 JNI概述 由前面基础知识可知,Android的应用层由Java语言编写,Framework框架层则是由Java代码与C/C++语言实现,之所以由两种不同的语言组合开发框架层,是因为Java代码是与硬件环境彻底“隔离”的跨平台语言,Java代码无法直接操作硬件。比如:Android系统支持大量传感器,Java运行在虚拟机中,无法直接得到传感器数据,而Android系统基于

mr_raptor的专栏 1万+

情景方式深入分析Android的源代码《深入理解Android》

内容简介 · · · · · · 深入理解Android 《深入理解Android:卷I》是一本以情景方式对Android的源代码进行深入分析的书。内容广泛,以对Framework层的分析为主,兼顾Native层和Application层;分析深入,每一部分源代码的分析都力求透彻;针对性强,注重实际应用开发需求,书中所涵盖的知识点都是Andr...

每天都有新发现 268

[Android] HAL(硬件抽象层) | JNI 桥接层

Java 程序调用 C/C++ 编写的本地代码(Native Code)本地代码访问 Java 对象和方法性能优化:硬件控制逻辑用 C/C++ 实现,执行效率更高代码复用:可以直接使用现有的 C/C++ 硬件控制库闭源保护:编译为.so动态链接库,不暴露源代码接口标准化:所有 HAL 模块都继承自和动态加载:运行时根据需要加载对应的.so库版本管理:通过和管理接口兼容性多实例支持:同一硬件可以有多个实现(如Android HAL 是一个精妙的设计,它在开源理念和商业利益之间找到了平衡点。

No Game No Life 2145

Android JNI开发环境搭建及so生成、测试、加密等参考链接

Android -so加密网址 1.https://blog.youkuaiyun.com/feibabeibei_beibei/article/details/51498285 2.https://blog.youkuaiyun.com/linuxheik/article/details/49202243 3.https://bbs.pediy.com/thread-191649.htm 4.https://b...

limingmcu的专栏 246

moonlight-android架构设计与实现原理:从JNI到Java的完整技术栈

moonlight-android作为一款开源的GameStream客户端,采用了从JNI到Java的多层次技术架构,实现了高性能的游戏流传输体验。本文将深入剖析其架构设计与实现原理,帮助开发者全面理解这一技术栈的构建与运作机制。 ## 整体架构概览 moonlight-android的架构采用了分层设计,主要包含Java应用层、JNI桥接层和C核心层。这种架构设计既充分利用了Java在And

gitblog_01048的博客 718

Android安卓书籍推荐《Android驱动开发与移植实战详解》下载

百度云下载地址:点我 Android凭借其开源性、优异的用户体验和极为方便的开发方式,赢得了广大用户和开发者的青睐,目前已经发展成为市场占有率很高的智能手机操作系统。  《Android驱动开发与移植实战详解》分为18章,依次讲解了Android系统的基本知识, Linux内核的基本知识,分析了Android系统的源码,深入分析HAL层的基本知识,GoldFish下的驱动、MSM内核和驱动、OM...

weixin_30367873的博客 2369
上一篇: Android - Binder与Service总结
下一篇: 安卓ServiceManager启动:彻底理解ServiceManager启动流程,这一篇就够了
kevin@1024
博客等级 码龄11年 677粉丝 95原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kevin@1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值