Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.NoClassDefFoundError: com.acme.R$layout referencing android library

I have an Android library project with a custom authenticator and an Activity that provides a login screen for the authenticator. The authenticator works fine when included directly in my main app, but I would like to put the authenticator into a separate android library. When I run the main android application project which references this library, I get a 'java.lang.NoClassDefFoundError: com.acme.R$layout' in the Activity's onCreate method when it calls setContentView with the R.layout.

I am using an android gradle build. I have published the libraries to a local maven repository and the main project seems to be building without any issues. I have decompiled the class.dex file in the debug apk in build/outputs/apk and I can see that the R$layout.class file from the library is present, so I am at a complete loss as to what might be the problem.

Here is the service which implements the custom authenticator:

public class AcmeAuthenticatorService extends Service {

public static final String ACME_ACCOUNT_TYPE = "acme-oauth";
public static final String ACME_ACCESS_TOKEN_TYPE = "acme-oauth-access-token";


private static Authenticator authenticator;

private Authenticator getAuthenticator() {
    if (authenticator == null)
        authenticator = new Authenticator(this);
    return authenticator;
}

@Override
public IBinder onBind(Intent intent) {
    if (intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT))
        return getAuthenticator().getIBinder();
    return null;
}

private static class Authenticator extends AbstractAccountAuthenticator {
    private final Context context;

    Authenticator(Context ctx) {
        super(ctx);
        this.context = ctx;
    }

    @Override
    public Bundle addAccount(AccountAuthenticatorResponse response,
                             String accountType,
                             String authTokenType,
                             String[] requiredFeatures,
                             Bundle options) throws NetworkErrorException {
        return makeAuthIntentBundle(response, options);
    }

    private Bundle makeAuthIntentBundle(AccountAuthenticatorResponse response, Bundle options) {
        Bundle reply = new Bundle();
        Intent i = new Intent(context, AcmeAccountAuthenticatorActivity.class);
        i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        i.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
        if (options != null)
            i.putExtras(options);
        reply.putParcelable(AccountManager.KEY_INTENT, i);
        return reply;
    }

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response,
                               Account account,
                               String authTokenType,
                               Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
        return null;
    }

    @Override
    public String getAuthTokenLabel(String authTokenType) {
        return null;
    }

    @Override
    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
        return null;
    }
}
}

Here is the Activity which handles login:

import com.acme.R;

public class AcmeAccountAuthenticatorActivity extends AccountAuthenticatorActivity {
private AccountManager accountManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //NoClassDefFoundError occurs for this line
    setContentView(R.layout.activity_account_auth);

    accountManager = AccountManager.get(this);

    WebView webview = (WebView)findViewById(R.id.web_view);
    WebSettings webSettings = webview.getSettings();
    webSettings.setJavaScriptEnabled(true);
    if ( webview == null ) {
        Log.e("TAG", "Web view not found!!");
    } else {
        //Do authentication
    }
}
}

Here is the layout xml for the login activity:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.acme.AcmeAccountAuthenticatorActivity"
tools:ignore="MergeRootFrame" >

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="8dp">
    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>
</FrameLayout>

In the applications section of the manifest for the library I have:

    <activity android:name="com.acme.AcmeAccountAuthenticatorActivity"
        android:label="Login">
    </activity>

    <service android:name="com.acme.AcmeAuthenticatorService"
             android:permission="com.acme.AUTHENTICATE_ACME_ACCOUNTS">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator"/>
    </service>

And the build.gradle for the library:

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
    }
}

apply plugin: 'com.android.library'
apply plugin: 'maven-publish'

version '1.0-SNAPSHOT'
group 'com.acme'

repositories {
    mavenLocal()
    mavenCentral()
}

android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.acme.test"
        minSdkVersion 16
        targetSdkVersion 19
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-project.txt'), 'proguard-rules.txt'
        }
    }
}

dependencies {
    compile 'com.android.support:support-v4:19.0.0'
    compile 'com.android.support:appcompat-v7:19.0.0'
}

def getArtifactFullPath() {
    return "${buildDir}/outputs/aar/${project.name}-${project.version}.aar"
}

android.libraryVariants
publishing {
    publications {
        maven(MavenPublication) {
            artifact getArtifactFullPath()
        }
    }
}

Then in the android application I do the following to trigger the auth activity:

    final AccountManagerFuture<Bundle> future = accountManager.addAccount(AcmeAuthenticatorService.ACME_ACCOUNT_TYPE, AcmeAuthenticatorService.ACME_ACCESS_TOKEN_TYPE, null, null, this, new AccountManagerCallback<Bundle>() {
        @Override
        public void run(AccountManagerFuture<Bundle> future) {
            try {
                Bundle bnd = future.getResult();
                showMessage("Account was created");
                Log.d("acme", "AddNewAccount Bundle is " + bnd);

            } catch (Exception e) {
                e.printStackTrace();
                showMessage(e.getMessage());
            }
        }
    }, null);

Here is the build.gradle for the android application:

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
    }
}

apply plugin: 'com.android.application'
apply plugin: 'maven'

repositories {
    mavenLocal()
    mavenCentral()
}

android {
    compileSdkVersion 19
    buildToolsVersion '20.0.0'

    defaultConfig {
        applicationId "com.acme.myapp"
        minSdkVersion 16
        targetSdkVersion 19
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

dependencies {
    compile 'com.acme:acme-auth:1.0-SNAPSHOT'
    compile 'com.android.support:support-v4:19.+'
    compile 'com.android.support:appcompat-v7:19.+'
}

and here is the logcat:

E/AndroidRuntime(27992): FATAL EXCEPTION: mainE/AndroidRuntime(27992): Process: com.acme.myapp, PID: 27992
E/AndroidRuntime(27992): java.lang.NoClassDefFoundError: com.acme.R$layout
E/AndroidRuntime(27992):        at com.acme.AcmeAccountAuthenticatorActivity.onCreate(AcmeAccountAuthenticatorActivity.java:29)
E/AndroidRuntime(27992):        at android.app.Activity.performCreate(Activity.java:5231)
E/AndroidRuntime(27992):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
E/AndroidRuntime(27992):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2148)
E/AndroidRuntime(27992):        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
E/AndroidRuntime(27992):        at android.app.ActivityThread.access$800(ActivityThread.java:135)
E/AndroidRuntime(27992):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
E/AndroidRuntime(27992):        at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(27992):        at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime(27992):        at android.app.ActivityThread.main(ActivityThread.java:5001)
E/AndroidRuntime(27992):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(27992):        at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(27992):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
E/AndroidRuntime(27992):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
E/AndroidRuntime(27992):        at dalvik.system.NativeStart.main(Native Method)
W/ActivityManager(  575):   Force finishing activity com.acme.myapp/com.acme.AcmeAccountAuthenticatorActivity
W/ActivityManager(  575):   Force finishing activity com.acme.myapp/.MainActivity
W/ActivityManager(  575): Activity pause timeout for ActivityRecord{42071a70 u0 com.acme.myapp/com.acme.AcmeAccountAuthenticatorActivity t75 f}

Update

I've created a sample project that demonstrates the issue on github. Please see the README for instructions on building and reproducing the error.

like image 965
kldavis4 Avatar asked Aug 07 '14 23:08

kldavis4


1 Answers

As long as the Bug in current Android-Gradle Plugin (Version 0.12.2) is not fixed, the only way to solve the problem, is to remove the applicationId from the build.gradle

I will update this Post, as soon as this bug has been fixed

Update: current version 0.13.3 still produces the same error

like image 98
Murmel Avatar answered Oct 22 '22 00:10

Murmel