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.
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:
com.myself.foo.myapp.OtherClass
missing was because the package here is the default package of the app, as per its Manifest.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!