Issue
My question is related directly to this post: https://groups.google.com/forum/#!topic/android-ndk/291sBdkITyI
Basically, I have an application written in C++ compiled with the NDK with a basic Android (activity). I have a textview (in Java) that needs to be updated when something happens in the c++ side (say for example a state change). I would like to call Java from C++ and update the textview when the state changes.
In the link above, the code they used was (probably pseudo code):
public class Example extends Activity{
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
this.currentDownloadField.setText(""+ msg.what);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
// whatever
}
public static void sendMessage(int id){
handler.sendEmptyMessage(id); // just the what() is filled with the id
}
}
And calling from C++ would be
void sendMessage(char* buffer, int bufferlen) {
JNIEnv *env = NULL;
jmethodID mid = NULL;
jbyteArray message;
jint res = (jjvm->AttachCurrentThread(&jjvm, &env, NULL));
if (res >= 0) {
message = (*env)->NewByteArray(env, bufferlen);
(*env)->SetByteArrayRegion(env, message, 0, bufferlen, (const jbyte *) ((BYTE*) buffer));
mid = (*env)->GetStaticMethodID(env, jcls, "sendMessage", "([B)V");
// Mid <= 0 ? Not found : OK
if (mid > 0) {
(*env)->CallStaticVoidMethod(env, jcls, mid, message);
}
}
}
The problem is using "handler" in the activity from a static function does not work (because it is not static). If it was static, then how does "this.currentDownloadField" get referenced?
I also tried just calling a public function from c++
public void update(String message) {
Log.i("logging", "Hit here")
mMyTextField.setText(message);
}
When C++ calls the Java function "update", the log hits (in logcat) but the textview does not update. Maybe it is an issue with threads but I have no idea how to update the textfield correctly.
The other option is to poll and have Java call C++ to read a variable (state) however that is tedious and not good programming practice.
Any suggestions to tackle this?
Thanks in advance.
Solution
You are right, you must call mMyTextField.setText()
from UI thread. Android Java classes provide multiple ways to do it, e.g. View.post() or Handler.post() or Handler.sendMessage(), or Activity.runOnUiThread(). None of these methods is static, they all need a live this object to work.
There are different approaches to resolve this for your purpose. Probably the minimal change to your code will go as follows:
public class Example extends Activity {
private static Handler staticHandler;
private TextView currentDownloadField;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
this.currentDownloadField.setText(""+ msg.what);
}
}
staticHandler = handler;
setContentView(R.layout.main_view);
currentDownloadField = (TextView)findViewById(R.id.download_field);
}
@Override
protected void onDestroy() {
staticHandler = null;
}
public static void sendMessage(int id) {
staticHandler.sendEmptyMessage(id); // just the what() is filled with the id
}
}
In the code above, error checking is skipped for brevity.
Alternatively, you could pass the reference to your activity (or handler, or downloadFiled) to the C library, and instead of calling CallStaticVoidMethod()
you would use the object global reference and use CallVoidMethod(env, jactivityObj, mid, message)
.
PS I just wonder, how you expect you system to work if you send a jbyteArray
(in Java, that's byte[]
) to a callback method that expects int
?
Update The simple code above is subject to Handler Leak, thus if you want to reuse it in production, please include a static inner class wrapper around the Handler.
Answered By - Alex Cohn
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.