3

I am very new to android dev. I've watched a few youtube video's and read some documentation. Today is like day 5, so be patient with me. I am trying to build a BluetoothLE app. I stole some code from here, but when I try to run in on my device. I get a "Unfortunately, (app name) has stopped" message. After commenting out different blocks of code I have pinned it down to the declaration of:

private ScanCallback mScanCallback = new ScanCallback() {};

If I include that line, it crashes. If I don't, then it lives. Here is the full Java Class:

package com.tremor.creech.bluetoothExample;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import java.util.List;
@TargetApi(21)
public class MainActivity extends AppCompatActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private int REQUEST_ENABLE_BT = 1;
    private Handler mHandler;
    private static final long SCAN_PERIOD = 10000;
    private BluetoothLeScanner mLEScanner;
    private ScanSettings settings;
    private List<ScanFilter> filters;
    private BluetoothGatt mGatt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private ScanCallback mScanCallback = new ScanCallback() { }; //If I include this line, then it crashes. If I take it out, then I see my Hello World activity.
}

LogCat errors:

09-15 19:16:17.503    1878-1878/com.roberts.croberts.mantis E/dalvikvm﹕ Could not find class 'com.roberts.croberts.mantis.MainActivity$1', referenced from method com.roberts.croberts.mantis.MainActivity.<init>
09-15 19:16:17.523    1878-1878/com.roberts.croberts.mantis E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.roberts.croberts.mantis, PID: 1878
    java.lang.NoClassDefFoundError: com.roberts.croberts.mantis.MainActivity$1
            at com.roberts.croberts.mantis.MainActivity.<init>(MainActivity.java:154)
            at java.lang.Class.newInstanceImpl(Native Method)
            at java.lang.Class.newInstance(Class.java:1208)
            at android.app.Instrumentation.newActivity(Instrumentation.java:1061)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2119)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2252)
            at android.app.ActivityThread.access$800(ActivityThread.java:139)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1200)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606)
            at dalvik.system.NativeStart.main(Native Method)
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ Thread state: conn_id=0, state=RX_THREAD_WAIT_READ
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ Thread state: conn_id=0, state=RX_THREAD_CLIENT_TX
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ qmi_qmux: TX/RX - RX 30 bytes on conn_id=0
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ 01 1D 00 80 03 01 04 00 00 51 00 11 00 10 03 00
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ BA 0E 00 11 08 00 BE 08 00 07 00 00 00 00
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ qmuxd: TX message on fd=29, to qmux_client_id=2, len=64
09-15 19:16:18.023      461-717/? E/Diag_Lib﹕ Thread state: conn_id=0, state=RX_THREAD_WAIT_POLL
09-15 19:16:18.033      454-454/? E/Parcel﹕ Reading a NULL string not supported here.
09-15 19:16:18.033      454-454/? E/Parcel﹕ Reading a NULL string not supported here.
09-15 19:16:18.033      454-454/? E/Parcel﹕ Reading a NULL string not supported here.
09-15 19:16:18.033      454-454/? E/Parcel﹕ Reading a NULL string not supported here.
3
  • 1
    Scancallback is part of Android 21 (lollipop), are you trying to run your code against a previous version of android ?
    – xiaomi
    Sep 16, 2015 at 4:49
  • My phone is running android 4.4.2, api version 19 or 20, I guess I thought that the @TargetApi(21) decorator was supposed to fix all my problems. Sep 16, 2015 at 15:21
  • The @TargetApi is for Lint, so the IDE won't bother you. developer.android.com/reference/android/annotation/…
    – xiaomi
    Sep 16, 2015 at 16:27

3 Answers 3

9

Unfortunately, to build a BLE application under android you will have to separate android Jelly Bean MR2(18) + Kitkat(19) and Lollipop (21+).

Because you just start, I will give you an overview of how to check.

First declare these variables for both JellyBean/Kitkat and lollipop.

//For Jelly Bean MR2 and Kitkat
private BluetoothAdapter.LeScanCallback leScanCallback;

//For Lollipop.
private BluetoothLeScanner bluetoothLeScanner;
private ScanCallback scanCallback;

Since they are not initiate, there is no error when you run the app. In order to initiate what the target api is, you can use Build:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
    //JellyBean/Kitkat
} else {
    //lollipop+
}

Also don't forget to check if a phone really have the Ble feature and is required or not with the uses feature into the manifest:

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

And check from the PackageManager :

boolean hasBle = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);

This check must be performed because some phones (like the galaxy nexus) is available with jelly bean MR2 but doesn't have the BLE feature)

And last advice, be very careful about callback leaks and stop/remove callbacks properly ;)

2
  • Is it necessary to check both from the manifest and from the packageManager? Or would setting required="true" in the manifest be enough? Sep 16, 2015 at 20:43
  • 2
    I think the manifest is more for Google play to allow or not someone to download the app. For example you can still install from android studio on a non ble phone even is required is true. So always check with the package manager ;) developer.android.com/guide/topics/manifest/…
    – xiaomi
    Sep 16, 2015 at 23:14
2

It's too late to respond on this thread, but I would like to share my idea to overcome this crash on devices below API 21. The ScanCallback class has support from API 21. So when you write a code to implement ScanCallback in your scan code it will cause a crash on lover APIs. I have fixed it following way:

I created one new abstract class which extending ScanCallback class as follows:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)  
public abstract class AppScanCallback extends ScanCallback {}

Now I am using this class instance in my BluetoothService class as follow:

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private AppScanCallback mScanCallback;

And using this variable as follow:

public void startBleScan() {
    if (isEnabled()) {
        if (Build.VERSION.SDK_INT < 21) {
            _bluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mLEScanner = _bluetoothAdapter.getBluetoothLeScanner();
            settings = new ScanSettings.Builder()
                    .build();
            filters = new ArrayList<>();

            mScanCallback = new AppScanCallback() {
                @Override
                public void onScanResult(int callbackType, ScanResult result) {
                    if (Build.VERSION.SDK_INT >= 21) {
                        BluetoothDevice btDevice = result.getDevice();
                        onLeScanResult(btDevice, result.getScanRecord().getBytes());
                    }
                }

                @Override
                public void onBatchScanResults(List<ScanResult> results) {
                    for (ScanResult sr : results) {
                        Log.i("ScanResult - Results", sr.toString());
                    }
                }
                @Override
                public void onScanFailed(int errorCode) {
                    Log.e("Scan Failed", "Error Code: " + errorCode);
                }
            };
            mLEScanner.startScan(filters, settings, mScanCallback);
        }

    }
}

and to stop scan

public void stopBLEScan() {;
        if (Build.VERSION.SDK_INT < 21) {
            _bluetoothAdapter.stopLeScan(mLeScanCallback);
        } else {
            mLEScanner.stopScan(mScanCallback);
        }
}

Hope it will help you out.

1

Instead of this

   private ScanCallback mScanCallback = new ScanCallback() { }; 

Try this

  private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {};
1
  • Is it possible to use both in the same class for API19 and API21?
    – zygimantus
    May 30, 2016 at 20:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.