Search code examples
androidjava-native-interface

Android JNI APP CRASHES ON CALL OF CallObjectMethod from another native method


I am tring to created a social app both for android app and desktop browsers using Node.js and socket.io.

On the browser client every thing works good the problem is the android client side.Actually i used the socket.io java client and i wish to do that in native c++.I have successfully called the socket.io java client class using jni and it successfully connects to my node.js server. The two emitter callback clients receives messages from the server send from the desktop browser javascript client.

The problem arises when the android client sends a message to the server when i add this line that sends message to the server

env->CallObjectMethod(globalSocketObj,emit,lo,o);

...the app crashes. -CRASH MESSAGE-

11-21 10:10:58.417 9310-9310/com.example.nyari.advancenative E/dalvikvm: JNI ERROR (app bug): attempt to use stale local reference 0x1df00025 11-21 10:10:58.417 9310-9310/com.example.nyari.advancenative E/dalvikvm: VM aborting 11-21 10:10:58.418 9310-9310/com.example.nyari.advancenative A/libc: Fatal signal 6 (SIGABRT) at 0x0000245e (code=-6), thread 9310 (i.advancenative) 11-21 10:10:58.418 9310-9310/com.example.nyari.advancenative A/libc: Send stop signal to pid:9310 in void debuggerd_signal_handler(int, siginfo_t*, void*)


It has to be done in native code this stuff has already taken me three days. Thanks for your helps.

NATIVE CODE :

//METHOD CONNECTING WITH NODE SERVER
JNIEXPORT void JNICALL Java_com_example_nyari_advancenative_MainActivity_count
                (JNIEnv* env, jobject obj){
            //LOCAL VARIABLES TO INSTANCES/////////////////////////////////////////////////
            Main_class=env->GetObjectClass(obj);//GETTING MAINACTIVITY OBJECT
            jstring f=env->NewStringUTF("http://192.168.43.113:8081");
            jstring il=env->NewStringUTF("hello");
            jstring ili=env->NewStringUTF("message");
        jclass my_socket=env->FindClass("io/socket/client/Socket");//GETTING SOCKET CLASS OF SOCKETIO
        jclass my_IO=env->FindClass("io/socket/client/IO");//GETTING IO LOCAL CLASS
            jobject socketObj=env->AllocObject(my_socket);//socketIO object
           jobject my_IOobj=env->AllocObject(my_IO);//GETTING IO OBJECT FROM IO CLASS
            //GETTING SOCKET STATIC METHOD FROM IO
           jmethodID static_socket=env->GetStaticMethodID(my_IO,"socket","(Ljava/lang/String;)Lio/socket/client/Socket;");
            //GLOBAL VARIABLES INSTANCES
            globalSocket= (jclass) env->NewGlobalRef(my_socket);//GLOBAL REFERENCE OF SOCKET CLASS
            globalSocketObj=env->NewGlobalRef(socketObj);//GLOBAL SOCKETIO REFERENCE OBJECT
        ///
            //INSTANTIATING GLOBAL REFERENCE OF SOCKET OBJECT
        globalSocketObj=env->CallObjectMethod(my_IOobj, static_socket, f);
            //LOCAL REFERENCES VARIABLES
            //GETTING CONNECT METHOD OF SOCKET CLASS
            jmethodID socket_connect=env->GetMethodID(my_socket,"connect","()Lio/socket/client/Socket;");
            //GETTING THE RECEIVE EMMITER CLASS FROM MAINACTIVITY
            receiveField=env->GetFieldID(Main_class,"receive","Lio/socket/emitter/Emitter$Listener;");
            //GETTING OBJECT FROM RECEIVEFIELD EMITTER FROM IN MAINACTIVITY
            receiveObj=env->GetObjectField(obj,receiveField);
            ///METHOD FROM SOCKET CLASS on FOR HELLO MESSAGE WITH SERVER
            onReceive=env->GetMethodID(my_socket,"on","(Ljava/lang/String;Lio/socket/emitter/Emitter$Listener;)Lio/socket/emitter/Emitter;");
            /////////////////
            //GETTING CHAT EMITTER FIELD FROM  MAINACTIVITY
            receiveMessage=env->GetFieldID(Main_class,"chats","Lio/socket/emitter/Emitter$Listener;");
            //GETTING OBJECT FROM CHATFIELD EMITTER FROM IN MAINACTIVITY
            receiveMessageObj=env->GetObjectField(obj,receiveMessage);
            ///METHOD FROM SOCKET CLASS on FOR MESSAGES SENT AND RECEIVED
            onReceiveMessage=env->GetMethodID(my_socket,"on","(Ljava/lang/String;Lio/socket/emitter/Emitter$Listener;)Lio/socket/emitter/Emitter;");
        ////////////////////////////
            env->CallObjectMethod(globalSocketObj,onReceive,il,receiveObj);
            env->CallObjectMethod(globalSocketObj,onReceiveMessage,ili,receiveMessageObj);
        env->CallObjectMethod(globalSocketObj,socket_connect);

          }

    ///PROBLEMATIC METHOD USED TO SEND MESSAGE TO SERVER
JNIEXPORT void JNICALL Java_com_example_nyari_advancenative_MainActivity_vari(JNIEnv* env, jobject obj){
        jstring il=env->NewStringUTF("hello");//message to be sent to the server
        jstring ili=env->NewStringUTF("message");//for callback method in server
        jclass ob=env->GetObjectClass(globalSocketObj);
        jmethodID emit=env->GetMethodID(ob,"emit","(Ljava/lang/String;[Ljava/lang/Object;)Lio/socket/emitter/Emitter;");
        env->CallObjectMethod(globalSocketObj,emit,ili,il);//PROBLEMATIC CALL

    }

JAVA CODE :


    public class MainActivity extends AppCompatActivity {
    static{
    /*try{
        System.loadLibrary("louts");
    }catch(Error | Exception ignore){

    }*/
        System.loadLibrary("louts");
    }
        TextView text,tex;

        public native void connect();
        public native void count();
        public native void vari();
        public native Socket network();
    public native void send();
        String message;
    Emitter.Listener receive,chats;
    String jo= io.socket.client.Socket.EVENT_CONNECT;
        ArrayList<String> items;
        ArrayAdapter<String> adapter;
        ListView list;
        EditText texter;
        Button button;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);
            text=(TextView)findViewById(R.id.numb);
            tex=(TextView)findViewById(R.id.num);
    //LISTVIEW TO ADD MESSAGES FROM NODE SERVER
            items=new ArrayList<String>();
         adapter=new ArrayAdapter<String>(getApplicationContext(),android.R.layout.simple_list_item_1,items);

            list=(ListView)findViewById(R.id.list);
            list.setAdapter(adapter);
    //EDITTEXT TO GET MESSAGES
            texter=(EditText)findViewById(R.id.texter);
    //CONTAINS THE EMITTER CALLBACKS METHODS TO RECEIVE MESSAGES FROM NODE SERVER
            emit();
    //THREAD THAT CONTAINS  NATIVE METHOD count() that connects to NODE SERVER
    Thread y=new Thread(new Runnable(){

        @Override
        public void run() {
            count();
        }
    });
            y.start();
    //BUTTON THAT MESSAGE TO NODE SERVER
            button=(Button)findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String j=null;
                    j=texter.getText().toString();
                    JSONObject reg2=new JSONObject();
                    try {
                        reg2.put("ki",j);
                        /*socket.emit("message",reg2);*/
                        vari();
                        texter.setText("");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            });

        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            connect();
        }

        private void emit(){
            receive= new Emitter.Listener() {
                @Override
                public void call(Object... args) {//CALLBACK METHOD FOR HELLO MESSAGE FROM SERVER
                    final JSONObject obj = (JSONObject)args[0];
                    MainActivity.this.runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            try {
                                message=obj.getString("ki");
                                text.setText(message);
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    });

                }
            };
            chats= new Emitter.Listener() {
                @Override
                public void call(Object... args) {//CALLBACK METHOD TO RECEIVE CHAT MESSAGES FROM SERVER
                    final JSONObject obj = (JSONObject)args[0];
                    MainActivity.this.runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            try {
                                message=obj.getString("mess");
                              //  items.add(message);
                                adapter.add(message);
                                adapter.notifyDataSetChanged();
                                //tex.setText(message);
                                //list.setVerticalScrollbarPosition(list.getHeight());
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            };

        }
    }

Solution

  • What you do here has a couple of problems:

           globalSocketObj=env->NewGlobalRef(socketObj);//GLOBAL SOCKETIO REFERENCE OBJECT
        ///
            //INSTANTIATING GLOBAL REFERENCE OF SOCKET OBJECT
        globalSocketObj=env->CallObjectMethod(my_IOobj, static_socket, f);
    

    First you create a global reference to the Socket instance referred to by socketObj. The point of creating a global reference is that unlike local references it won't be deleted when you return back to Java. So far so good, assuming that you want to keep the Socket instance from being garbage collected.

    The first problem is that you directly overwrite the value of globalSocketObj on the very next line, so that global reference you just created is now in limbo. That means that you won't be able to refer to that Socket object any longer as soon as you return from the current method, which kind of defeats the purpose of creating a global reference in the first place. And since you won't be able to delete the global reference you created you've just earned yourself a memory leak.

    The second potential problem is that you don't create a global reference for the object you create with env->CallObjectMethod(my_IOobj, static_socket, f), which means that you won't be able to refer to that object any longer once you've returned from the current method.