jni调用的函数和native函数不在一个java文件内,调用出错

问题描述如题。下来我贴代码:
这是含有native函数的java文件

 public class ProgressBarOperation {
    static {
        System.loadLibrary("Pressure");
    }

    public native void startMonitor();

    public native void stopMonitor();
}

native函数所在的java文件不能继承Activity之类的。是我哪里搞错了还是什么原因,求大神解释。
接下来就是主文件,继承Activity的

 public class MainActivity extends Activity implements OnClickListener {
    private ProgressBar progressBar;
    private ProgressBarOperation operation;
    private Button btn_start, btn_stop;

    static {
        System.loadLibrary("Pressure");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        operation = new ProgressBarOperation();
        btn_start = (Button)findViewById(R.id.btn_start);
        btn_start.setOnClickListener(this);
        btn_stop = (Button)findViewById(R.id.btn_stop);
        btn_stop.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_start) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    operation.startMonitor();
                }
            }).start();
        } else if (v.getId() == R.id.btn_stop) {
            operation.stopMonitor();
        }
    }

    public void setPressure(int pressure) {
        Log.e("pressure", "" + pressure);
        progressBar.setProgress(pressure);
    }
}

接下来就是自己编写jni文件了。中间自动生成头文件就不说了
我把整个cpp文件全部贴过来

 #include <jni.h>
#include <stdlib.h>
#include <unistd.h>
#include "com_example_pressuretest_ProgressBarOperation.h"

int getPressure() {
    return rand() % 100;
}

int flag = JNI_TRUE;

JNIEXPORT void JNICALL Java_com_example_pressuretest_ProgressBarOperation_startMonitor(
        JNIEnv *env, jobject obj) {
    while (flag) {
        sleep(1);
        //拿到字节码对象
        jclass clazz = env->FindClass("com/example/pressuretest/MainActivity");
        jmethodID methodId = env->GetMethodID(clazz, "setPressure", "(I)V");
        jobject dpobj= env->NewObject(clazz, methodId);
        env->CallVoidMethod(dpobj, methodId, getPressure());
    }
}

JNIEXPORT void JNICALL Java_com_example_pressuretest_ProgressBarOperation_stopMonitor(
        JNIEnv *env, jobject obj) {
    flag = JNI_FALSE;
}

env->CallVoidMethod(dpobj, methodId, getPressure());这句话一直报错,错误提示也看不来究竟错在哪里

5个回答

我看错了,这个obj代表的是ProgressBarOperation 的对象,你要想办法把MainActivity 的对象传进来或者可以取到,而不能直接去new一个

shihengzhen101
AlbertS 回复勇敢地追:解决了问题就好,帮你解决了问题也使我自己进步了,望及时采纳答案
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复AlbertS: 可以了也,谢谢你哦。一开始我定义的静态方法直接返回Context,肯定不行;如果里面直接调用setPressure的话就可以了,连获取对象都不要。再次感谢哦
接近 4 年之前 回复
shihengzhen101
AlbertS 回复勇敢地追: ProgressBarOperation 这个类可以是由于他可以作为工具类,随便申对象,但是MainActivity 不可以
接近 4 年之前 回复
shihengzhen101
AlbertS 回复勇敢地追:变通一下,还是在MainActivity 类中定义一个自身类型的静态变量selfObject,在成员方法onCreate中将this指针赋值给这个静态变量selfObject,然后在定义一个静态方法SelfStatic(),方法中调用selfObject.setPressure() , 然后你在cpp中调用静态方法 可以吗?我感觉理论上说的通啊!
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复AlbertS: jclass clazz = env->FindClass("com/example/pressuretest/ProgressBarOperation"); jmethodID methodId = env->GetMethodID(clazz, "set", "()V"); jobject jobt = env->AllocObject(clazz); env->CallVoidMethod(jobt, methodId, getPressure());
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复AlbertS: 如果你在ProgressBarOperation 定义一个非静态函数是可以调用的。代码如下:我试过了,可行
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复AlbertS: 你之前不是说“在MainActivity 类中定义一个自身类型的变量,在成员方法中将this指针赋值给这个变量,在写一个静态方法获得这个变量,把参数类型对应好,这样的就可以通过静态方法取到MainActivity 的对象了”这句话么?在cpp文件里面要想调用静态方法必须要有GetStaticMethodID这个函数。这个函数第三个参数是函数参数的签名,这个参数是java类型的数据,没有context这个类型。不知道你明白了没?
接近 4 年之前 回复
shihengzhen101
AlbertS 我表示看不懂你在说什么
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复AlbertS: 我试过了,不可能实现。因为如果需要一个静态函数来传参的话,返回值一定是Context。而GetStaticMethodID后面最后的参数只能是java类型的,没有context,所以无法通过这个方法获得MainActivity的jobject。谢谢你啦
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复AlbertS: 我看了一下,如果是ProgressBarOperation 这个类,是不是就可以重新申请对象?如果是MainActivity就不能?
接近 4 年之前 回复
shihengzhen101
AlbertS 你只能设法获取原来的创建的对象
接近 4 年之前 回复
shihengzhen101
AlbertS 回复勇敢地追:这个类不能重新申请对象
接近 4 年之前 回复
lxj1137800599
勇敢地追 jobject jobt = env->AllocObject(clazz); 用这个方法还是不行
接近 4 年之前 回复
shihengzhen101
AlbertS 回复AlbertS: 在MainActivity 类中定义一个自身类型的静态变量
接近 4 年之前 回复
shihengzhen101
AlbertS 回复勇敢地追: 我每次也是上网查,我想了一个方法你可以试一试,在MainActivity 类中定义一个自身类型的变量,在成员方法中将this指针赋值给这个变量,在写一个静态方法获得这个变量,把参数类型对应好,这样的就可以通过静态方法取到MainActivity 的对象了,我只是这么想想,你看看这样能不能实现吧
接近 4 年之前 回复
lxj1137800599
勇敢地追 那该怎么去传啊?没有这方面的资料啊
接近 4 年之前 回复

你先看看这个clazz 有没有正确的取到,输出日志查一下

lxj1137800599
勇敢地追 这个是取得到的,我用的静态方法都能成功
接近 4 年之前 回复

这个主活动好像不能直接像这样jobject dpobj直接new一个对象,要用函数传进来的那个obj,就是函数JNIEXPORT void JNICALL Java_com_example_pressuretest_ProgressBarOperation_startMonitor(
JNIEnv *env, jobject obj)
的第二个参数

lxj1137800599
勇敢地追 改成第二个参数以后直接就在CallVoidMethod里面报了空指针的错误
接近 4 年之前 回复

我现在再把我的问题补充一下

 JNIEXPORT void JNICALL Java_com_example_pressuretest_ProgressBarOperation_startMonitor(
        JNIEnv *env, jobject obj) {
    while (flag) {
        sleep(1);
        //静态方法
        //jclass clazz = env->FindClass("com/example/pressuretest/MainActivity");
        //jmethodID methodId = env->GetStaticMethodID(clazz, "set", "(I)V");
        //env->CallStaticVoidMethod(clazz, methodId, getPressure());

        //非静态方法
        jclass clazz = env->FindClass("com/example/pressuretest/MainActivity");
        jmethodID methodId = env->GetMethodID(clazz, "setPressure", "(I)V");
        jobject dpobj = env->NewObject(clazz, methodId);
        env->CallVoidMethod(obj, methodId, getPressure());
    }
}

在MainActivity里面重新定义了两个方法

    public void setPressure(int pressure) {
        Log.e("pressure", "" + pressure);
        progressBar.setProgress(pressure);
    }

    public static void set(int pressure) {
        Log.e("pressure", "" + pressure);
    }

静态方法的调用没有问题,有log显示。非静态的就不行,《android开发与艺术探索》中提到的“非静态只是多了一步构造对象的过程”,它没有具体说明怎么构造,所以我就自己构造了一个jobject dpobj = env->NewObject(clazz, methodId);。但是还是有问题

你这个写法完全有问题:
你想在JNI里面回调Activity的方法,那你必须将activity的对象传到JNI里面去,而不是说在JNI里面去New一个Activity的对象,这样的话就不是同一个对象了

我建议你将回调方法setPressure写到ProgressBarOperation 类里面,然后Activity再监听ProgressBarOperation的setPressure方法

lxj1137800599
勇敢地追 回复媒体盒子: 一定需要一个单例模式么?应该不需要吧
接近 4 年之前 回复
lxj1137800599
勇敢地追 回复媒体盒子: 你的意思是说我最好还要把ProgressBarOperation设置成单例模式,然后调用里面的某个方法,然后通知到activity的非静态方法?
接近 4 年之前 回复
luowenlong860502
媒体盒子 回复勇敢地追: 我是说你在JNI里面调用Activity的方法有问题,一般必须弄一个单例模式的类,然后JNI里面调用该类的方法,然后由该类去通知到Activity
接近 4 年之前 回复
luowenlong860502
媒体盒子 回复勇敢地追: 你如果非要调用Activity的非静态方法,那么你必须把该activity的对象传到jni里面去保存起来,然后再在Jni里面通过该对象调用非静态方法,但问题是,你JNI里面根本不知道Activity的生命周期
接近 4 年之前 回复
lxj1137800599
勇敢地追 调用ProgressBarOperation的非静态方法我会,没什么问题。如果我一定要调用Activity的非静态方法呢?是不是就是上面AlbertS说的
接近 4 年之前 回复
lxj1137800599
勇敢地追 你的意思是说“在JNI里面回调Activity的方法”这种写法有问题?还是只能调用某个java类的非静态方法
接近 4 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问