2016-11-21 4 views
2

Я пытаюсь создать социальное приложение для Android-приложений и настольных браузеров с помощью Node.js и socket.io.Android JNI APP CRASHES на вызове CallObjectMethod из другого родного метода

В браузере все работает хорошо, проблема связана с клиентской стороной android. Обычно я использовал java-клиент socket.io, и я хочу сделать это на родном C++. Я успешно назвал класс socket.io java client используя jni и успешно подключается к моему серверу node.js. Два клиента обратного вызова эмиттера получают сообщения от сервера, отправляемые от клиента javascript клиента рабочего стола.

Проблема возникает, когда андроид клиент отправляет сообщение на сервер, когда я добавить строку, которая отправляет сообщения на сервер

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

... то приложение падает. -crash Сообщение-

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*)


Это должно быть сделано в родном коде этот материал уже взял меня за три дня. Спасибо за помощь.

ИСХОДНЫЙ КОД:

//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(); 
          } 
         } 
        }); 
       } 
      }; 

     } 
    } 
+1

Почему вы назначая новое значение 'globalSocketObj' на следующей строке после' globalSocketObj = env-> NewGlobalRef (socketObj) '? – Michael

+0

Делаю это, поэтому я могу использовать его в другом родном методе –

+0

. Моя мысль была в том, почему вы назначаете его 'env-> NewGlobalRef (socketObj);' и затем перезаписываете это значение с помощью env-> CallObjectMethod (my_IOobj, static_socket, f), 'на следующей строке? Какое из этих двух значений является правильным? – Michael

ответ

1

Что вы делаете здесь есть несколько проблем:

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

Сначала необходимо создать глобальную ссылку на Socket экземпляр ссылается socketObj. Точка создания глобальной ссылки заключается в том, что в отличие от локальных ссылок она не будет удалена при возврате на Java. До сих пор так хорошо, предполагая, что вы хотите сохранить экземпляр Socket от сбора мусора.

Первая проблема заключается в том, что вы непосредственно перезаписываете значение globalSocketObj на самой следующей строке, так что глобальная ссылка, которую вы только что создали, теперь находится в неопределенности. Это означает, что вы больше не сможете ссылаться на этот объект Socket, как только вы вернетесь из текущего метода, который в первую очередь побеждает цель создания глобальной ссылки. И поскольку вы не сможете удалить глобальную ссылку, которую вы создали, вы только что заработали себе утечку памяти.

Вторая потенциальная проблема заключается в том, что вы не создаете глобальную ссылку для объекта, который вы создаете, с env->CallObjectMethod(my_IOobj, static_socket, f), что означает, что вы не сможете больше ссылаться на этот объект после того, как вы вернулись с текущего метод.

1

Я, наконец, нашел решение. Сначала хочу поблагодарить вас, Micheal, потому что ваши ответы действительно открыли мне глаза и заставили меня подключиться от родного кода по-разному чище. Фактически вы, где правильно, но там много утечки памяти и приложение сбой с OutOfMemryError Итак, я сделал это следующим образом. Сначала в моем файле cpp я создаю jobject, который возвращает объект IO.socket(), а затем создает обратно в свой java-файл с обычным сокетом.Вот мой родной код:

JNIEXPORT jobject JNICALL Java_com_example_nyari_advancenative_MainActivity_SocketIO (JNIEnv * окр, JClass clazz) { jstring е = env-> NewStringUTF ("http://192.168.43.113:8081"); jclass my_IO = env-> FindClass ("io/socket/client/IO"); // ПОЛУЧЕНИЕ IO LOCAL CLASS jobject my_IOobj = env-> AllocObject (my_IO); // ПОЛУЧЕНИЕ ОБЪЕКТА IO ИЗ IO CLASS // ПОЛУЧЕНИЕ SOCKET STATIC METHOD FROM IO jmethodID static_socket = env-> GetStaticMethodID (my_IO, "socket", "(Ljava/lang/String;) Lio/socket/client/Socket;"); return env-> CallObjectMethod (my_IOobj, static_socket, f);

} 

Java код:

public class MainActivity extends AppCompatActivity { 
    static{ 
     System.loadLibrary("louts"); 
    } 
     TextView text,tex; 

     public native void connect(); 
     public static native Socket SocketIO(); 
     Socket sok; 
     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() { 
      sok=SocketIO(); 
      sok.on("hello", receive); 
      sok.on("message",chats); 
      sok.connect(); 
     } 
    }); 
      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); 
         sok.emit("message",reg2); 
         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(); 
          } 
         } 
        }); 
       } 
      }; 

     } 
    } 
Hope it helps some one with the same problem