0

I have a C++ NDK plugin (.so) that I want to modify an Activity layout on Android. This plugin needs to support push notifications and a listener interface.

For that, I am aware I need to use JNI interface to initiate requests to Java and also receive events back from the UI elements. However where I am stuck is how my C++ has access to the JVM or JEnv properly and beyond.

Warning all psudo-code. My questions and confusion are inline.

public class MyActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new TestRenderer().Start()
    }
}

class TestRenderer {
    // Messages to the plugin.
    void native Start();

    // Messages from the plugin.
    void OnCreated() {
        // Do something.
    }

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

Which will in then call into JNI

JNIEXPORT void JNICALL Java_TestRenderer_Start(JNIEnv* env, jobject obj)
{
    // Create my C++ class that does all the work!
    TestRenderer renderer = new TestRenderer();
    renderer.Start();
}

However, when it comes time for my C++ TestRenderer to call into its own Java, this is where I draw a blank as to what to do here.

class TestRenderer
{
public:
    ...
    void Start()
    {
        // Create a relative view.
        CreateRelativeView();
    }

private:
    void CreateRelativeView()
    {
        // JNI create my java class and find method.
        // Call method against against that Java class.
        // What JVM, JEnv, or jobject should I use?
        // AT THIS EXACT POINT I NEED ACCESS TO THE ACTIVITY CONTEXT.
        // new CustomView(context); <--- ?????
    }

    // Also, how do I ensure I call OnCreated on the right Java object.
    void OnCreated()
    {
        ...
        // Call method against Java TestRenderer on the right jobject.
    }
};

Now for the plugin's Java

public class CustomView extends RelativeLayout {
    public CustomView(Context context) {
        super(context);

        // Call back into native
        OnCreated();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // Call back into native
        OnCreated();
    }

    // My listener call I would want invoked to go back through plugin to client.
    public native OnCreated();

    static {
        // I assume back to the same plugin?
        System.loadLibrary("testrenderer");
    }
}

I know this sounds kind of a convoluted but if I am able to go through native in the middle layer, it solves so many shared code problems across other platforms. I feel like this is completely possible, but I'm drawing a blank.

Thank you in advance.

gravityab
  • 21
  • 4
  • The `JavaVM*` is safe to cache, so you can take the one you receive in `JNI_OnLoad` and save it in a global variable. A `JNIEnv*` is _not_ safe to cache, so it takes a bit of work to obtain a valid `JNIEnv*` from an arbitrary thread. See [this answer](https://stackoverflow.com/questions/30026030/what-is-the-best-way-to-save-jnienv/30026231#30026231). – Michael Jun 04 '18 at 08:01
  • I see. So based on what you said, it sounds like the best approach would be to store the Activity in some sort of singleton value to be retrieved from GetEnv/AttachCurrentThread that utilized the cached JavaVM from JNI_OnLoad. It also sounds like ideally, I would want to cache this jobject I create to maintain the lifetime CustomView created from native... How do I know which CustomView corresponds to what TestRenderer though? My guess is I'll implement the JNI for "public native OnCreated();" and there will be some way to compare the jobjects? – gravityab Jun 04 '18 at 19:00

0 Answers0