8

Wydałem wersję mojej aplikacji do Google Play i obudziłem się dziś rano z wieloma niezadowolonymi klientami. Najnowsza wersja aplikacji integruje obsługę monitora tętna Bluetooth o niskiej energii (BTLE).java.lang.NoClassDefFoundError na starszych wersjach Androida SDK

Aplikacja działa poprawnie na Androidzie 4.3 i 4.4, ale ulega awarii w wersjach 4.0, 4.1 i 4.2 z następującym błędem.

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) 

Błąd występuje przy tworzeniu woli w prosty Onclick podobny do następującego ...

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

Po śpieszył się i kupowania 4.2 tablet więc mogę powtórzyć problem, doszedłem do wniosek, że ma to związek z tą nową wersją aplikacji obsługującej Bluetooth LE, która jest włączona w SDK 4.3 i nowszych. Jeśli usuniemy wszystkie odwołania do Bluetooth w MainActivity, to awarie znikną na urządzeniach 4.2 i wcześniejszych.

Zrozumiałam, czytając dokumentację, że można napisać aplikację, która zawiera funkcjonalność Bluetooth LE i działałaby na starszych urządzeniach, tak długo jak jeden był ostrożny, aby nie wykonywać kodu BTLE, używając czegoś podobnego do następującego ...

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

Dlatego moja manifest.xml nie zaliczyć jak to uniemożliwić pobieranie do starszych urządzeń, a ja oczywiście chcą zachować jedną bazę kodu, jeśli to możliwe ...

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

Pierwsze pytanie, czy moje założenie powyżej jest w stanie uwzględnić kod BTLE w pakietach SDK przed wersją 4.3? Jeśli nie, czy naprawdę muszę utworzyć dwie wersje aplikacji ... jedną dla osób używających 4.3 i późniejszych i jedną dla wszystkich innych?

Istnieje wiele postów StackOverflow o java.lang.NoClassDefFoundError i myślę, że przeczytałem większość istotnych. Wielu sugeruje, że sprawdzam ścieżkę budowania Java, aby upewnić się, że sprawdzane są prywatne biblioteki Androida i zależności Androida. Oni są. Niektórzy sugerują przeniesienie folderu gen przed folderem src, ale nie wydaje się to mieć znaczenia.

Chciałbym opublikować obraz ścieżki budowy Java Eclipse, ale ponieważ jest to mój pierwszy wpis, nie mam 10 punktów reputacji potrzebnych do wstawienia obrazu, więc oto kolejny wpis, którego przestrzegałem ... Android java.lang.NoClassDefFoundError

Tak, drugie pytanie, inne myśli o tym, co może być nie tak ze ścieżką budowania?

wielkie dzięki z góry.


Aktualizacja ... w jakiś sposób, zadając pytanie Mam wystarczającą liczbę punktów, aby opublikować obrazy ścieżki budowania Java. Jak wskazuje @Ashoke, uważam, że ma to coś wspólnego z niewłaściwą ścieżką budowania lub bibliotekami wsparcia.

Eclipse Order and Export

Eclipse Libraries

+0

Czy ten sam kod wewnątrz onClick uruchamiane na> = 4,3, czy też istnieje inna droga kod w zależności od wersji Androida? –

+0

Zdecydowanie nie powinieneś potrzebować dwóch wersji aplikacji. Jeśli chodzi o znacznik use-feature, polecam, abyś to * uwzględnił, ale ustaw 'android: required =" false "'. Naprawdę nie sądzę, że to jest główny problem, ale i tak warto to zrobić. – kcoppock

+0

@MichaelKrause - tak dokładnie ta sama ścieżka kodowa dla różnych wersji Androida. –

Odpowiedz

0

Jest to normalne, aby uzyskać ten wyjątek na < 4,3 BLE urządzeń, ponieważ nie istnieje, więc skompilowany kod nie może znaleźć odpowiednich klas w systemie operacyjnym. Nic nie jest nie tak z twoją ścieżką budowania.

Najlepszym rozwiązaniem jest rzeczywiście ochrona kodu BLE w klauzuli if, testowanie pod kątem BLE w czasie wykonywania. Należy również filtrować wersję systemu operacyjnego w razie jak:

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

należy zadeklarować android:required="false" dla tej funkcji, oznacza to, że aplikacja woli korzystać z funkcji, jeśli obecne na urządzeniu, ale że jest on zaprojektowany, aby funkcjonować bez określonej funkcji, jeśli to konieczne.

<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" /> 
+0

Dzięki za opinię. Problem polega jednak na tym, że MainActivity.class (z moim kodem Bluetooth) nie można znaleźć w wersjach systemu Android <4.3 (powodując wyjątek), dlatego nigdy nie dojdziemy do klauzuli "if". –

+0

Myślę, że powinieneś również dodać kontrolę systemu operacyjnego, która zapewni, że ten kod nie zostanie osiągnięty dla niskich wersji systemu operacyjnego w czasie kompilacji (zaktualizowałem odpowiedź). – Murphy

0

teraz, gdy używasz interfejsów API, które mogą nie być dostępne w starszych wersjach systemu Android, upewnij się, że pakujesz aplikację z odpowiednią biblioteką obsługi. Na przykład: android sample BluetoothLeGattSample. Używa poniżej wsparcia 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

+0

Ashoke - Dziękuję za poradę. Próbowałem obsługi Android-support-v13.jar na ścieżce budowania Eclipse, ale bez radości. –

2

Spróbuj znaleźć cały kod Ble związanej w oddzielnej klasie, że jedyny przypadek w ogóle, jeśli na urządzeniu z niezbędnymi poziomów API. Myślę, że bez tego odezwy mogą prowadzić do twoich problemów.

1

Stwierdziłem, że zdefiniowanie statystycznego wywołania zwrotnego le w działaniu powoduje błąd, ale włączenie go w funkcję z kontrolką api nie spowoduje tego samego błędu.

zamiast:

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

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

użyłem:

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

      } 
     }; 

    } 
} 
+0

Jak dodać zmienne globalne bez otrzymywania ostrzeżeń VFY od dalivk? –

+1

@ TargetApi (Build.VERSION_CODES.LOLLIPOP) zastępuje błędy lint. – TacoEater