Reputation: 33
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();
}
}
});
}
};
}
}
Upvotes: 2
Views: 2124
Reputation: 33
I have finally found the solution.I first want to thank you Micheal because your answers actually opened my eyes and made me connect from native code differently in a cleaner way.Actually you where right but there is alot of memory leak and app crashes with OutOfMemryError So i did it the following way First in my cpp file i create a jobject that returns an IO.socket() object, i then instantiates back in my java file with a regular socket. Here is my Native code:
JNIEXPORT jobject JNICALL Java_com_example_nyari_advancenative_MainActivity_SocketIO(JNIEnv* env, jclass clazz){ jstring f=env->NewStringUTF("http://192.168.43.113:8081"); jclass my_IO=env->FindClass("io/socket/client/IO");//GETTING IO LOCAL CLASS 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;"); return env->CallObjectMethod(my_IOobj, static_socket, f);
}
Java Code:
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
Upvotes: 1
Reputation: 58507
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.
Upvotes: 1