解决jni链接时找不到函数的问题

用jni调用库函数时,经常会碰到link的错误,具体出错信息如下:
08-07 01:42:06.490: E/AndroidRuntime(1665): java.lang.UnsatisfiedLinkError: xxxx

核对后发现函数名称并没有错。

这个问题有几种可能,最根本的解决方法是把so的内容dump出来。具体的方法可以参考 http://stackoverflow.com/questions/34732/how-do-i-list-the-symbols-in-a-so-file

我的文件是elf格式,执行如下的命令:

readelf -Ws xxxx.so >> test.log

so这个库导出的函数就在test.log里了,你可以在log找找看,如果没发现你要导出的函数,
大概有两种可能,第一个是JNIEXPORT没起到作用,第二个是你的函数名或者路径由错误。

附上我的test.log

Symbol table '.dynsym' contains 79 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000fa8     0 SECTION LOCAL  DEFAULT    7
     2: 0000aaa0     0 SECTION LOCAL  DEFAULT   15
     3: 00002278    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_ForcedUnwind
     4: 00001c7c   164 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_RaiseException
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND ioctl
     6: 00000fdd    60 FUNC    GLOBAL DEFAULT    7 Java_com_android_ralmes_GlobalProcess_read
     7: 00001065    56 FUNC    GLOBAL DEFAULT    7 Java_com_android_ralmes_GlobalProcess_isGpioTriggerDeviceExist
     8: 00001019    76 FUNC    GLOBAL DEFAULT    7 Java_com_android_ralmes_GlobalProcess_write
     9: 00002134     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP
    10: 00002254    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Resume_or_Rethrow
    11: 0000293c     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_end
    12: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __stack_chk_guard
    13: 000017d4     8 FUNC    GLOBAL DEFAULT    7 __aeabi_unwind_cpp_pr0
    14: 00002360    44 FUNC    GLOBAL DEFAULT    7 _Unwind_GetRegionStart
    15: 00002254    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Resume_or_Rethrow
    16: 0000aab0     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__
    17: 0000229c    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Backtrace
    18: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_begin_cleanup
    19: 00002118    20 FUNC    GLOBAL DEFAULT    7 __restore_core_regs
    20: 00002154     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP_D_16_to_31
    21: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_call_unexpected
    22: 00001304     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetCFA
    23: 00000000     0 FUNC    GLOBAL DEFAULT  UND memcpy
    24: 000012a0    76 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Set
    25: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize
    26: 0000a944     0 NOTYPE  GLOBAL DEFAULT   12 __FINI_ARRAY__
    27: 000011f1   100 FUNC    GLOBAL DEFAULT    7 Java_com_android_ralmes_GlobalProcess_openGpioM0
    28: 0000aaa0     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start__
    29: 00001a18   212 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Backtrace
    30: 0000aaa0     4 OBJECT  GLOBAL DEFAULT   15 __dso_handle
    31: 00002144     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP_D
    32: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print
    33: 00001dac   876 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Pop
    34: 000017c4     8 FUNC    WEAK   DEFAULT    7 __aeabi_unwind_cpp_pr2
    35: 0000229c    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Backtrace
    36: 0000285c     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_start
    37: 0000220c    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_RaiseException
    38: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort
    39: 00002230    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Resume
    40: 00000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail
    41: 00000fb9    36 FUNC    GLOBAL DEFAULT    7 _Z43Java_com_android_ralems_GlobalProcess_closeP7_JNIEnvP7_jclassi
    42: 0000215c     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_WMMXD
    43: 00001189   104 FUNC    GLOBAL DEFAULT    7 Java_com_android_ralmes_GlobalProcess_closeGpioM0
    44: 00000000     0 FUNC    GLOBAL DEFAULT  UND write
    45: 0000212c     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP
    46: 00002230    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Resume
    47: 00001310    32 FUNC    GLOBAL DEFAULT    7 _Unwind_DeleteException
    48: 0000130c     4 FUNC    GLOBAL DEFAULT    7 _Unwind_Complete
    49: 0000aab0     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__
    50: 0000109d   236 FUNC    GLOBAL DEFAULT    7 Java_com_android_ralmes_GlobalProcess_openSerialPort
    51: 0000a93c     0 NOTYPE  GLOBAL DEFAULT   11 __INIT_ARRAY__
    52: 00000000     0 FUNC    GLOBAL DEFAULT  UND read
    53: 0000238c   888 FUNC    GLOBAL DEFAULT    7 __gnu_unwind_execute
    54: 0000213c     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP_D
    55: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_type_match
    56: 00001d40   108 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Resume
    57: 0000214c     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP_D_16_to_31
    58: 0000220c    36 FUNC    GLOBAL DEFAULT    7 _Unwind_RaiseException
    59: 0000aaa0     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    60: 0000aab0     0 NOTYPE  GLOBAL DEFAULT  ABS __end__
    61: 00001c08    28 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_ForcedUnwind
    62: 00001d20    32 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Resume_or_Rethrow
    63: 00000000     0 FUNC    WEAK   DEFAULT  UND __gnu_Unwind_Find_exidx
    64: 00002118    20 FUNC    GLOBAL DEFAULT    7 restore_core_regs
    65: 000021f8     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_WMMXC
    66: 00002318     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetTextRelBase
    67: 00002328    56 FUNC    GLOBAL DEFAULT    7 _Unwind_GetLanguageSpecificData
    68: 00001254    76 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Get
    69: 000021e4     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_WMMXC
    70: 00002704    64 FUNC    GLOBAL DEFAULT    7 __gnu_unwind_frame
    71: 00002278    36 FUNC    GLOBAL DEFAULT    7 _Unwind_ForcedUnwind
    72: 0000aaa0     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    73: 0000aab0     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    74: 000021a0     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_WMMXD
    75: 000017cc     8 FUNC    WEAK   DEFAULT    7 __aeabi_unwind_cpp_pr1
    76: 00000000     0 FUNC    GLOBAL DEFAULT  UND open
    77: 00002320     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetDataRelBase
    78: 00000000     0 FUNC    GLOBAL DEFAULT  UND close

我要导出一个叫close的函数,但是第41行的内容如下:
41: 00000fb9    36 FUNC    GLOBAL DEFAULT    7 _Z43Java_com_android_ralems_GlobalProcess_closeP7_JNIEnvP7_jclassi

可以看到,导出的函数名不规则,而且ralmes错写成了ralems,
发现问题后立刻就解决了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二、JDBC连接MySql方式 下面是使用JDBC连接MySql的一个小的教程 1、查驱动程序 MySQL目前提供的java驱动程序为Connection/J,可以从MySQL官方网站下载,并到mysql-connector-java-3.0.15-ga-bin.jar文件,此驱动程序为纯java驱动程序,不需做其他配置。 2、动态指定classpath 如果需要执行动态指定classpath,就在执行采用-cp方式。否则将上面的.jar文件加入到classpath环境变量中。 3、加载驱动程序 try{ Class.forName(com.mysql.jdbc.Driver); System.out.println(Success loading Mysql Driver!); }catch(Exception e) { System.out.println(Error loading Mysql Driver!); e.printStackTrace(); } 4、设置连接的url jdbc:mysql://localhost/databasename[?pa=va][&pa=va] 三、以下列出了在使用JDBC来连接Oracle数据库可以使用的一些技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能(系转载)。   1、在客户端软件开发中使用Thin驱动程序   在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。   2、关闭自动提交功能,提高系统性能   在第一次建立与数据库的连接,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:   conn.setAutoCommit(false);   值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。   3、在动态SQL或有间限制的命令中使用Statement对象   在执行SQL命令,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象,每次执行一个SQL命令,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。   此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。   4、利用helper函数对动态SQL命令进行格式化   在创建使用Statement对象执行的动态SQL命令,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。   5、利用PreparedStatement对象提高数据库的总体效率   在使用PreparedStatement对象执行SQL命令,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。   6、在成批处理重复的插入或更新操作中使用PreparedStatement对象   如果成批地处理插入和更新操作,就能够显著地减少它们所需要的间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch(): PreparedStatement pstmt3D null; try { ((OraclePreparedStatement) pstmt).setExecuteBatch(30); ... pstmt.executeUpdate(); }   调用setExecuteBatch()指定的值是一个上限,当达到该值,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法传输批处理任务。   7、使用Oracle locator方法插入、更新大对象(LOB)   Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。   8、使用SQL92语法调用存储过程   在调用存储过程,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程使用SQL92。   9、使用Object SQL将对象模式转移到数据库中   既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。   10、利用SQL完成数据库内的操作   我要向大家介绍的最重要的经验是充分利用SQL的面向集合的方法解决数据库处理需求,而不是使用Java等过程化的编程语言。   如果编程人员要在一个表中查许多行,结果中的每个行都会查其他表中的数据,最后,编程人员创建了独立的UPDATE命令来成批地更新第一个表中的数据。与此类似的任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成。当能够在单一的SQL命令中完成任务,何必要让数据在网上流来流去的?我建议用户认真学习如何最大限度地发挥SQL的功能。 1、查驱动程序 MySQL目前提供的java驱动程序为Connection/J,可以从MySQL官方网站下载,并到mysql-connector-java-3.0.15-ga-bin.jar文件,此驱动程序为纯java驱动程序,不需做其他配置。 2、动态指定classpath 如果需要执行动态指定classpath,就在执行采用-cp方式。否则将上面的.jar文件加入到classpath环境变量中。 3、加载驱动程序 try{ Class.forName(com.mysql.jdbc.Driver); System.out.println(Success loading Mysql Driver!); }catch(Exception e) { System.out.println(Error loading Mysql Driver!); e.printStackTrace(); } 4、设置连接的url jdbc:mysql://localhost/databasename[?pa=va][&pa=va] 三、以下列出了在使用JDBC来连接Oracle数据库可以使用的一些技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能(系转载)。   1、在客户端软件开发中使用Thin驱动程序   在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。   2、关闭自动提交功能,提高系统性能   在第一次建立与数据库的连接,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:   conn.setAutoCommit(false);   值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。   3、在动态SQL或有间限制的命令中使用Statement对象   在执行SQL命令,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象,每次执行一个SQL命令,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。   此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。   4、利用helper函数对动态SQL命令进行格式化   在创建使用Statement对象执行的动态SQL命令,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。   5、利用PreparedStatement对象提高数据库的总体效率   在使用PreparedStatement对象执行SQL命令,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。   6、在成批处理重复的插入或更新操作中使用PreparedStatement对象   如果成批地处理插入和更新操作,就能够显著地减少它们所需要的间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch(): PreparedStatement pstmt3D null; try { ((OraclePreparedStatement) pstmt).setExecuteBatch(30); ... pstmt.executeUpdate(); }   调用setExecuteBatch()指定的值是一个上限,当达到该值,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法传输批处理任务。   7、使用Oracle locator方法插入、更新大对象(LOB)   Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。   8、使用SQL92语法调用存储过程   在调用存储过程,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程使用SQL92。   9、使用Object SQL将对象模式转移到数据库中   既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值