Packaging Android resource files within a distributable Jar file
Asked Answered
T

9

48

I am working on some reusable Android code that I would like to distribute to other developers for use within their own applications. The code has some resource dependencies (layouts, xml & png drawables) that I would like to be able to include and reference within a single package (Jar file).

Is this possible, and if so what is the best method to do this?

Tropho answered 3/1, 2010 at 13:45 Comment(1)
can't be done was accepted as the correct answer...interesting.Jewish
F
26

I don't have any optimistic news for you. In general you cannot distribute your own package as easily as you would expect. The main problem is the autogenerated R file. You probably make some references to it - you have your layouts and drawables. User of your library will have his own R file - containing ids for his resources, not for the ones your library provides.

In case of drawable resources you could read them in classic way, without R file. But as far as I know you are not able to inflate layouts from external xml files.

So the ugly hack here would be to distribute your library together with all your resources, which user would have to copy to his "res" folder. To be honest, there is no good solution to your problem... sorry. Regards!

Filial answered 3/1, 2010 at 14:10 Comment(5)
Just to confirm that unfortunately this is indeed the easiest method for now.Nystatin
I attempted the same thing, and was really disappointed that I couldn't package everything in my jar. Is there an enhancement request on b.android.com for this? I'd like to star it up if there is but I can't seem to find anything requesting this feature on there.Jarret
People has been struggling with that for a very long time, so I believe google knows about that problem and its scople. I don't know if there is a change request reported, but even so, it would be a huge change. So, unfortunately, I don't think that goole is going to make such enhancement.Filial
Has anyone actually tried this? If you distribute your resources with jar file, those resources have to be merged with other application's resources. That means that all resource id's are re-generated. And that means that all id's referenced from your jar file are invalid.Valentijn
@Viktor Brešan I have done what my answer states, which works fine and seems far less hacky.Jewish
S
55

Since Android makes the R class automatically with resource files under the /res folder, using the R class as final static is impossible.

I found a nice solution to use a jar file with the res files. Here is how I did it:

  1. In your source code which will be exported in the jar file, don't use R variable because it will be replaced with a final static memory address in compile time. Instead of using R, I made my own method below:

    public static int getResourceIdByName(String packageName, String className, String name) {
        Class r = null;
        int id = 0;
        try {
            r = Class.forName(packageName + ".R");
    
            Class[] classes = r.getClasses();
            Class desireClass = null;
    
            for (int i = 0; i < classes.length; i++) {
                if (classes[i].getName().split("\\$")[1].equals(className)) {
                    desireClass = classes[i];
    
                    break;
                }
            }
    
            if (desireClass != null) {
                id = desireClass.getField(name).getInt(desireClass);
            }
    
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    
        return id;
    }
    

    For example, if you have a layout named main.xml, you can get it by calling the method:

    int id = getResourceIdByName(context.getPackageName(), "layout", "main");
    

    If you have a string whose id is "text1", you can get it by calling method

    int id = getResourceIdByName(context.getPackageName(), "string", "text1");
    

    This method gives you your resource id at runtime. It uses the reflection API to get the status of R at runtime.

    By using this method you can avoid using the R variable.

  2. Copy your res files to your target project.

  3. Build.

Sulfonmethane answered 19/8, 2011 at 5:45 Comment(12)
how do I reach to jar's resource file? I mean i made a jar file which has some drawables and layouts, after I added it into the project i could not reach them. I'm using it like this context.getResources().getDrawable(Util.getResourceIdByName("com.mgandronet", "drawable", "alert")); but I know it's not logical because it gets only resources of the passed context. however I need to reach to the resource of the jar file and get the drawable object. How can I do it?Wedged
Also if the library ships the layout files, then the pacakge name where the 'R' class has to be the Application's which uses the JAR file right? Even then I am getting noClassDefinitionFound exceptionMaltose
@J.J.Kim I used the same implementation. I failed to retrive drawables using the above process. Is there any other method to resolve this ?Shalandashale
@Shalandashale do you use proguard? if so you have to add some exception for your proguard.Sulfonmethane
@J.J. Kim I am using eclipse. I havnt configured any progaurd properties. Can you guide me ?Shalandashale
How would I use this for a Constant where there is no context variable: public static final Style STYLE_ALERT = new Style(LENGTH_LONG, R.color.alert);?Ineligible
@JoshPinter It is a kind of trick. you can use Application class and it has static member that points itself. then you can use it as a context.Sulfonmethane
Does getResourseIdByName make app much slower than find resources by R.*.*?Highwayman
@Highwayman Yes it makes app slower.Sulfonmethane
step 2 . Copy your res files to your target project. What it means. target project is the one which use the jar file ? then we have to distribute resource files as a separate bundle ?Narbada
@preetha Yes, Target project means that the project which is using the jar file.Sulfonmethane
@J.J.Kim - Is it possible to generate DEX file that use its own XML layout file without copying it into target res directory at the time of compilation? I am looking for a way how can I dynamically load updated DEX file with updated layout, so copying resources into target project is not an option for such case.Cyst
F
26

I don't have any optimistic news for you. In general you cannot distribute your own package as easily as you would expect. The main problem is the autogenerated R file. You probably make some references to it - you have your layouts and drawables. User of your library will have his own R file - containing ids for his resources, not for the ones your library provides.

In case of drawable resources you could read them in classic way, without R file. But as far as I know you are not able to inflate layouts from external xml files.

So the ugly hack here would be to distribute your library together with all your resources, which user would have to copy to his "res" folder. To be honest, there is no good solution to your problem... sorry. Regards!

Filial answered 3/1, 2010 at 14:10 Comment(5)
Just to confirm that unfortunately this is indeed the easiest method for now.Nystatin
I attempted the same thing, and was really disappointed that I couldn't package everything in my jar. Is there an enhancement request on b.android.com for this? I'd like to star it up if there is but I can't seem to find anything requesting this feature on there.Jarret
People has been struggling with that for a very long time, so I believe google knows about that problem and its scople. I don't know if there is a change request reported, but even so, it would be a huge change. So, unfortunately, I don't think that goole is going to make such enhancement.Filial
Has anyone actually tried this? If you distribute your resources with jar file, those resources have to be merged with other application's resources. That means that all resource id's are re-generated. And that means that all id's referenced from your jar file are invalid.Valentijn
@Viktor Brešan I have done what my answer states, which works fine and seems far less hacky.Jewish
L
20

layouts:

hard code in your java source


xml & png:

copy xml & png to your project src folder and package to jar, for example

copy a.png to src/com/example/test

load it as following java codes:

    InputStream is = YourClassName.class.getClassLoader().getResourceAsStream("com/example/test/a.png");
    if ( null != is )
    {
          Bitmap bmp = BitmapFactory.decodeStream(is);
Lichfield answered 30/12, 2010 at 10:56 Comment(4)
this actually worked . wonder why it didn't get even a single vote .Klinges
@user558185: but how can i uses the layout and drawableDenesedengue
According with your solution, please could you indicate how to use a layout xml file moved to src/ with setContentView() ? Thanks in advanceVilleneuve
It's not a good option because it will not manage intertionalization, different dpi and so on that make Android coolAngleworm
B
4

I just found that the aapt tool in the Android SDK allows the following option:

   --non-constant-id
       Make the resources ID non constant. This is required to make an R java class
       that does not contain the final value but is used to make reusable compiled
       libraries that need to access resources.

This sounds promising, though I have not tried this so I haven't a clue if it works. I suspect this is to get around the problem addressed by @J.J.Kim's post above.

Boomerang answered 27/4, 2012 at 21:57 Comment(3)
I tried. But it didn't help me. It is a magic. We found a big gradle script that do the same work.Excurrent
@Excurrent can you post the gradle script ?Angleworm
@Excurrent don't worry, I have found a nice solution https://mcmap.net/q/245593/-packaging-android-resource-files-within-a-distributable-jar-fileAngleworm
S
3

You can dynamically get resource id using android method.

int preferences = context.getResources().getIdentifier("preferences", "xml", context.getPackageName());

Later your resources have to be included (copied) to your new project.

Surpassing answered 27/9, 2012 at 11:51 Comment(0)
A
1

I was using user558185 solution but here is a more simple way. If you resources is in a library project (for a Cordova Plugin for example), you can get resource with context.getResources().getIdentifier(name, className, context.getPackageName());

This happen when the library project resources R file is not generated properly and the R packageName did not match the required packageName by the library project.

The usage is the same as user558185 answer, but it use a native Android method.

Angleworm answered 10/4, 2015 at 15:22 Comment(0)
B
1

Hi i think this is a late response but still i just what to inform about AAR

Android ARchive - this file can hold your res and manifest files, so that the other develop can just import the aar to their project and compile their code.

This way we might get Manifest merge errors, the fix for it to use replace property in your manifest and this should be checked before distribution.

Boettcher answered 10/3, 2016 at 14:46 Comment(0)
J
0

you can use abstraction to get the R values from the implementing class (the user).

protected abstract void getMyLayoutTextBox(int myLayoutTextBox);

Then the user has to extend your class (which extends Activity), and implement this method. In your class, you just call getMyLayoutTextBox() and you'll have the correct R value supplied by the user after he implements your distributable jar.

You can read more about abstraction here.

Jewish answered 19/8, 2011 at 14:12 Comment(1)
interesting -1 with no comment. my solution has been implemented by myself for quite some time, very effectively too i might addJewish
C
0

If you are using Eclipse, go to project "Properties->Java Build Path". Under tab "Source", add the res folder you want to pack (e.g. res/raw).

It will add the following line into the .classpath file
<classpathentry kind="src" path="res/raw"/>

After building done, the new jar file will contain the files in res/raw/ folder. You can access them as user558185 suggested.

Cantabrigian answered 6/1, 2015 at 8:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.