java.lang.NoClassDefFoundError on older Android SDK versions
Asked Answered
T

4

9

I released a version of my app to the Google Play and woke up this morning with a number of unhappy customers. The latest version of the app integrates support for a Bluetooth Low Energy (BTLE) heart rate monitor.

The app runs fine on Android 4.3 and 4.4 but crashes on 4.0, 4.1, and 4.2 with the following error.

FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: com.eiref.boatcoach.MainActivity
    at com.eiref.boatcoach.WhatToDo.onClick(WhatToDo.java:274)
    at android.view.View.performClick(View.java:4204)
    at android.view.View$PerformClick.run(View.java:17355)
    at android.os.Handler.handleCallback(Handler.java:725)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:152)
    at android.app.ActivityThread.main(ActivityThread.java:5132)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
    at dalvik.system.NativeStart.main(Native Method)

The error occurs when creating an Intent in a simple Onclick similar to the following…

public void onClick(View v) {
        Intent i = new Intent(this, MainActivity.class);
        startActivity(i);
}

After rushing out and buying a 4.2 tablet so I can replicate the issue, I’ve come to the conclusion that it has to do with this new version of the app supporting Bluetooth LE, which is enabled in SDK 4.3 and later. If I delete all references to Bluetooth in MainActivity then the crash goes away on 4.2 and earlier devices.

My understanding from reading the documentation was that one could write an app that includes Bluetooth LE functionality and it would run on older devices, just so long as one was careful not to execute BTLE code using something like the following...

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) return;
    BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
    mBluetoothAdapter = manager.getAdapter();
//etc.

Therefore my manifest.xml does not include the following as it would prevent download to older devices and I obviously want to maintain a single code base if possible ...

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />    

First question, is my assumption above being able to include BTLE code in SDKs prior to 4.3 correct? If not, do I really need to create two versions of the app... one for folks using 4.3 and later and one for everyone else?

There are many StackOverflow posts about java.lang.NoClassDefFoundError and I think I've read most of the relevant ones. Many suggest that I examine the Java Build Path to make sure that Android Private Libraries and Android Dependencies are checked. They are. Some suggest moving the gen folder before the src folder, but this doesn't seem to make a difference.

I'd post an image of the Eclipse Java Build Path but as this is my first post I don't have the 10 reputation points needed to insert an image, so here's another post that I followed... Android java.lang.NoClassDefFoundError

So, second question, any other thoughts about what could be wrong with the build path?

many thanks in advance.


Update... somehow by asking a question I got enough points to post images of the java build path. As @Ashoke points out I do think it has something to do with the wrong build path or support libraries.

Eclipse Order and Export

Eclipse Libraries

Tindall answered 6/11, 2014 at 22:27 Comment(8)
Does the same code inside your onClick run on >= 4.3 or is there another code path depending on Android version?Seton
You definitely shouldn't need two versions of the application. As far as the uses-feature tag, I would recommend that you do include it, but set android:required="false". I don't really think that's the root issue, but it's good to do anyway.Kobarid
@MichaelKrause - yes exactly the same code path for the different Android Versions.Tindall
@kcoppock - I'll follow your advice but you're right it isn't the root issue.Tindall
Is your main activity implementing any of the BLE callback methods directly? Or do you have any static references to them? All my BLE stuff is in an extending class from my standard stuff and I don't instance this class at all if the OS version is to low.Brenn
@ifor - Thanks! Putting all the BLE code in a separate class and then only instancing this class if BLE is supported seems to have done the trick.Tindall
I wrote an answer to this effect.Brenn
What is the manifest entry for MainActivity?Rustyrut
B
2

Try getting all of the Ble related code into a separate class that you only instance at all if on a device with the necessary API levels. I think without this the call-backs could be leading to your issues.

Brenn answered 11/11, 2014 at 16:7 Comment(0)
U
1

I found that defining the le callback statically in the activity gets an error, but inclosing it in a function with an api check will not produce the same error.

instead of:

    @TargetApi(Build.VERSION_CODES.LOLLIPOP){
    public class RouteMapActivity extends ActionBarActivity

    private BluetoothAdapter.LeScanCallback mScanCallback = new ScanCallback()    {...}

I used:

private void setUpLeCallbacks(){


    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                .build();

        for (BTDeviceName device : mTestPointsToRead) {
            ScanFilter filter = new ScanFilter.Builder().setDeviceAddress(device.le_serial).build();
            filters.add(filter);
        }

        mScanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                super.onScanResult(callbackType, result);
            }

            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
            }
        };
    }else {
        mLeScanCallback= new BluetoothAdapter.LeScanCallback() {

            @Override
            public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {

            }
        };

    }
}
Unspoiled answered 10/2, 2016 at 2:36 Comment(2)
how did you add the global variables with out getting VFY warnings from dalivk?Gerontocracy
@TargetApi(Build.VERSION_CODES.LOLLIPOP) overrides the lint errors.Unspoiled
S
0

This is normal to get this exception on <4.3 devices because BLE didn't exist, so your compiled code can't find the corresponding classes in the OS. Nothing wrong with your build path.

The best solution is indeed to protect your BLE code in a if clause, testing for BLE capability at runtime. You should also filter the OS version in the if like:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {...}

You should declare android:required="false" for this feature, it means that the application prefers to use the feature if present on the device, but that it is designed to function without the specified feature, if necessary.

<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
Sabayon answered 6/11, 2014 at 22:56 Comment(2)
Thanks for the feedback. However, the problem is that MainActivity.class (with my Bluetooth code) can't be found in Android versions <4.3 (causing the exception), therefore we never even get to the 'if' clause.Tindall
I think you should also add an OS check, which will ensure this code is not reached for low OS versions at compilation time (I updated the answer).Sabayon
P
0

now that you are using APIs that may not be available in older Android versions, make sure you are packaging the app with appropriate support library.

For example, see this android sample BluetoothLeGattSample. It uses below support lib.

    dependencies {
       // Add the support lib that is appropriate for SDK 18
       compile "com.android.support:support-v13:19.0.+"
    }

see android docs for eclipse instructions for support library setup

Phila answered 6/11, 2014 at 22:59 Comment(1)
Ashoke - Thanks for your advice. I tried android-support-v13.jar to the Eclipse build path but no joy.Tindall

© 2022 - 2024 — McMap. All rights reserved.