Search code examples
javaandroideclipseadt

Added new (partially refactored) code, now getting ClassNotFoundException for a class that never existed


I have the following code in the main activity of my app:

package com.myself.foo.myapp;

import com.someotherfellow.hisapp.OtherClass;
// more imports here

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Object bar = new OtherClass(blah, this, YetAnotherClass.class);
        // ...
    }
}

When I build and launch it (using Eclipse and ADT), the app crashes with an unhandled ClassNotFoundException. The logcat tells me the missing class is

com.myself.foo.myapp.OtherClass

—which is a nonexistent class. The simple class name is that of a class from the other package, but it is prefixed with my package name. Obviously that class will never be found.

When I hover over the constructor call for OtherClass in my code, Eclipse shows the correct class name, with the correct package prefix.

Both packages are present in the same source tree as source files—the respective paths are:

~/src/myapp/src/com/myself/foo/myapp/MainActivity.java
~/src/myapp/src/com/someotherfellow/hisapp/OtherClass.java

What’s causing this behavior, and how can I fix this?


EDIT: Just out of curiosity, I’ve refactored the code, moving OtherClass to the package where Android tries to locate it. Now I get:

java.lang.InstantiationException: java.lang.Class<com.myself.foo.myapp.OtherClass> has no zero argument constructor

Which is indeed true—the constructor of the OtherClass takes three arguments, as supplied in the call.

It looks like for some reason the JVM is calling new OtherClass() (with zero arguments), which does not match any known constructor of the class. If OtherClass resides in a different package, the JVM tries to find a class named OtherClass with a matching constructor signature in the current package. If, however, OtherClass is in the same package, the JVM at least picks the correct class, but obviously does not find a matching constructor.

Which begs the question: what is causing the JVM to look for a zero-argument constructor?

Note that OtherClass is not an Android component: It does not descend from any classes that are part of the Android framework, and is not referenced anywhere in the Manifest. OtherClass is a direct descendant of Object and implements a custom Interface.


Solution

  • I believe I’ve found the error. The app had worked before I imported the new code.

    The code I imported is an app that had a mix of UI and functionality in its main activity. OtherClass is a stripped-down version of the activity in which I only kept the functionality.

    OtherClass was originally called MainActivity, same as the main activity of my app. I renamed it into OtherClass using the Refactor functionality in Eclipse.

    To my surprise, I found the following in my Manifest:

        <activity
            android:name=".OtherClass"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    

    Apparently, the two identical class names (except for the package name), in conjunction with the local reference, confused Eclipse and it changed the name in the Manifest (where it should have been left unchanged). That explains why:

    • The original message about com.myself.foo.myapp.OtherClass missing was because the package here is the default package of the app, as per its Manifest.
    • After moving OtherClass to ensure it would be found, Android still expected this class to be an Activity, which (like other Android system components) is expected to have a zero argument constructor.

    Once I changed the name of the Activity in my Manifest back to MainActivity, the error went away.

    Lesson learned: when two classes in different packages have the same simple name and there are local references to these classes, renaming one of them may confuse Eclipse.

    Recommendation: Commit everything before you refactor. After refactoring, look at the diff and see if the changes are correct. This can potentially save you hours of debugging!