2009-11-30

Unavoidable clash of resource IDs when loading classes dynamically???

An application I am working on queries the PackageManager for all BroadcastReceiver that are defined to handle some specific action. These receivers are then listed on the UI for users to choose. This behavior can be done by PackageManager.queryBroadcastReceivers() and qualified receivers are returned from this call in the form of list. Until now, everything is fine. I can see several receivers, either defined in my package or in 3rd-party package. Now, the next step should I go is to load preferences of a receiver. When an user choose a receiver to serve, the receiver provides its extra preferences to my application and I show them on the Extra Settings section. But, something obviously wrong just caught my eyes,

It's impossible that I hard-coded this /res/layout/alarm_text_viewXXX through TextView.setText(CharSequence). It must be done by resource id. So I went on to check if something suspicious in res/values/strings.xml. Every ID is unique in that file. Looks like no problems in it, I thought.

But, this weird /res/layout/alarm_text_viewXXX is something stored in my main application, not in the receiver. I compared two strings.xml from main aplication and 3-rd party receiver and finally I got the cause. When an user choose an action on the UI, the associated receiver is loaded and an addMyPreferences() is called through reflective APIs. This method adds receiver-specific preferences into main application as shown above. The problem is it is called in the main application context. The integral resource ID you have in the receiver's R.java may conflict some other resource IDs in the R.java of the main application because they are compiled independently.

When some TextView.setText(0x7f030001) in addPreferences() is called, it's not the resource from 3-rd party gets linked; it's the resource from main application gets linked into the application which explains why this weird text shown on the UI above.

I am still looking for ways to fix this...... Until fixed, my recommendation my be

Don't use resources in this situation.

Update: The answer of this problem is very simple. Instead of using main application's Context object, get receiver's Context object in addPreferences() from this call,

Context pkgContext = context.getPackageContext("XXX.XXX.XXX.WifiReceiver", 0);

and use this Context object to get the resource string.

pkgContext.getString(R.id.wifi_title);