如何在 Android studio 中调用 .so 库的方法

How to call methods on .so library in Android studio

编辑:看我的第一个回答。

我想在我的项目中使用 android 串口 api。我这样做有很多麻烦。关于如何配置旧版本 gradle 或如何使用 NDK 进行编译,存在大量相互矛盾的信息,但两者都没有用。我完全迷路了。

我发现唯一可能正确的是以下步骤:

进度 #1。我将 libserial_port.so 放在 src/main/jnilibs/armeabi 中。当我将它作为 zip 文件打开时,它出现在 apk 中。

但是我如何告诉编译器使用这个库呢?如何判断将其包含在项目输出中?我怎样才能引用这个库中的方法? (有一个SerialPort.c和一个SerialPort.h)?将这些 .mk 文件放在哪里?

我觉得我完全错过了一条每个人似乎都认为的信息。在 api 示例中也没有对本机库的引用。

进度 #2:在我的代码中,我尝试使用
加载库 System.loadLibrary("libserial_port");

此行抛出 UnsatisfiedLinkError。

Native code library failed to load.java.lang.UnsatisfiedLinkError: Couldn't load libserial_port from loader dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/myapp.apk"],nativeLibraryDirectories=[/data/app-lib/myapp.apk, /vendor/lib, /system/lib]]]: findLibrary returned null

进展 #3:链接器不支持带下划线的库名称。

进度 #4:链接器采用 lib 前缀。你应该把它从 loadlibrary 命令中去掉。

现在调用System.loadLibrary("serialport");我的图书馆名为 libserialport.so。现在我不再收到 UnsatisfiedLinkError!

现在开始了解如何从库中引用方法。

我所有问题的答案。适用于 android studio 1.5.1,gradle 2.2.1 2016 年 1 月。

  1. 使用 android studio,.so 文件应该放在 /app/src/main/jniLibs/[armeabi|armeabi-v7a|x86|etcetera]。对于 Eclipse,它是一个不同的目录。

  2. 我们不需要头文件、c 文件或 mk 文件即可工作。

  3. Loadlibrary 在搜索库时采用 "lib" 前缀,因此如果要加载 libdoesstuff.so,命令应为 System.loadLibrary("doesstuff" );我还发现一些不支持 lib 名称中的下划线的语句(虽然没有测试)。

  4. 串口 api 文件应该在它自己的包中(参见这个问题的答案:)。将 SerialPort.java 和 SerialPortFinder.java 放在 /src/java/android_serialport_api 中并保留它们在默认包中(不要将它们移动到您的项目包中,它们在那里不起作用,不要问我如何).

  5. 在您要使用串行端口 class 的 java 文件中,添加行 import android_serialport_api.*;

  6. 如果你不想编译c代码,就不需要安装NDK(很多指南都是这么认为的)。

  7. 从 gradle (2.2.1) 的当前发行版开始,无需更改任何 gradle 构建文件(这里有很多关于 SO 的评论会告诉你这样做)。

  8. 您不能在 Android Studio 的项目设置中添加 .so 文件。将 lib 放在 jniLibs 中会将 .so 添加到 APK。

  9. 完整示例(串行输入的线程读取)。

    package com.yourpackage;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.io.StringWriter;import java.util.Set;
    import android_serialport_api.SerialPort;
    
    public class SerialPortReader {
        private Thread readSerialDataThread;
        private SerialPort serialPort;
        private InputStream inStream;
        private OutputStream outStream;
        private boolean shouldRun = true;
    
        public SerialPortReader() { }
    
        protected void start() {
            try {
                File portLocation = new File("/dev/ttyS1");
                serialPort = new SerialPort(portLocation, 9600, 0);
                inStream = serialPort.getInputStream();
                outStream = serialPort.getOutputStream();
                sendBytes();
            } catch (IOException e) {
                Log.e("SerialPort", "IOException while opening serial port: " + e.getMessage());
                e.printStackTrace();
            }
            startThread();
        }
    
        protected void stop() {
            // break thread
            this.shouldRun = false;
            try {
                readSerialDataThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            serialPort.close();
        }
    
        private void sendBytes() {
            // example how to send data to the opened serial port
            byte[] data = new byte[]{(byte) 0xFF, (byte) 0xAA, (byte) 0x64};
            try {
                outStream.write(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void startThread() {
            readSerialDataThread = new Thread(new Runnable() {
                public void run() {
                    while (shouldRun) {
                        int dataSize = 0;
                        try {
                            dataSize = inStream.available();
                            byte[] data = new byte[dataSize];
                            inStream.read(data);
                            processData(data);
                            Thread.sleep(50); // my serial sensor gives 20 Hz updates
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            readSerialDataThread.start();
        }
    }