2

I am making a remote controlling app for my arduino car. The code that is used has first been tested in Eclipse using Java and now I'm trying to use the same code for the Android application.

I have used the jSerialComm library, I have no errors in my code according to Android Studio, but when I run it, it can't find the library? I recieve the following error:

FATAL EXCEPTION: main Process: com.sahragard.avengrecontroller, PID: 11728 java.lang.UnsatisfiedLinkError: dlopen failed: file offset for the library "/data/user/0/com.sahragard.avengrecontroller/cache/1454627726168-libjSerialComm.so"

= file size: 0 >= 0 at java.lang.Runtime.load(Runtime.java:332) at java.lang.System.load(System.java:1069) at com.fazecast.jSerialComm.SerialPort.(SerialPort.java:181) at com.sahragard.avengrecontroller.Conn.getPorts(Conn.java:19) at com.sahragard.avengrecontroller.MainActivity.onCreate(MainActivity.java:25) at android.app.Activity.performCreate(Activity.java:6237) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

I googled for many hours and followed the tips that I have found, but whatever I do, it's always the same error. I have added the library according to this post: https://stackoverflow.com/a/16628496/4582696

I appreciate any help I can get! Thanks in advance.

EDIT:

MainActivity class is an adaptation of a class containing Swing interface, its code is added below this code

public class MainActivity extends AppCompatActivity {



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Spinner conSpinner =(Spinner) findViewById(R.id.spinner);

        String[] a = new String[Conn.getPorts().length];
        for(int i=0; i<Conn.getPorts().length; i++){
            a[i] = Conn.getPorts()[i].getSystemPortName();
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_spinner_item, a);
        conSpinner.setAdapter(adapter);


        final Button disconnectButton = (Button) findViewById(R.id.disconnect);
        disconnectButton.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                Conn.disconnect();
            }
        });


        final Button connectButton = (Button) findViewById(R.id.connect);
        connectButton.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                Conn.connect(conSpinner.getSelectedItemPosition());
                Runnable run = new Runnable() {
                    public void run() {
                        Conn.listen();
                    }
                };
                Conn.listen = new Thread(run);
                Conn.listen.start();
            }
        });



        final Button up = (Button) findViewById(R.id.upButton);
        up.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                Conn.sendMsg("w");
            }
        });

        final Button down = (Button) findViewById(R.id.downButton);
        down.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                Conn.sendMsg("s");
            }
        });

        final Button left = (Button) findViewById(R.id.leftButton);
        left.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                Conn.sendMsg("a");
            }
        });

        final Button right = (Button) findViewById(R.id.rightButton);
        right.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                Conn.sendMsg("d");
            }
        });




    }
}

Original Remote_Interface class

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.fazecast.jSerialComm.SerialPort;

import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JTextPane;
import java.awt.SystemColor;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class Remote_Interface extends JFrame {

    private JPanel contentPane;
    private JTextField textField;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Remote_Interface frame = new Remote_Interface();
                    frame.setVisible(true);
//                  Conn.listen();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public Remote_Interface() {
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 270, 268);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JComboBox<String> comboBox = new JComboBox<String> ();
        comboBox.setBounds(20, 46, 214, 20);
        for(int i=0; i<Conn.getPorts().length; i++){
            comboBox.addItem(Conn.getPorts()[i].getSystemPortName());
        }
        contentPane.add(comboBox);

        textField = new JTextField();
        textField.setBounds(20, 131, 214, 20);
        contentPane.add(textField);
        textField.setColumns(10);

        textField.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                Conn.sendMsg(e.getActionCommand());
                textField.setText("");
            }
        });

        JButton btnNewButton = new JButton("Send");
        btnNewButton.setBounds(20, 162, 89, 23);
        contentPane.add(btnNewButton);

        JTextPane txtpnPleaseSelectA = new JTextPane();
        txtpnPleaseSelectA.setBackground(SystemColor.control);
        txtpnPleaseSelectA.setText("Please select a port to connect");
        txtpnPleaseSelectA.setEditable(false);
        txtpnPleaseSelectA.setBounds(10, 11, 214, 20);
        contentPane.add(txtpnPleaseSelectA);

        btnNewButton.setEnabled(false);
        textField.setEnabled(false);

        JButton btnNewButton_1 = new JButton("Connect");
        btnNewButton_1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Conn.connect(comboBox.getSelectedIndex());
                btnNewButton.setEnabled(true);
                textField.setEnabled(true);
                Runnable run = new Runnable(){
                    public void run(){
                        Conn.listen();
                    }
                };
                Conn.listen = new Thread(run);
                Conn.listen.start();
            }
        });
        btnNewButton_1.setBounds(143, 77, 89, 23);
        contentPane.add(btnNewButton_1);

        JButton btnD = new JButton("Disconnect");
        btnD.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Conn.disconnect();
                btnNewButton.setEnabled(false);
                textField.setEnabled(false);
            }
        });
        btnD.setBounds(20, 77, 89, 23);
        contentPane.add(btnD);
    }
}

And the Conn class

import com.fazecast.jSerialComm.*;

import java.io.PrintWriter;
import java.util.Scanner;

public class Conn {

static  PrintWriter out;
static  SerialPort[] ports;
static  SerialPort port;
static  Scanner in;
static Thread listen;

public static SerialPort[] getPorts(){
    ports = SerialPort.getCommPorts();
    return ports;
}

public static void sendMsg(String s){
    out.println(s);
    out.flush();
}

public static void connect(int i){
    port = ports[i];
    port.openPort();
    port.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
    out = new PrintWriter(port.getOutputStream());
    in = new Scanner(port.getInputStream());
}

public static void disconnect(){
    port.closePort();

}

public static void listen(){// handle the input from the Arduino chip 
    while(in.hasNextLine()){
        System.out.println(in.nextLine());// just print it out to the console 
    }
}



}
Community
  • 1
  • 1
Coco
  • 826
  • 1
  • 12
  • 20
  • Have you looked at other SO threads? this one - http://stackoverflow.com/a/28152287/1091286 for exampe talks about differences in the chip architectire and considering you are developing something for arduino it might be the problem.... – crazyPixel Feb 04 '16 at 23:41
  • Thank you for your answer! I didn't quite understand the answer posted on that question. But my problem is that the app crashes immediately when I run it on my phone or on the virtual phone. So it has not made any connection to the Arduino chip yet – Coco Feb 04 '16 at 23:53
  • `at com.fazecast.jSerialComm.SerialPort` line 181. That *is* the library, which means it *was* loaded – OneCricketeer Feb 05 '16 at 00:00
  • @cricket_007 but why does it say "dlopen failed: file offset for the library"? – Coco Feb 05 '16 at 00:03
  • According to the stack trace, whatever you are doing at `Conn.getPorts` in line 19, the library doesn't like that. I also see you are using a model object as a static utility class. That is just bad code... sorry – OneCricketeer Feb 05 '16 at 00:06
  • Thanks a lot for your feedback, but I don't quite understand? What should I do to improve it? – Coco Feb 05 '16 at 00:10
  • You are just trying to make a wrapper object for some port number, yes? – OneCricketeer Feb 05 '16 at 00:12
  • I don't know what that means, but the function is supposed to return an array of ports, which is then used to fill a list, and then you can choose which port you want to connect to – Coco Feb 05 '16 at 00:16
  • I would just like to add that this works perfectly when running it on Eclipse – Coco Feb 05 '16 at 00:22
  • Between Android Studio and Eclipse you ran the same code on the same device and you got different results? – OneCricketeer Feb 05 '16 at 00:23
  • The Conn class is identical but the MainActivity is an adaptation from another class. The original class uses swing, and MainActivity has an android interface – Coco Feb 05 '16 at 00:28
  • I have added the original code below the adapted code if you wish to compare – Coco Feb 05 '16 at 00:32
  • You could post an issue on the [GitHub page for this library](https://github.com/Fazecast/jSerialComm/issues). I'm sure they understand what is going on better than anyone here – OneCricketeer Feb 05 '16 at 18:46

2 Answers2

1

Assuming this code works absolutely fine on an Android device, I honestly cannot see why you would get any errors because clearly the library is being loaded in the stacktrace, otherwise you would get both compile errors and class not found exceptions.

Anyways, this is what is happening...

final Spinner conSpinner =(Spinner) findViewById(R.id.spinner);

String[] a = new String[Conn.getPorts().length];
for(int i=0; i<Conn.getPorts().length; i++){
    a[i] = Conn.getPorts()[i].getSystemPortName();
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_spinner_item, a);
conSpinner.setAdapter(adapter);

And later do

Conn.connect(conSpinner.getSelectedItemPosition());

... which (roughly) calls

public static void connect(int i){
    port = SerialPort.getCommPorts()[i];
    port.openPort();
    ...
}

I think you shouldn't use static method when you clearly want an instance variable.

I know this code doesn't "matter" per-say, but I modified your class into a proper instance variable, which is the preferred way to save the state of your Conn variable(s).

I would explain the difference between static and non-static variables, but that is too broad for the error you have shown.


Here is the update Conn.java with comments.

NOTE I may have made a typo or some other error, I didn't try to compile this

import com.fazecast.jSerialComm.*;

import java.io.PrintWriter;
import java.util.Scanner;

public class Conn {

    /* These are instance variables for *THIS* Conn object */
    private  PrintWriter out;
    // static  SerialPort[] ports; // unneccessary
    private  SerialPort port;
    private Scanner in;
    // static Thread listen; // unused

    // this acts like the connect method
    // it initializes all your stuff
    public Conn(int i) {
        this.port = SerialPort.getCommPorts()[i];
        // maybe you should check this to see it was opened
        boolean opened = port.openPort(); 
        if (!opened) {
            System.err.println("Oh no! The port wasn't opened");
        }
        this.port.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
        this.out = new PrintWriter(port.getOutputStream());
        this.in = new Scanner(port.getInputStream());
    }

    // not needed here, just call SerialPort.getCommPorts() in any other class
    /*
    public static SerialPort[] getPorts(){
        ports = SerialPort.getCommPorts();
        return ports;
    }
    */

    public void sendMsg(String s){
        this.out.println(s);
        this.out.flush();
    }

    // Maybe propogate whether you successfully closed the Connection
    public boolean disconnect(){
        return this.port.closePort();
    }

    // You probably want this on a separate thread since this will block the main thread
    // ... that's another topic though
    public void listen(){// handle the input from the Arduino chip 
        while(this.in.hasNextLine()){
            System.out.println(in.nextLine());// just print it out to the console 
        }
    }
} 

And here is an updated Activity with just a few of the button listeners for reference. Notice the private conn field and conn with a lower case c in all the methods and conn = new Conn(position) in the connect button. You didn't need a static class is all I'm trying to show here, really...

import com.fazecast.jSerialComm.*;

public class MainActivity extends AppCompatActivity {

    private conn Conn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Spinner conSpinner =(Spinner) findViewById(R.id.spinner);

        SerialPort[] ports = SerialPort.getCommPorts()
        String[] a = new String[ports.length];
        for(int i=0; i<ports.length; i++){
            a[i] = ports[i].getSystemPortName();
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_spinner_item, a);
        conSpinner.setAdapter(adapter);

        final Button connectButton = (Button) findViewById(R.id.connect);
        connectButton.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                conn = new Conn(conSpinner.getSelectedItemPosition());
                Runnable run = new Runnable() {
                    public void run() {
                        conn.listen();
                    }
                };
                new Thread(run).start();
            }
        });

        final Button disconnectButton = (Button) findViewById(R.id.disconnect);
        disconnectButton.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                if (conn != null) {
                    conn.disconnect();
                }
            }
        });

        final Button up = (Button) findViewById(R.id.upButton);
        up.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                if (conn != null) {
                    conn.sendMsg("w");
                }
            }
        });
    }
}
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • Thank you a lot for taking your time my friend. After going through your code I now understand what you mean. I made some corrections to the typos and so on in order to make it runnable, but I still recieve the same error... – Coco Feb 05 '16 at 00:53
  • Yeah, I found a couple typos myself :) You said it runs fine in Eclipse, though? – OneCricketeer Feb 05 '16 at 00:58
  • Hi @cricket_007 can you also help me with my problem here http://stackoverflow.com/questions/35230272/android-dlopen-failed-file-offset-for-the-library-libnexplayerengine-so-fi thanks! –  Feb 05 '16 at 18:35
1

I had the same problem, the only way how I was able to fix it was:

  1. Copied files from https://github.com/Fazecast/jSerialComm/tree/master/src/main/java/com/fazecast/jSerialComm into my project keeping the same package name as in original.
  2. Downloaded jar from https://mvnrepository.com/artifact/com.fazecast/jSerialComm/1.3.11 and extracted it, copied all folders inside \jSerialComm-1.3.11\Android\
  3. Pasted all folders inside project src/main/jniLibs
  4. Call System.loadLibrary("jSerialComm"); before doing any method calls.

After that

SerialPort myPort = SerialPort.getCommPort("/dev/ttyMT2");

gave the corresponding object, without any UnsatisfiedLinkError.

*Another thing, if you are using the /dev folder, it could be necessary to make a android build, where the SeLinux permission mode is set to Disabled or Permissive. https://www.centos.org/docs/5/html/5.1/Deployment_Guide/sec-sel-enable-disable.html

Mikelis Kaneps
  • 4,576
  • 2
  • 34
  • 48
  • Thanks for the answer, I moved on and created my own bluetooth functions. But I hope it will serve well as a solution for people in the future that encounter this problem. Best wishes to you – Coco Sep 15 '16 at 13:15