getResourceId method of TypedArray - android

I am reading docs about getResourceId() method. It says that:
Retrieves the resource identifier for the attribute at index. Note
that attribute resource as resolved when the overall TypedArray object
is retrieved. As a result, this function will return the resource
identifier of the final resource value that was found, not necessarily
the original resource that was specified by the attribute.
So
the first paragraph is clear:
Retrieves the resource identifier for the attribute at index.
the second is clear too:
Note that attribute resource as resolved when the overall TypedArray
object is retrieved.
but what means the 3 paragraph? why it could return not necessarily the original resource id?
As a result, this function will return the resource identifier of the
final resource value that was found, not necessarily the original
resource that was specified by the attribute.

From the
documentation:
TypedArray obtainStyledAttributes (AttributeSet set,
int[] attrs,
int defStyleAttr,
int defStyleRes)
....
When determining the final value of a particular attribute, there are
four inputs that come into play:
Any attribute values in the given AttributeSet.
The style resource specified in the AttributeSet (named "style").
The default style specified by defStyleAttr and defStyleRes
The base values in this theme.

This is because Resource Merging needs to happen before the TypedArray is retrieved.
The Gradle-base build system uses a new merging mechanism for
resources. In previous build system, merging was done by passing a
list of resource folders to aapt which acted as overlays, alongside
the --auto-add-overlay to ensure that new resources in the overlays
would be automatically added (default behavior is for overlays is to
only override existing resources, not create new ones).
One of the goals of the Gradle-based build system was providing more
flexibility, and one frequently asked feature request was the ability
to have more than one resources folders. aapt is not able to handle
this so the new build system introduces a new merging mechanism that
is run ahead of aapt and generates a single, merged, resources folder
that is fed to aapt. This merging has the advantage of being
incremental, both through Gradle's input/output change detection, and
in the way it is implemented (ie it can rerun the merge by only
applying the change in a single file).
The merged resources are coming from 3 types of sources:
The main resources, associated with the main sourceSet, generally located in src/main/res
The variant overlays, coming from the Build Type, and Flavor(s).
The Library Project dependencies, which contribute resources through the res entry in their aar bundle.
E.g. If you use different productFlavors or buildTypes you may have different resources for each flavor. So the one originally set at the time of development may be different from what is actually presented after changing the flavor.

Related

Are integer value of view IDs are always constant in android?

Are int value of R.id.view is always same?
I tried printed it in two devices and it was same.
I also changed name of id but still the value was same.
But will it stay the same in all scenarios?
For any particular app resource, the particular R class associated resource ID will be the same in all scenarios during runtime as this resource ID is generated by aapt during compile time.
From Android Documentation:
Once you provide a resource in your application, you can apply it by referencing its resource ID. All resource IDs are defined in your project's R class, which the aapt tool automatically generates. When your application is compiled, aapt generates the R class, which contains resource IDs for all the resources in your res/ directory.
Thus printing it in two devices will result the same int value, provided that the same apk is used. Also note that the resource id is final in the respective R subclass.
From this answer:
Note that aapt by default makes no attempt to keep these identifiers
the same between builds. Each time the resources change, they can all
get new identifiers. Each time they are built, a new R.java is created
with the current identifiers so your code gets the correct values.
Because of this, you must never persist resource identifiers anywhere
where they can be used across different builds of your app.
Any changes to the resource might make the resource get an new R class resource ID.
Based on the way in which the R class Resource ID are calculated, as described in that answer, I think since you only changed the name of the XML resource ID, and didn't change the type of the resource nor the placement of the respective View object declaration in the respective XML file, the same R class Resource ID is being calculated.
A View ID is generated during Compilation, so Yes the ID will be the same in each Device if the APK is the same. Pay attention that "different compilations could generate different IDs".

Reference resource id from resource

I have a project with multiple flavors, apple and banana. It references a file that is stored in the raw resource directory. I would prefer to avoid naming each one database.sqlite and instead keep the name relevant to the flavor, apple.sqlite and banana.sqlite. In my code, I have a line that reads as
getResources().openRawResource(R.raw.apple);
Is it possible to create an integer resource that could essentially link to the right id, or would I be better off making a class in each flavor that would have the link to it?
You can create dedicated source sets for build types, product flavors (such as apple and banana), and build variants. You can create resource aliases, to give a resource another identifier. You should be able to mash these up to get what you want.
I assume, from your question, that you have apple/res/raw/apple.sqlite and banana/res/raw/banana.sqlite in your project. If so, then:
Create apple/res/values/ids.xml, and in there have <item type="raw" name="whatever">#raw/apple</item>
Create banana/res/values/ids.xml, and in there have <item type="raw" name="whatever">#raw/banana</item>
Use R.raw.whatever to identify this resource in your code in main
(where you can replace whatever with, like, whatever)
Here, you are setting up a common alias (whatever) for the resource, pointing the flavor-specific definition of the alias to the flavor-specific resource.

Android resources key collision

I have two Android projects, a main one (package name com.adip.sampler) and a library that is added to main (package name com.samples.projb). In both of them in resources I have an integer-array with same key: my_int_values:
In main project:
<integer-array name="my_int_values">
<item>10</item>
<item>20</item>
<item>30</item>
<item>40</item>
<item>50</item>
<item>60</item>
<item>70</item>
<item>80</item>
</integer-array>
while in library:
<integer-array name="my_int_values">
<item>34</item>
<item>35</item>
<item>36</item>
<item>37</item>
</integer-array>
In main project from an activity if I am investigating what are the values from these arrays (both main project and library):
protected void showLocalStrings() {
Log.d("RESSampler", "In Main: " + Arrays.toString(getResources().getIntArray(com.adip.sampler.R.array.my_int_values)));
Log.d("RESSampler", "In Libr: " + Arrays.toString(getResources().getIntArray(com.samples.projb.R.array.my_int_values)));
}
then I'm seeing this in Logcat:
In Main: [10, 20, 30, 40, 50, 60, 70, 80]
In Libr: [10, 20, 30, 40, 50, 60, 70, 80]
It seems that main project is overriding the values defined in library array ... I doubled checked if I am reading from resources with correct key and that is ok. Until I took a look in each generated R class. In the main project this is what I have for com.adip.sampler.R.array.my_int_values:
public static final class array {
public static final int my_int_values=0x7f060000;
}
while in library project com.samples.projb.R.array.my_int_values:
public static final class array {
public static final int my_int_values = 0x7f060000;
}
Android tool has generated the same value, so no wonder I am getting this behavior. I can get rid of this behavior if I change the key from one of the integer arrays, but imagine that you have some big projects with a lot of resources, dependency libraries and sooner or later you may bump into this kind of issue: having the same type of resources with the same key value (I've checked with string and with string-array and above behavior appears there as well). So the questions would be:
Why this issue appears? Or if it's not an issue what explains this behavior?
How to avoid it best? I am guessing that trying to have some kind of uniqueness in defining the keys will do the trick, but developers tend to be lazy ...
This appears using multiple variants of latest ADTs and Eclipse versions (Juno and Indigo). Checked on Windows only.
Reading from Library Projects at Android Developers, there are many references where they clearly say merging happens at build time and resources with same IDs overwrite each other.
For resources with same ID from a library and application
In cases where a resource ID is defined in both the application and
the library, the tools ensure that the resource declared in the
application gets priority and that the resource in the library project
is not compiled into the application .apk. This gives your application
the flexibility to either use or redefine any resource behaviors or
values that are defined in any library.
For resources with same ID from two libraries
... your application can add references to multiple library projects, then
specify the relative priority of the resources in each library. This
lets you build up the resources actually used in your application in a
cumulative manner. When two libraries referenced from an application
define the same resource ID, the tools select the resource from the
library with higher priority and discard the other.
Solution suggested in documentation
Use prefixes to avoid resource conflicts
To avoid resource conflicts for common resource IDs, consider using a
prefix or other consistent naming scheme that is unique to the project
(or is unique across all projects).
How to set priority in libraries from command line
If you are adding references to multiple libraries, note that you can
set their relative priority (and merge order) by manually editing the
project.properties file and adjusting the each reference's .n index as
appropriate.
Personally, I would change the names of the resources. If you read anything about naming conventions, it should be something meaningful and "my_int_array" isn't really too helpful, especially in a library that other people or project could potentially use.
Ideally, you want to be able to forget about this for 6 months, come back and look at it and know what that array is for/contains, without having to delve through code to deduce what the array is for by what's done with it.
This post; https://stackoverflow.com/a/7249410/1222199 contains a few different answers on naming conventions.
Finally, not sure about the conflict, couldn't dig anything up. I'm guessing it's to do with the way it's automatically generated, might be worth logging it as a bug and see if you get any response from the development team.

Cannot override library's xml resource with png resource in application?

I have an Android library MyLib containing everything I need for my app (targeting Android 2.2). This library has an XML resource:
drawable/main_background.xml
In my Application MyApp project I reference MyLib. Here I want to override specific resources (i.e. branding). So I added a background image in MyApp:
drawable/main_background.png
Eclipse keeps giving me this error:
[com.mycom.mylib.myapp] res\drawable\main_background.xml:0: error: Resource entry main_background is already defined.
[com.mycom.mylib.myapp] res\drawable\main_background.png:0: Originally defined here.
How can I override the resource in the library project?
You cannot simply override resource ID (it's the resource ID you are overriding, not the actual file) with a file with different extension in Android SDK. However, you can do the trick by putting in your project xml file with the same name (main_background.xml) and fill it in a proper way to display your new file (main_background.png), which you need to rename earlier. All syntax you need is descibed here:
http://developer.android.com/guide/topics/resources/drawable-resource.html
, in your case it could be simply (assuming you put this in your non-library project as main_background.xml, and you have your new png as main_background_new.png):
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/main_background_new" />
With above solution, you could refer to #drawable/main_background from your project and it should use your file included with that project, instead of a library one.
[com.mycom.mylib.myapp] res\drawable\main_background.xml:0: error: Resource entry main_background is already defined.
[com.mycom.mylib.myapp] res\drawable\main_background.png:0: Originally defined here.
I don't believe you can have the same file name even with different extensions. Try naming the png something else.
Now, i've not used overriding, So this seems odd as you'd expect this to be how you override the asset. However i think you've either got the two assets in your lib named the same. And that in your project it might be ok to have an asset with the same name. I would however check that its ok to have different types. XML is different than png, and if you access the asset from code you could get type errors.
Let me clarify the above point. I understand that a library project can have an item with the same Resource ID as an item in your application.
However the error above suggests that both main_background.png and main_background.xml are in the same project ([com.mycom.mylib.myapp]) which i don't believe is correct.
Further reading
This page describes the various types of project including the library project http://developer.android.com/tools/projects/index.html
Now i don't know where i got the impression from but having looked again it simply doesn't state anywhere that you can override a resource by using the same resource name. God knows why i thought that was a feature.
So no, the same rule applies as far as i can tell, that resources have to be named uniquely even across library projects, otherwise the generated resource ids will conflict. (The error your getting)
What is explained is how resource conflicts are managed.
Resource conflicts Since the tools merge the resources of a library
project with those of a dependent application project, a given
resource ID might be defined in both projects. In this case, the tools
select the resource from the application, or the library with highest
priority, and discard the other resource. As you develop your
applications, be aware that common resource IDs are likely to be
defined in more than one project and will be merged, with the resource
from the application or highest-priority library taking precedence.
The system will use the resource with the highest priority, discarding everything else. Whats odd, is that you would think that a compile error wouldn't occur as the compiler should be discarding the resource. This makes me believe that the original poster had the similarly named assets in the same project, and not across the lib and project.
I haven't read anywhere that this is actually an intended feature. Got any links to say otherwise? (comment them)
So one 'solution' to this problem, which I do not consider to be an answer is the following:
Define an XML document in the library in question (we'll call it bunny.xml), and have it refer to another xml of a similar name (bunny_drawn.xml) with the actual content to be displayed.
Then, in the target project, override bunny.xml with another and use it to refer to an image with a different name instead - bunny_image.png
This does not however solve the problem, firstly because we aren't technically overriding a png with an xml (although the effect is somewhat close to that). Secondly because one of the key features of overriding resources is they are overridden, i.e. they are NOT compiled into the APK:
the tools ensure that the resource declared in the application gets
priority and that the resource in the library project is not compiled
into the application .apk
But the bunny_drawn.xml will still be compiled in! We can sort-of overcome the second point, by not only defining the image to be replaced in the target APP, but also replacing the old target bunny_drawn.xml with a blank xml. (or, as Fenix pointed out, you can have the contents of bunny_drawn.xml inside bunny.xml in the first case - the fact still remains that the resource ID can't be replaced...)
So my final conclusion is that this need to be submitted as a bug in the Developer Tools.

Using #string for android:authorities in a ContentProvider

I have a ContentProvider in my manifest, when I define them fully with hardcoded strings it works. E.g.
<provider android:name="com.myprovider" android:authorities="com.myprovider"/>
Works perfect, however the ContentProviders are in a library that gets used by multiple projects, and I don't want authority conflicts, so I attempted to do the following.
<provider android:name="com.myprovider" android:authorities="#string/myProviderAuthority">
This way I should be able to define all my authorities in a single strings.xml file and not have conflicts between apps since I should be able to change them using each apps resource override system.
However, it appears that when I try to build with #string, it gives me a malformed manifest error and says "Provider does not INCUDE (yes it says INCUDE) authorities tribute"
Can I not use a resource string for the authorities tribute, I feel sick everytime I need to maintain constants in two locations. Authority conflicts can be hard to catch by our QA dept, and I don't want things to become out of sync or it could cause confusion. Anybody have any ideas why my code isn't working?
I faced a similar problem but with the android:versionCode attribute. When I tried to define the version code in resources and use a reference to it in the manifest Android Market even forbade me to publish the application. The reason of such behavior turned out to be rather simple. Resources can change depending on current configuration and this value have to be the same in any case.
Probably, this is the reason why content providers with authority references do not work too. And it seems to me that it's not a good idea to use such a reference because there's no guarantee that there will be the only one value for an authority resource in an app. I understand that you can be careful enough to keep a single instance of this resource but there's no special compiler or system checks for this so it cannot be trusted.
Many manifest attributes cannot be specified as a reference to a string -- they must be specified as explicit string values.
The code that parses the manifest is in: frameworks/base/core/java/android/content/pm/PackageParser.java. That class calls, among others, getNonConfigurationString() and getNonResourceString() (which are implemented in: frameworks/base/core/java/android/content/res/TypedArray.java).
getNonConfigurationString() describes itself as:
Retrieve the string value of an attribute that is not allowed to change with the given configurations.
getNonResourceString() describes itself as:
Retrieve the string value for an attribute, but only if that string comes from an immediate value in an XML file. That is, this does not allow references to string resources, string attributes, or conversions from other types. As such, this method will only return strings that come from attributes in an XML file.
The manifest attributes that PackageParser doesn't allow to be taken from resources or from different configurations are listed below.
These attributes are defined in com.android.internal.R.styleable The manifest.xml element attribute name is usually the part of the name after the last '_' in the formal name. For example, the android:authorities attribute in a element in manifest.xml is AndroidManifestProvider_authorities, or com.android.internal.R.styleable.AndroidManifestProvider_authorities. (The number in the lists of attribute names below are the line number of the relevant code in version 4.1.1 of PackageParser.java)
Attributes read by getNonConfigurationString:
917: AndroidManifest_versionName
922: AndroidManifest_sharedUserId
2057: AndroidManifestActivity_parentActivityName
2071: AndroidManifestActivity_permission
2079: AndroidManifestActivity_taskAffinity
2247: AndroidManifestActivityAlias_targetActivity
2330: AndroidManifestActivityAlias_permission
2336: AndroidManifestActivityAlias_parentActivityName
1672: AndroidManifestApplication_name
1683: AndroidManifestApplication_manageSpaceActivity
1697: AndroidManifestApplication_backupAgent
1795: AndroidManifestApplication_permission
1800: AndroidManifestApplication_taskAffinity
1815: AndroidManifestApplication_process
3005: AndroidManifestData_mimeType
3017: AndroidManifestData_scheme
3023: AndroidManifestData_host
3025: AndroidManifestData_port
3031: AndroidManifestData_path
3037: AndroidManifestData_pathPrefix
3043: AndroidManifestData_pathPattern
2527: AndroidManifestGrantUriPermission_path
2533: AndroidManifestGrantUriPermission_pathPrefix
2539: AndroidManifestGrantUriPermission_pathPattern
2579: AndroidManifestPathPermission_permission
2581: AndroidManifestPathPermission_readPermission
2586: AndroidManifestPathPermission_writePermission
2615: AndroidManifestPathPermission_path
2622: AndroidManifestPathPermission_pathPrefix
2629: AndroidManifestPathPermission_pathPattern
2434: AndroidManifestProvider_authorities
2441: AndroidManifestProvider_permission
2443: AndroidManifestProvider_readPermission
2454: AndroidManifestProvider_writePermission
2713: AndroidManifestService_permission
2832: AndroidManifestMetaData_name
1225: AndroidManifestOriginalPackage_name
1981: (parsePackageItemInfo -- I can't tell list of all names)
3258: (Component constructor args.nameres -- I can't tell list of all names)
Attributes read by getNonResourceString:
1806: AndroidManifestApplication_taskAffinity
1821: AndroidManifestApplication_process
1632: AndroidManifestInstrumentation_targetPackage
2891: AndroidManifestPackageVerifier_name
2894: AndroidManifestPackageVerifier_publicKey
1512: AndroidManifestPermission_permissionGroup
1200: AndroidManifestProtectedBroadcast_name
1927: AndroidManifestUsesLibrary_name
1054: AndroidManifestUsesFeature_name
1004: AndroidManifestUsesPermission_name
3308: (Component constructor args.processRes -- I can't tell list of all names)
So, alot of attributes in the manifest.xml file must be specified as explicit string values (ie in quotes) rather than references to strings in strings.xml.

Categories

Resources