Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.NoClassDefFoundError on older Android SDK versions

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

like image 232
Dan Eiref Avatar asked Nov 06 '14 22:11

Dan Eiref


2 Answers

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.

like image 169
Ifor Avatar answered Oct 17 '22 22:10

Ifor


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) {

            }
        };

    }
}
like image 27
TacoEater Avatar answered Oct 17 '22 21:10

TacoEater