【我的Android进阶之旅】Android调用JNI出错 java.lang.UnsatisfiedLinkError: No implementation found for的解决方法

简介: 错误描述 今天使用第三方的so库时候,调用JNI方法时出现了错误。报错如下所示:11-01 16:39:20.979 4669-4669/com.

错误描述

今天使用第三方的so库时候,调用JNI方法时出现了错误。报错如下所示:

11-01 16:39:20.979 4669-4669/com.netease.xtc.cloudmusic E/art: No implementation found for void com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(android.content.Context) (tried Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit and Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit__Landroid_content_Context_2)
11-01 16:39:20.980 4669-4669/com.netease.xtc.cloudmusic D/AndroidRuntime: Shutting down VM
11-01 16:39:20.984 4669-4669/com.netease.xtc.cloudmusic E/AndroidRuntime: FATAL EXCEPTION: main
                                                                          Process: com.netease.xtc.cloudmusic, PID: 4669
                                                                          java.lang.UnsatisfiedLinkError: No implementation found for void com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(android.content.Context) (tried Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit and Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit__Landroid_content_Context_2)
                                                                              at com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(Native Method)
                                                                              at com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.<clinit>(NeteaseMusicUtils.java:11)
                                                                              at com.netease.xtc.cloudmusic.MainActivity$1.onClick(MainActivity.java:26)
                                                                              at android.view.View.performClick(View.java:5277)
                                                                              at android.view.View$PerformClick.run(View.java:21704)
                                                                              at android.os.Handler.handleCallback(Handler.java:815)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:104)
                                                                              at android.os.Looper.loop(Looper.java:207)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5905)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:749)

在MainActivity中,尝试调用NeteaseMusicUtils类封装好的JNI方法,代码如下所示:

package com.netease.xtc.cloudmusic;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;

import com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                String params = NeteaseMusicUtils.getUrlParameters("/search","{\"keyword\":\"周杰伦\",\"limit\":10,\"offset\":0}");

                Snackbar.make(view, "params = " + params , Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

}

而com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils代码如下:

package com.netease.xtc.cloudmusic.utils;

import android.content.Context;

import com.netease.xtc.cloudmusic.app.NeteaseMusicApplication;

public class NeteaseMusicUtils {
    static {
        //加载库文件
        System.loadLibrary("poison");
        nativeInit(NeteaseMusicApplication.getContext());
    }

    /**
     * 初始化,必须
     * @param context
     */
    public native static void nativeInit(Context context);

    /**
     * 获取请求中"params"的值
     * 例:如果调用搜索接口,搜索keyword=周杰伦 limit=10 offset=0
     * params=getUrlParameters("/search","{\"keyword\":\"周杰伦\",\"limit\":10,\"offset\":0}\")
     * @param requestUri 接口说明中的uri
     * @param paramJson 接口说明中的参数json表达,如果为空请传"{}"
     * @return
     */
    public native static String getUrlParameters(String requestUri, String paramJson);
}

其中so库位置如下所示:
这里写图片描述

错误分析

再一次查看下错误日志,如下所示:

11-01 16:47:53.363 11117-11117/com.netease.xtc.cloudmusic E/art: No implementation found for void com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(android.content.Context) (tried Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit and Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit__Landroid_content_Context_2)
11-01 16:47:53.364 11117-11117/com.netease.xtc.cloudmusic D/AndroidRuntime: Shutting down VM
11-01 16:47:53.365 11117-11131/com.netease.xtc.cloudmusic I/[MALI][Gralloc]: [+]r_hnd(0xf24b3960), client(36), share_fd(44)
11-01 16:47:53.373 11117-11117/com.netease.xtc.cloudmusic E/AndroidRuntime: FATAL EXCEPTION: main
                                                                            Process: com.netease.xtc.cloudmusic, PID: 11117
                                                                            java.lang.UnsatisfiedLinkError: No implementation found for void com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(android.content.Context) (tried Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit and Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit__Landroid_content_Context_2)
                                                                                at com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(Native Method)
                                                                                at com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.<clinit>(NeteaseMusicUtils.java:11)
                                                                                at com.netease.xtc.cloudmusic.MainActivity$1.onClick(MainActivity.java:26)
                                                                                at android.view.View.performClick(View.java:5277)
                                                                                at android.view.View$PerformClick.run(View.java:21704)
                                                                                at android.os.Handler.handleCallback(Handler.java:815)
                                                                                at android.os.Handler.dispatchMessage(Handler.java:104)
                                                                                at android.os.Looper.loop(Looper.java:207)
                                                                                at android.app.ActivityThread.main(ActivityThread.java:5905)
                                                                                at java.lang.reflect.Method.invoke(Native Method)
                                                                                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
                                                                                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:749)

错误日志提示我们没有 Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit 方法的实现,如下所示:

 java.lang.UnsatisfiedLinkError: No implementation found for void com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(android.content.Context) (tried Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit and Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit__Landroid_content_Context_2)

这里写图片描述

然后我去咨询了一下第三方提供so库的工作人员,回复说NeteaseMusicUtils类的包名一定要是com.netease.cloudmusic.utils。原因是JNI接口Java_com_netease_xtc_cloudmusic_utils_NeteaseMusicUtils_nativeInit中,com.netease.xtc.cloudmusic.utils代表的是package nameNeteaseMusicUtils则是class name
而第三方提供so库的工作人员的c文件的定义JNI接口为Java_com_netease_cloudmusic_utils_NeteaseMusicUtils_nativeInit,即:package name必须为:com.netease.cloudmusic.utils,而class name必须为 NeteaseMusicUtils
也就是说,我们.so中函数声明涉及到的package name和class name与调用它的package name和class name不符。因此我们要改变我们工程中的package name和class name。使其与.so文件中函数签名提示的一致,在这个类中加入native方法的声明。

错误解决方法

好吧,我新建一个包名为com.netease.cloudmusic.utils,并把NeteaseMusicUtils类移到该包名地下。

未移动NeteaseMusicUtils到com.netease.cloudmusic.utils包之前
这里写图片描述

移动NeteaseMusicUtils到com.netease.cloudmusic.utils包之后
这里写图片描述

然后重新编译,成功运行。

JNI的命名规则

这里顺便说一下JNI的命名规则,对于传统的JNI编程来说,JNI方法跟Java类方法的名称之间有一定的对应关系,要遵循一定的命名规则,如下所示:

  1. 前缀: Java_
  2. 类的全限定名,用下划线进行分隔(_):com_oyp_jni_JniTest
  3. 方法名:getTestString
  4. JNI函数指定第一个参数: JNIEnv *
  5. JNI函数指定第二个参数: jobject
  6. 实际Java参数: jstring, jint ….

所以对于在Java类 com.oyp.jni.JniTest类的一个方法:

public native String getTestString (String oyp);

其对应的jni层的方法如下:

jstring Java_com_oyp_jni_JniTest_getTestString(JNIEnv * e, jobject clazz, jstring oyp);

如果不这样命名,当把动态库加载进DVM的时候,通过JNIEnv *指针去查找Java Native方法对应的JNI方法的时候,就会找不到了。
注意,我们也可以利用函数注册的方法,将Java层的方法名跟JNI层的方法名的对应关系保存起来,注册到DVM中,就不需要这样的命名规范了。

JNI 数据类型

我们知道Java的数据类型是跟C/C++的数据类型是不一样的,而JNI是处于Java和Native本地库(大部分是用C/C++写的)中间的一层,JNI对于两种不同的数据类型之间必须做一种转换,所以在JNI跟Java之间就会有数据类型的对应关系。 在JNI中,提供了以下各种数据类型,可以分为原生类型和引用类型: 对于原生类型有:jchar, jbyte, jshort, jint, jlong, jfloat, jdouble, jboolean,其与java端的数据类型对应如下表:

java jni
char jchar
byte jbyte
short jshort
int jint
long jlong
float jfloat
double jdouble
boolean jboolean

对于引用类型则有:jobject, jstring, jthrowable, jclass, jarray, 以及继承于jarray,对应于其原生类型的8种jarray和jobjectarray。

这里写图片描述


作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/ouyang_peng/article/details/52973274

这里写图片描述

相关文章
|
4月前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
336 1
|
3月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
169 1
|
3月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
74 11
|
3月前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
121 11
|
4月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
4月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
87 1
|
4月前
|
开发工具 Android开发
Flutter: Android SDK not found at this location,Android Studio not found at xxx
Flutter: Android SDK not found at this location,Android Studio not found at xxx
169 2
|
4月前
|
Android开发
Cannot create android app from an archive...containing both DEX and Java-bytecode content
Cannot create android app from an archive...containing both DEX and Java-bytecode content
48 2
|
4月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
657 1
|
4月前
|
IDE Java Linux
探索安卓开发:从基础到进阶的旅程Java中的异常处理:从基础到高级
【8月更文挑战第30天】在这个数字时代,移动应用已经成为我们日常生活中不可或缺的一部分。安卓系统由于其开放性和灵活性,成为了开发者的首选平台之一。本文将带领读者踏上一段从零开始的安卓开发之旅,通过深入浅出的方式介绍安卓开发的基础知识、核心概念以及进阶技巧。我们将一起构建一个简单的安卓应用,并探讨如何优化代码以提高性能和应用的用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识和启发。