How to launch a new Activity using NativeActivity?

4,822 views
Skip to first unread message

James Woodbridge

unread,
Jul 4, 2012, 11:49:45 AM7/4/12
to andro...@googlegroups.com
My app uses "android.app.NativeActivity" as the root activity.

I use JNI to create a java object retaining a reference to it and call its various members through out my app lifecycle.

At one point i pass back a reference to my native activity through state->activity. Using this reference i try to launch a new intent to display a new Java based Activity but it just crashes. How do i get a valid activity to launch a new activity in this instance? And is this the correct approach ?

Jim


David Given

unread,
Jul 4, 2012, 12:13:50 PM7/4/12
to andro...@googlegroups.com
James Woodbridge wrote:
[...]
> At one point i pass back a reference to my native activity through
> state->activity. Using this reference i try to launch a new intent to
> display a new Java based Activity but it just crashes. How do i get a
> valid activity to launch a new activity in this instance? And is this
> the correct approach ?

JNI and keeping references straight is a nightmare; I do much the same
thing but avoid the pain by subclassing NativeActivity and then in my
subclass' onCreate() taking a copy of the instance pointer in a static
variable.

Then I can do a JNI static method call into my subclass, which pulls the
context out of the variable, and so don't need to pass Java object
references back into Java.

(Naturally this will only work if your app is limited to a single
instance per process, which ours is.)

--
┌─── dg@cowlark.com ───── http://www.cowlark.com ─────
│ "Parents let children ride bicycles on the street. But parents do not
│ allow children to hear vulgar words. Therefore we can deduce that
│ cursing is more dangerous than being hit by a car." --- Scott Adams



signature.asc

James Woodbridge

unread,
Jul 4, 2012, 12:26:34 PM7/4/12
to andro...@googlegroups.com
Maybe i am missing something in what your saying, please correct me if ive missed the point.

I have no problem getting the context out of the native activity using mApplication->activity->clazz is fine weather i pass it as a parameter calling Java from C++ or access it through an instance by calling C++ from Java.

The problem i have is with the Activity not working as hoped.

Do you from C++ launch a new activity ?

David Given

unread,
Jul 4, 2012, 1:02:48 PM7/4/12
to andro...@googlegroups.com
James Woodbridge wrote:
[...]
> I have no problem getting the context out of the native activity using
> mApplication->activity->clazz is fine weather i pass it as a parameter
> calling Java from C++ or access it through an instance by calling C++
> from Java.
>
> The problem i have is with the Activity not working as hoped.
>
> Do you from C++ launch a new activity ?

You can't --- you have to do it from Java, which means calling into Java
via JNI.

You said that your app crashed when you tried to launch the new
activity, right? That's usually a sign that the context pointer is bogus
for some reason, and the most common cause of that is not getting the
referencing right.

(Incidentally, be aware that activity->clazz is actually an object
pointer, not a class pointer...)
signature.asc

James Woodbridge

unread,
Jul 4, 2012, 1:13:49 PM7/4/12
to andro...@googlegroups.com
Ok I think i understand, to clarify.....

What your saying is that by trying to use state->activity the referencing probably gets messed up when sending it back to java using JNI ?

So what i need to do is to extend NativeActivity and then store a reference to that in a static member variable correct?

Then when i go to call java through JNI to launch my new Activity i get the static reference out of my extended activity purely on the java side and use that correct?

James Woodbridge

unread,
Jul 4, 2012, 1:30:43 PM7/4/12
to andro...@googlegroups.com
I can confirm that is correct and that David is a genius who i probably owe a Coffee or muffin or beer too if i ever meet him .

Thanks David :)

Dianne Hackborn

unread,
Jul 4, 2012, 2:17:57 PM7/4/12
to andro...@googlegroups.com
You don't need to do that, you can use clazz to call methods on the activity.  This is how the implementation internally works.  I would *strongly* recommend this over stashing things in static variables -- that has all kinds of gotchas and problems.

That said, you will probably want to implement a subclass of NativeActivity that has methods that do the actual startActivity() call, since writing JNI to built up the correct Intent object would be way more trouble.

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.



--
Dianne Hackborn
Android framework engineer
hac...@android.com

Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails.  All such questions should be posted on public forums, where I and others can see and answer them.

James Woodbridge

unread,
Jul 4, 2012, 3:20:27 PM7/4/12
to andro...@googlegroups.com
Hi Dianne ,

So to clarify on the recommended approach:-

Are you suggesting extending the NativeActivity then add additional methods in that activity and calling those in native code using JNI to launch new activities?

If so how can i get MyNativeActivity object in native code if it has already been instantiated when using android.intent.category.LAUNCHER. I understand you can get classes with the class loader and instantiate them but i dont understand how you can get already instantiated jobjects?

Jim

Dianne Hackborn

unread,
Jul 4, 2012, 4:36:57 PM7/4/12
to andro...@googlegroups.com
The activity object is the thing provided by clazz.  I don't understand what your questions has to do with Intent categories or what-not...?

James Woodbridge

unread,
Jul 4, 2012, 4:55:44 PM7/4/12
to andro...@googlegroups.com
Oh i see sorry i was being a fool. I was using the clazz as the context and forgot that it is an Activity and an Activity is a context e.t.c.

That works perfectly with no nasty lingering static references.

Thanks once again for your sage advice :)

David Given

unread,
Jul 4, 2012, 7:09:17 PM7/4/12
to andro...@googlegroups.com
On 04/07/12 20:20, James Woodbridge wrote:
[...]
> If so how can i get MyNativeActivity object in native code if it has
> already been instantiated when using android.intent.category.LAUNCHER. I
> understand you can get classes with the class loader and instantiate
> them but i dont understand how you can get already instantiated jobjects?

activity->clazz will point at the NDK app's main activity object,
regardless of whether it's a plain NativeActivity or a MyNativeActivity.

--
┌─── dg@cowlark.com ───── http://www.cowlark.com ─────

│ life←{ ↑1 ⍵∨.^3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵ }
│ --- Conway's Game Of Life, in one line of APL

signature.asc

Timothy Graupmann

unread,
Jul 19, 2013, 8:05:29 PM7/19/13
to andro...@googlegroups.com
Thanks for your launching intent example. It worked for me. I modified the Tegra Toolkit JNI example and this worked for me.


bool Engine::launchJavaActivity()
{
    LOGI(">launchJavaActivity");

    // Find the Intent class
    jclass intentClass = mApp->appThreadEnv->FindClass("android/content/Intent");
    EXCEPTION_RETURN(mApp->appThreadEnv);

    jstring actionString = mApp->appThreadEnv->NewStringUTF("tv.ouya.android.setresolutions.MAIN");
    EXCEPTION_RETURN(mApp->appThreadEnv);

    // Get the initializer/constructor for Uri
    jmethodID newIntent = mApp->appThreadEnv->GetMethodID(intentClass, "<init>",
        "()V");
    EXCEPTION_RETURN(mApp->appThreadEnv);

    // Call Intent intent = new Intent(actionString); 
    // Three stages: alloc object, then call init on it (init is the constructor)
    // then call the set functions
    // We SHOULD be able to set the action string and Action in <init>; it works on HC and ICS,
    // but GB fails.  So this longer code is safer on GB.
    jobject intent = mApp->appThreadEnv->AllocObject(intentClass);
    EXCEPTION_RETURN(mApp->appThreadEnv);

    mApp->appThreadEnv->CallVoidMethod(intent, newIntent);
    EXCEPTION_RETURN(mApp->appThreadEnv);

    jmethodID setAction = mApp->appThreadEnv->GetMethodID(intentClass, "setAction",
        "(Ljava/lang/String;)Landroid/content/Intent;");
    EXCEPTION_RETURN(mApp->appThreadEnv);

    // Set the action
    mApp->appThreadEnv->CallObjectMethod(intent, setAction, actionString);
    EXCEPTION_RETURN(mApp->appThreadEnv);

    // startActivity(launchBrowser); 
    jclass activityClass = mApp->appThreadEnv->FindClass("android/app/Activity");
    EXCEPTION_RETURN(mApp->appThreadEnv);

    jmethodID startActivity = mApp->appThreadEnv->GetMethodID(activityClass,
        "startActivity", "(Landroid/content/Intent;)V");
    EXCEPTION_RETURN(mApp->appThreadEnv);

    mApp->appThreadEnv->CallVoidMethod(mApp->appThreadThis, startActivity, intent);
    EXCEPTION_RETURN(mApp->appThreadEnv);

    return true;
}



Thanks,

~Tim Graupmann




On Tuesday, April 23, 2013 12:07:22 PM UTC-7, Alejandro Barbieri wrote:
Hi this works for me:

In your NativeActivity:

void launchJavaActivity(){

// Attaches the current thread to the JVM.

jint lResult;

jint lFlags = 0;


EngineAndroid * pEngine = (EngineAndroid*)Engine::InstancePtr();

JavaVM* lJavaVM = pEngine->GetNativeActivity()->vm;    //<---- replace this with your NativeActivity pointer

JNIEnv *lJNIEnv = 0;


lResult=lJavaVM->AttachCurrentThread(&lJNIEnv,NULL);

if (lResult == JNI_ERR) {

return;

}

jobject lNativeActivity =  pEngine->GetNativeActivity()->clazz;

jclass ClassNativeActivity = lJNIEnv->FindClass("android/app/NativeActivity");

jclass contextClass = lJNIEnv->FindClass("android/content/Context");

if(contextClass == 0)

return;

jmethodID startActivityMethodId = lJNIEnv->GetMethodID(contextClass, "startActivity", "(Landroid/content/Intent;)V");

if(startActivityMethodId == 0)

return;

jclass intentClass = lJNIEnv->FindClass("android/content/Intent");

if(intentClass == 0)

return;

jmethodID intentConstructorMethodId = lJNIEnv->GetMethodID(intentClass, "<init>", "()V");

if(intentConstructorMethodId == 0)

return;

jmethodID intentSetActionMethodId = lJNIEnv->GetMethodID(intentClass, "setAction", "(Ljava/lang/String;)Landroid/content/Intent;");

if(intentSetActionMethodId == 0)

return;

jmethodID getClassLoader = lJNIEnv->GetMethodID(ClassNativeActivity,"getClassLoader", "()Ljava/lang/ClassLoader;");

if(getClassLoader == 0)

return;

jobject cls = lJNIEnv->CallObjectMethod(lNativeActivity, getClassLoader);

if(cls == 0)

return;

jclass classLoader = lJNIEnv->FindClass("java/lang/ClassLoader");

if(classLoader == 0)

return;

jmethodID findClass = lJNIEnv->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");

if(findClass == 0)

return;

jstring intentString = lJNIEnv->NewStringUTF("ar.com.eudaimonia.JAVAACTIVITY);

if(intentString == 0)

return;

jclass marketActivityClass = (jclass)lJNIEnv->CallObjectMethod(cls, findClass, intentString);

if(marketActivityClass == 0)

return;

jobject intentObject = lJNIEnv->NewObject(intentClass,intentConstructorMethodId);

if(intentObject == 0)

return;

lJNIEnv->CallVoidMethod(intentObject, intentSetActionMethodId,intentString);

lJNIEnv->CallVoidMethod(lNativeActivity, startActivityMethodId, intentObject);

lJavaVM->DetachCurrentThread();

}

In the AndroidManifest.xml  define your Java Activity :

<activity   android:name=".JAVAACTIVITY"  android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >  

    <intent-filter> 

      <action android:name="ar.com.eudaimonia.JAVAACTIVITY" />  

        <category android:name="android.intent.category.DEFAULT" />  

    </intent-filter>   

  </activity>

Reply all
Reply to author
Forward
0 new messages