Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call methods on .so library in Android studio

Edit: see my first answer.

I'd like to use the android serialport api in my project. I have a lot of trouble doing so. There is an enormous amount of conflicting information about how to configure older versions of gradle or how to compile with NDK which both aren't useful. I am completely lost.

The only thing I found that is probably correct is the following step:

Progress #1. I placed the libserial_port.so in src/main/jnilibs/armeabi. It appears in the apk when I open it as a zip file.

But how do I tell the compiler to use this library? How to tell to include it in the project output? How can I reference methods in this library? (there are a SerialPort.c and a SerialPort.h)?. And where to put these .mk files?

I have the feeling I am completely missing a piece of information that everyone seems to assume. In the api samples there is no referencing to native libraries as well.

Progress #2: In my code I try to load the library using
System.loadLibrary("libserial_port");

This line throws an 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

Progress #3: the linker doesn't support library names with underscores.

Progress #4: the linker assumes the lib-prefix. You should leave it out of the loadlibrary command.

Now I call System.loadLibrary("serialport"); and my library is named libserialport.so. Now I don't get the UnsatisfiedLinkError anymore!

Now off to find out how to reference methods from the lib.

like image 834
Harmen Avatar asked Jan 09 '16 14:01

Harmen


1 Answers

Answers to all my questions. Valid for android studio 1.5.1 with gradle 2.2.1 january 2016.

  1. With android studio, the .so files should be placed /app/src/main/jniLibs/[armeabi|armeabi-v7a|x86|etcetera]. For eclipse it is a different directory.

  2. We don't need header, c files or mk files for this to work.

  3. Loadlibrary assumes the "lib" prefix when searching for a library so if you want to load libdoesstuff.so the command should be System.loadLibrary("doesstuff"); I also found some statements that underscores in the lib name are not supported (did not test this though).

  4. The serialport api files should be in it's own package (see the answer to this question: How to unit test serial ports in Java on Android). Place the SerialPort.java and SerialPortFinder.java in /src/java/android_serialport_api and leave them in the default package (don't move them to your project package, they won't work there, don't ask me how).

  5. In your java file where you want to use the SerialPort class, add the line import android_serialport_api.*;

  6. There is no need to install the NDK if you don't want to compile c code (a lot of guides assume this).

  7. As of current release version of gradle (2.2.1) there is no need to change any of the gradle build files (a lot of comments here on SO will tell you to do so).

  8. You cannot add the .so files in the project settings in Android Studio. Placing the lib in jniLibs will add the .so to the APK.

  9. Full example (threaded reading of the serial input).

    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();
        }
    }
    
like image 138
Harmen Avatar answered Oct 11 '22 23:10

Harmen