Friday, January 25, 2013

P Android Native Extensions Part 2: Accessing R file variables from native extensions

In the previous post, I covered how to bundle the resources with the ANE. In this post I would cover how to access them from the native code, when it runs with the native extension.

Let us understand the issue first. When creating Android native applications or libraries, we use several resources such as images, layout XMLs, ids, strings. At the time of compilation Android generates an R.java file which provides the native code access to these resources. Examples:

R.id.myLayout
R.layout.mywebviewlayout
R.strings.close_button

This works perfectly fine when you run the app natively. However, when we run this code as an extension from the AIR application, the app crashes. A look at the DDMS log would tell you that a null pointer exception occurred at the place you were accessing the R file.

So, what is happening here? Let me explain.

When you compile an Android application, Android runs through all the resources used by the app and assigns them a unique integer id. If you open the R.java file from the gen folder of your app, you'd find something like this:
public static final class drawable
{
    public static final int ic_action_search=0x7f020000;
    public static final int ic_launcher=0x7f020001;
}
public static final class id
{
    public static final int menu_settings=0x7f070001;
    public static final int openwebview=0x7f070000;
}
public static final class layout
{
    public static final int activity_main=0x7f030000;
    public static final int rla_activity_main=0x7f030001;
}

Now, when you exported this code as a JAR, everywhere in the code where the R class was used, the references were replaced by their values.

Take a look at the following code:

    setContentView(R.layout.rla_activity_main);

In your JAR, the code would have compiled like this:

    setContentView(2130903041);

So, R.layout.rla_activity_main has been replaced with it's int value 2130903041

Now, what does AIR do that we don't find this resource, even if it is bundled with the application? Let us see.

Your AIR application has it's own resources plus it would have resources from your native extension and any other native extensions that you use. So, when compiling the final APK, AIR generates it's own R file. The Android SDK would once again loop through all the resources collectively and assign unique integer ID to each of them. Let's have a look at the same values defined in AIR's R.java file:

public static final class drawable
{
    public static final int ic_action_search=2130968576;
    public static final int op_action_some=213137980;
    public static final int ic_launcher=20979292;
}
public static final class id
{
    public static final int menu_settings=2131296275;
    public static final int openwebview=2131296260;
}
public static final class layout
{
    public static final int activity_main=2130837582;
    public static final int rla_activity_main=2130837585;
    public static final int someother_activity_two=2476689229;
}

As you can see, the IDs assigned to each item have changed. And this is where the issue is. Your native code is :
  setContentView(2130903041);

So, it is looking for rla_activity_main with the id 2130903041, however, AIR has assigned it a different id i.e. 2130837585. So, when you run the extension within the AIR app, it tries to find for the resource with if 2130837585, which is not present, hence the null exception.

Solution: AIR to rescue. The FREContext object that we use in our extensions exposes a function named getResourceId("resource_name"). This function would always give you the correct IDs mapped with the AIR's R file.

So, instead of using:
    setContentView(R.layout.rla_activity_main);

use:
     setContentView(extensionContext.getResourceId("layout.rla_activity_main"));

You can follow the same approach for all the resources,

For colors, use extensionContext.getResourceId("color.colorName") For id, use extensionContext.getResourceId("id.my_resource_id")

Also, do note that the extensionContext object is the extension's FREContext and not Android application context. Although, this works without fail, but at times accessing resources via extensionContext can slow down the application majorly. I would cover ways to speed up access to Android resources through the extension contxt. Stay tuned.

Hope this would save someone a few hours of googling atleast. Thanks and do check out the next article in the series. AIR Android Native Extensions: Part 3: Speeding up access of resources in Native extensions

No comments:

Post a Comment