在Android应用中使用现有共享库(.so)

10

我需要处理以下情况。我已经得到一个共享库(libeffect.so),要在我正在为客户工作的Android项目中使用。我没有共享库的源代码,只有.so文件。该库是预编译的,可在Android设备上运行。除了共享库之外,我还有方法签名。

public static native void doEffect(int param1, IntBuffer intBuffer);

现在我对如何调用这个本地方法有一些问题,如果只有.so文件,是否可以进行调用,以下是我的问题:

  1. 我需要把本地方法签名放在与.so文件定义的相同包/类中,还是可以在项目的任何包/类中使用此签名,在运行时jvm将能够在共享库中找到方法?例如,如果该共享库最初在类mypackage.MyClass中使用,我是否需要创建相同的包、类,然后将方法签名放在其中?

  2. 我需要在我的eclipse android项目中的哪里放置这个.so文件,以便在apk文件中部署此文件?

这些问题可能听起来很新手,但我以前从未使用过jndi,所以我有点担心是否能够实现无误调用doEffect方法。任何能指导我的答案都非常欢迎。

非常感谢 Thiago

2个回答

7
您是否需要在与.so文件相同的包/类中放置本地方法签名,或者我可以在项目中的任何包/类中使用此签名,在运行时jvm将能够在共享库中找到该方法?例如,如果此共享库首先在mypackage.MyClass类中使用,我是否需要创建相同的包、类,然后将方法签名放在那里?
无需创建相同的包/类。您可以将方法签名放在任何包中。
public class NativeLib {

  static {
    System.loadLibrary("so_file");
  }

  public static native void doEffect(int param1, IntBuffer intBuffer);

}

2.我需要把这个.so文件放在我的Eclipse Android项目的哪里,才能在我的apk文件中部署这个文件?

您需要将此.so文件放置在应用程序的lib文件夹中。如果没有lib文件夹,则可以创建一个lib文件夹并放置.so文件。您可以使用System.loadLibrary("so_文件");来调用它。


1
谢谢@Sujit。我在看这个教程,http://www.android10.org/index.php/articlesother/276-call-c-code-from-application-using-ndk,我发现生成的C++代码片段中方法名由Java包名和方法名组成。这会影响我使用本地签名的方式吗?我认为我应该遵循创建共享库时使用的相同命名约定。另一个问题是,一旦我在AndroidProject/lib文件夹中有了.so文件,这个文件会随APK文件一起导出吗?我需要创建armeabi子文件夹吗?非常感谢,T - Thiago

1

我需要将本地方法签名放在与.so文件相同的包/类中,还是可以在我的项目中的任何包/类中使用此签名,在运行时jvm将能够在共享库中找到该方法?

根据http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/design.html,您必须使用匹配的包和类名称。

我只观察过JNI方法,其中C端函数被称为Java_com_company_whatever_SomeClass_someMethod,这意味着您必须将“native”声明放在具有类似命名的Java类中。

使用工具“nm”或“nm++”(它们在NDK的预编译文件夹中)查看.so文件并查看其中定义的函数的名称。如果您看到任何以Java_开头的内容,则需要这些内容。

我对先前的说法持怀疑态度,即你可以调用未命名为Java_PACKAGE_CLASS_METHOD格式的函数;如果它确实有效,那么这可能是一种遗留行为,但即使你可以,这似乎很危险 - 你可能会得到错误的结果。
2. 我需要将这个.so文件放在哪里才能在我的eclipse android项目中部署这个文件并打包进apk文件?
你的.so文件位于libs/armeabi、libs/armeabi-v7a、libs/x86和/或libs/mips,具体取决于你使用了多少个平台,其中“libs”与“src”和“res”同级。我不知道Android是否会查找没有平台限定符的libs/,但这样做似乎没有明显的好处。大多数/所有英特尔设备包括精良的技术,允许它们在x86硬件上执行大多数ARM库,这使得情况稍微有些复杂。
此外,我喜欢声明JNI类的接口并提供一个工厂(这里是一个方法,为了简洁起见,但我更喜欢一个工厂类),如果出现问题,它提供一个接口的无操作实现:它有助于单元测试,并且避免了在调用其方法之前测试空值的麻烦(假设您确定您的已发布库永远不会缺少或更改方法签名-您的集成测试应该检查这一点)。
public interface YourLibI {
    @Override
    public native yourMethod();

    public static final NO_OP = new YourLibI() {
        @Override
        public void yourMethod(){}
    }
}

public class YourLib extends YourLibI {
    public newYourLibI() {
        try {
            return new YourLib();
        }
        catch (UnsatisfiedLinkError e) {
            Log.e("YourLibJNI", "Load failed, returning NO-OP dummy", e);
            return YourLibI.NO_OP;
        }
    }

    static {
        System.loadLibrary("arbitronSDK");
    }

    private YourLib() {
    }

    @Override
    public native void yourMethod();
}

我通常不会把接口命名为'xxxI',但我假设你的库的JNI类没有像UtilityJNI这样好的名称(在这种情况下,我会将接口命名为'Utility')。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接