2016-11-21 513 views
2

我正在爲使用Node.js和socket.io的android應用和桌面瀏覽器創建社交應用。Android JNI應用從另一種本地方法調用CallObjectMethod

在瀏覽器客戶端上,每件事情都很好,問題在於android客戶端。實際上,我使用了socket.io java客戶端,我希望在native C++中這樣做。我已成功調用socket.io java客戶端類使用jni併成功連接到我的node.js服務器。兩個發射器回調客戶端從桌面瀏覽器JavaScript客戶端接收來自服務器的消息。

的問題時,Android客戶端將消息發送到服務器時,我此行發送 信息添加到服務器

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

...應用程序崩潰發生。 -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*)


它有這個東西已經採取了我三天本地代碼來完成。感謝您的幫助。

本地代碼:

//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 = env-> NewGlobalRef(socketObj)下一行分配一個新值'globalSocketObj';'? – 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); 

首先創建一個全局引用socketObj提到的Socket實例。創建全局引用的意義在於,與本地引用不同,當您返回到Java時,它不會被刪除。目前爲止,假設您想保留Socket實例不被垃圾回收。

第一個問題是您直接覆蓋下一行的值globalSocketObj,因此您剛創建的全局引用現在處於空白狀態。這意味着,只要從當前方法返回,您就無法再引用Socket對象,這種方式首先會破壞創建全局引用的目的。而且由於你不能刪除你創建的全局引用,你只能讓自己陷入內存泄漏。

第二個潛在的問題是您不會爲您使用env->CallObjectMethod(my_IOobj, static_socket, f)創建的對象創建全局引用,這意味着您將無法再從當前返回的對象中引用該對象方法。

1

我終於找到了solution.I首先要謝謝邁克爾,因爲你的答案 竟睜開眼睛,使我從本地代碼不同的清潔劑連接way.Actually你在哪裏正確的,但有很多內存泄漏和應用程序崩潰與OutOfMemryError所以我這樣做了以下方式 首先在我的cpp文件中創建一個jobject,返回一個IO.socket()對象,然後在我的java文件中使用常規套接字實例化 。這是我的本機代碼:

JNIEXPORT jobject JNICALL Java_com_example_nyari_advancenative_MainActivity_SocketIO(JNIEnv的* ENV,JCLASS clazz所){ 的jstring F = env-> NewStringUTF( 「http://192.168.43.113:8081」);我們可以通過下面的例子來說明如何使用IO類來訪問數據庫:io/socket/client/IO; //獲取IO本地類; ; SOCKET靜態方法來自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