Monday, November 14, 2011

Resources NotFoundException in Android

Sometimes (ok frequently) I do stupid things.  Here was my offending line:

list.setAdapter(new ArrayAdapter<Foo>(this, android.R.id.text1, foos));

Which was met at runtime by this stack trace:

android.content.res.Resources$NotFoundException: File  from xml type layout resource ID #0x1020014
at android.content.res.Resources.loadXmlResourceParser(Resources.java:1916)
at android.content.res.Resources.loadXmlResourceParser(Resources.java:1871) 
at android.content.res.Resources.getLayout(Resources.java:731)
at android.view.LayoutInflater.inflate(LayoutInflater.java:318)
   

Not sure what I was smokin when I wrote the code.  Was in a hurry.  Problem is with the android.R.id.text1.  The parameter is supposed to be a layout, not an id.

Correct code is something like:


list.setAdapter(
   new ArrayAdapter<Foo>(this, android.R.layout.simple_list_item_1, foos));

Or whatever layout you like.  

This problem was a little obscure because I was just trying to do something very quickly and didn't have any exception handling in place.  Additionally, none of my code appeared in the stack trace.

Since it's not obvious from the stack trace what the problem is, I've posted it here.  I tend to do the same stupid things at approximately 6-month intervals :-)

 




Monday, November 7, 2011

What? Sorry, I missed that.

With all the great stuff that phones do today, why do we have to endure such poor sound quality for phone calls? Please, device makers, fix this problem! I'm tired of struggling to make out what people are saying on the phone. Yeah, yeah, you've only got so much space to work with, but figure it out! It's not that it can't be done. It's that I don't think anybody cares to do it. Anyone else want to make some noise about this?

Thursday, November 3, 2011

NullPointerException in IntentService

0x000000

NullPointerException.  My all time favorite Java exception.  Probably the single most common exception in Java.  Richly ironic, isn't it?  For a language that doesn't have "pointers?"

Anyway, my latest encounter with NPE was one thrown from the Android framework in the constructor of my IntentService-derived class.

Try it:

public class MyService extends IntentService {



    public MyService() {

        super("MySericeName");

        String s = this.getPackageName();




NullPointerException at
android.content.ContextWrapper.getPackageName(ContextWrapper.java:120)

Here's the inheritance hierarchy:

The no-args constructor of Service, which extends ContextWrapper, calls super(null).  So wrapper is wrapping null.

ContextWrapper doesn't do much of anything but delegate calls to the real context that it's given.  So if you try to make any calls on the Context during construction, you will get an NPE.

Your IntentService derived class, although it gets instantiated when the app starts, will not get a Context until it gets "attached."

That happens before your onHandleIntent method gets invoked.  So anything that needs a Context will have to wait until onHandleIntent gets called.


Launching an Intent by Action

I had some trouble getting my implicit intent to launch.  An implicit intent is one that uses just the action name string, without specifying the actual class of the action to launch.

This is an explicit Inent:

new Intent(context, com.example.MyActivity.class);


This is an implicit Intent:

new Intent("com.example.my_action");


My app has two different flavors (see earlier post on supporting this), and each one needs to handle a pending intent (in a user Notification) with different Activity classes.  So, the common code, rather than trying to figure out what Activity class to start -- that would be evil indeed, a library having a dependency on its clients -- it specifies an Action in the pending intent, like this:


PendingIntent.getActivity(
    this, -1, new Intent("MY_ACTION")...


Here is the snippet from the manifest:


<activity android:name="com.example.MyActivity" >
  <intent-filter>
        <action android:name="MY_ACTION" /> 
 </intent-filter>

</activity>

Problem was that I would tap on the notification, but nothing would happen.  I finally noticed the following line in LogCat:

WARN/IntentResolver(61): resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT

Two things I did wrong.  Both were clearly stated in the documentation, but I missed it.

First, the action name MUST be prefixed with the application package, as declared in the manifest.

Second, the intent-filter element must contain the android.intent.category.DEFAULT category, or else Android will not find your activity to launch. 

So the proper intent filter in the manifest should look like this:


<intent-filter>
  <action android:name="com.example.MY_ACTION" /> 
  <category android:name="android.intent.category.DEFAULT" />

</intent-filter> 


And, of course, the action specified in the Intent constructor should be "com.example.MY_ACTION".

Works great after doing this.