调用 C 函数时 Toast 不显示
Toast not displaying when C function is called
我有一项服务可以使用启动器启动或在启动时运行。问题是当我用 JNI 调用 C 函数时,所有 Toast
通知都不再显示,而如果我只调用 java 方法,Toast
通知显示得很好。是什么原因造成的,我该如何解决?
注:
如果 C 函数做了一些简单的事情,比如回调 java 并终止,Toast
通知将显示。但是,我的函数有一个用于处理中断的无限循环。
ServiceLauncher
public class ServiceLauncher extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
Toast.makeText(getBaseContext(), "onCreate", Toast.LENGTH_LONG).show();
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
finish();
}
}
我的服务
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onDestroy() {
Toast.makeText(this, "Terminated", Toast.LENGTH_LONG).show();
}
@Override
public int onStartCommand(Intent intent,int flags, int startid) {
Toast.makeText(this, "Running", Toast.LENGTH_LONG).show();
javaMethod(); /*toast displays if this is called*/
cMethodFromJNI(); /*toast does not display if this is called*/
return START_STICKY;
}
}
C代码结构
JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv *env, jclass type) {
jmethodID mid = (*env)->GetStaticMethodID(env, type, "javaMethod", "(I)V");
// open and read some fd (not shown)
for (;;) {
// wait on change in fd and act on it (not shown)
callJavaAgain(env,type,mid)
}
}
//close fd (not shown)
exit(0);
}
编辑: 我已经按照下面的答案更改了代码。看来情况和之前一模一样,可能是我漏掉了什么。
修改后的 C 代码结构
JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv * env, jclass type) {
/*cache JVM*/
int status = ( * env) - > GetJavaVM(env, & jvm);
if (status != 0) {
LOGD("failed to retrieve *env");
exit(1);
}
attachedThread();
}
void attachedThread() {
/* get a new environment and attach a new thread */
JNIEnv * newEnv;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // if you want to give the java thread a name
args.group = NULL; // you can assign the java thread to a ThreadGroup
( * jvm) - > AttachCurrentThread(jvm, & newEnv, & args);
jclass cls = ( * newEnv) - > FindClass(newEnv, "fully/qualified/class/name");
jmethodID mid = ( * newEnv) - > GetStaticMethodID(newEnv, cls, "callJavaAgain", "(V)V");
// open and read some fd (not shown)
for (;;) {
// wait on change in fd and act on it (not shown)
intermediaryFunction(newEnv, cls, mid);
}
}
//close fd (not shown)
exit(0);
}
void intermediaryFunction(JNIEnv * newEnv, jclass cls, jmethodID mid) {
//do some stuff (not shown)
( * newEnv) - > CallStaticVoidMethod(newEnv, cls, mid);
}
好的,所以这个话题需要特别注意。 Android docs 讨论 JavaVM 和 JNIEnv 以及如何与它们交互。您需要做的是存储对 JavaVM 的全局引用,该引用可从 C 代码中的所有线程访问。 JVM 允许线程访问 JNIEnv,这可以让您访问 class 的方法,但不能访问。下一个关键问题是您需要将 jobject 'pointer' 存储到从 C 代码(您可能已经知道)回调的 android 对象。确保在获取 JNIEnv 引用的线程上调用 JNIEnv->DetachThread 。
又是这样:
- 获取 JavaVM 引用并将其存储在 C 代码中
- 已 java 调用 C 代码来存储回调的作业对象引用
- 启动一个线程进行循环,在该线程上获取对 JNIEnv 的引用以调用 java
- 在终止线程执行之前调用 DetachThread
另一个麻烦点是从 java 代码访问 c++ 对象,有一个巧妙的技巧,您可以让 c++ 代码 创建 java 对象,并分配给创建的 Java class 上的 private long _cppPointer
成员,然后将此对象添加到您已经指向的 java 对象上的某个集合(例如 activity 或其他东西)
祝你好运。
编辑:我终于想起了another great source关于JNI工作的资料
我有一项服务可以使用启动器启动或在启动时运行。问题是当我用 JNI 调用 C 函数时,所有 Toast
通知都不再显示,而如果我只调用 java 方法,Toast
通知显示得很好。是什么原因造成的,我该如何解决?
注:
如果 C 函数做了一些简单的事情,比如回调 java 并终止,Toast
通知将显示。但是,我的函数有一个用于处理中断的无限循环。
ServiceLauncher
public class ServiceLauncher extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
Toast.makeText(getBaseContext(), "onCreate", Toast.LENGTH_LONG).show();
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
finish();
}
}
我的服务
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onDestroy() {
Toast.makeText(this, "Terminated", Toast.LENGTH_LONG).show();
}
@Override
public int onStartCommand(Intent intent,int flags, int startid) {
Toast.makeText(this, "Running", Toast.LENGTH_LONG).show();
javaMethod(); /*toast displays if this is called*/
cMethodFromJNI(); /*toast does not display if this is called*/
return START_STICKY;
}
}
C代码结构
JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv *env, jclass type) {
jmethodID mid = (*env)->GetStaticMethodID(env, type, "javaMethod", "(I)V");
// open and read some fd (not shown)
for (;;) {
// wait on change in fd and act on it (not shown)
callJavaAgain(env,type,mid)
}
}
//close fd (not shown)
exit(0);
}
编辑: 我已经按照下面的答案更改了代码。看来情况和之前一模一样,可能是我漏掉了什么。
修改后的 C 代码结构
JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv * env, jclass type) {
/*cache JVM*/
int status = ( * env) - > GetJavaVM(env, & jvm);
if (status != 0) {
LOGD("failed to retrieve *env");
exit(1);
}
attachedThread();
}
void attachedThread() {
/* get a new environment and attach a new thread */
JNIEnv * newEnv;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // if you want to give the java thread a name
args.group = NULL; // you can assign the java thread to a ThreadGroup
( * jvm) - > AttachCurrentThread(jvm, & newEnv, & args);
jclass cls = ( * newEnv) - > FindClass(newEnv, "fully/qualified/class/name");
jmethodID mid = ( * newEnv) - > GetStaticMethodID(newEnv, cls, "callJavaAgain", "(V)V");
// open and read some fd (not shown)
for (;;) {
// wait on change in fd and act on it (not shown)
intermediaryFunction(newEnv, cls, mid);
}
}
//close fd (not shown)
exit(0);
}
void intermediaryFunction(JNIEnv * newEnv, jclass cls, jmethodID mid) {
//do some stuff (not shown)
( * newEnv) - > CallStaticVoidMethod(newEnv, cls, mid);
}
好的,所以这个话题需要特别注意。 Android docs 讨论 JavaVM 和 JNIEnv 以及如何与它们交互。您需要做的是存储对 JavaVM 的全局引用,该引用可从 C 代码中的所有线程访问。 JVM 允许线程访问 JNIEnv,这可以让您访问 class 的方法,但不能访问。下一个关键问题是您需要将 jobject 'pointer' 存储到从 C 代码(您可能已经知道)回调的 android 对象。确保在获取 JNIEnv 引用的线程上调用 JNIEnv->DetachThread 。
又是这样:
- 获取 JavaVM 引用并将其存储在 C 代码中
- 已 java 调用 C 代码来存储回调的作业对象引用
- 启动一个线程进行循环,在该线程上获取对 JNIEnv 的引用以调用 java
- 在终止线程执行之前调用 DetachThread
另一个麻烦点是从 java 代码访问 c++ 对象,有一个巧妙的技巧,您可以让 c++ 代码 创建 java 对象,并分配给创建的 Java class 上的 private long _cppPointer
成员,然后将此对象添加到您已经指向的 java 对象上的某个集合(例如 activity 或其他东西)
祝你好运。
编辑:我终于想起了another great source关于JNI工作的资料