我有一个服务,既可以使用启动器启动,也可以在启动时运行。问题是当我用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();
}
}
MyService
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);
}
答案 0 :(得分:1)
好的,所以这个话题需要一些注意。 Android docs讨论JavaVM和JNIEnv以及如何与它们进行交互。您需要做的是存储可从C代码中的所有线程访问的JavaVM的全局引用。 JVM允许线程访问JNIEnv,这可以让您访问类的方法,而不是。下一个关键问题是你需要存储一个工作指针'你从C代码(你可能已经知道)回调的android对象。确保在获取JNIEnv引用的线程上调用JNIEnv-> DetachThread 。
再次:
另一个麻烦的问题是从java代码访问c ++对象,这里有一个巧妙的技巧,你可以使用c ++代码创建 java对象,并分配给private long _cppPointer
成员在创建的Java类上,然后将此对象添加到已经指向的java对象上的某个集合(如活动或其他内容)
编辑:我终于回忆起有关JNI工作的another great source信息