Question

I am writing a small library that is basically a single custom Activity:

package com.example.android.somelibrary;

public class CustomActivity extends Activity {

    private static final String EXTRA_SOME_THING = "CustomActivity.EXTRA_SOMETHING";

    public static Intent newIntent(Context context, int arg1) {
        Intent intent = new Intent(context, CustomActivity.class);
        intent.putExtra(EXTRA_SOME_THING arg1);
        return intent;
    }

    //... other methods
}

I want users of this library be able to extend this activity:

package com.example.android.myproject;

public class MyCustomActivity extends CustomActivity {
    //... some methods
}

But I want users to be able to rely on my public factory newInstance():

// start MyCustomActivity
int someInt = 0;
Intent intent = MyCustomActivity.newIntent(getApplicationContext(), someInt);
startActivity(intent);

However, when I try to start an activity this way I get an error:

android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.android.myproject/com.example.android.somelibrary.CustomActivity}; have you declared this activity in your AndroidManifest.xml?

I have both activities declared in their respective manifests: CustomActivity in SomeLibrary's manifest, and MyCustomActivity in MyProject's manifest.

I get confused because if I change the start activity call everything works fine:

Intent intent = new Intent(context, MyCustomActivity.class);
startActivity(intent);

This makes me think that I've set all of my module dependencies correctly in MyProject. Is this just a limit of Java? Can I not call static methods in this way (call parent method on child)?

Was it helpful?

Solution

This isn't going to work.

CustomActivity.newIntent() is a static method defined on the CustomActivity class. That static method returns an Intent that points at CustomActivity.class.

So when you call MyCustomActivity.newIntent(), what do you get? An intent that points at CustomActivity.class. Because that's what the method defined in CustomActivity does. Firing that intent won't work, because CustomActivity is not defined in your app's manifest. And that doesn't matter, anyway, because you want to fire up MyCustomActivity, not CustomActivity.

What you'd like is a static method that "knows" which class name it is invoked upon. Unfortunately, this is not possible in Java.

Now, having said that... I'm going to show you how to do it. I don't recommend it, though. (I'll say why a bit later)

Anyway, like I said: static methods don't know what class they're invoked on. Instance methods do, though. So you could do this:

public class CustomActivity extends Activity {

    private static final String EXTRA_SOME_THING = "CustomActivity.EXTRA_SOMETHING";

    public Intent newIntent(Context context, int arg1) {
        Intent intent = new Intent(context, getClass());
        intent.putExtra(EXTRA_SOME_THING arg1);
        return intent;
    }

    //... other methods
}

Same thing, but:

  • It's an instance method, not a static
  • Instead of saying CustomActivity.class (which always points at the CustomActivity class), we say getClass() (which dynamically returns the class of the current instance)

Then if we extend CustomActivity with MyCustomActivity, we can create a MyCustomActivity intent like so:

Intent i = new MyCustomActivity().newIntent(getActivity(), 1);

And it will work.

Why don't I recommend this:

  • It's ugly and weird. If you want to do something and the language makes it ugly and gross, hansolov("Let the language win");
  • The technique it enables is not a good idea.

Let me go on about that a bit. Inheritance is little like injection molded plastic: we see this technique used to make tools that we rely on every day, but if we do it at home, we're probably going to make a mess.

That's because inheritance is harder than it looks. If three activities all need to use one argument of a specific type, does that mean that they should all be the SAME KIND of activity? Probably not. I don't have the space or the time to get into it (and others have said it better than I ever could), but suffice to say that there are a lot of gotchas with sharing code in this way.

Does that mean that they can share some code in another way? Sure! Put the implementation in another class and call through in your own static newIntent() method. It's a little more code, but it doesn't have this pitfall. So I recommend doing that instead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top