2016-01-14 16 views
19

Mamy naprawdę dziwną awarię, która wskazuje na klasy systemowe. Pojawia się przy starcie aplikacji.Awaria aplikacji na początku spowodowana przez NPE w android.content.Context.getString

krytyczny Wyjątek java.lang.RuntimeException: Nie rozpocząć działalność ComponentInfo {com.myapp.android/com.myapp.android.main.BaseMainActivity} java.lang.RuntimeException: Nie można utworzyć aplikacji com.myapp.android.main.MyApp: java.lang.NullPointerException w android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2377) w android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2429) w android.app.ActivityThread.access 800 USD (ActivityThread.java:151) at android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1342) na android.os.Handler.dispatchMessage (Handler.java:110) na android.os.Looper.loop (Looper.java:193) na android.app.ActivityThread.main (ActivityThread.java:5333) na java .lang.reflect.Method.invokeNative (Method.java) w java.lang.reflect.Method.invoke (Method.java:515) na com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java : 828) na com.android.internal.os.ZygoteInit.main (ZygoteInit.java:644) na dalvik.system.NativeStart.main (NativeStart.java) Powodowane przez java.lang.RuntimeException: Nie można utworzyć aplikacji com.myapp.android.main.MyApp: java.lang.NullPointerException at android.app.LoadedApk.makeApplication (LoadedApk.java:529) w andro id.app.ActivityThread.performLaunchActivity (ActivityThread.java:2292) na android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2429) na android.app.ActivityThread.access 800 $ (ActivityThread.java:151) na Androidzie .app.ActivityThread $ H.handleMessage (ActivityThread.java:1342) na android.os.Handler.dispatchMessage (Handler.java:110) na android.os.Looper.loop (Looper.java:193) na Androidzie .app.ActivityThread.main (ActivityThread.java:5333) na java.lang.reflect.Method.invokeNative (Method.java) na java.lang.reflect.Method.invoke (Method.java:515) na com .android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:828) na com.android.internal.os .ZygoteInit.main (ZygoteInit.java:644) w dalvik.system.NativeStart.main (NativeStart.java) Spowodowane przez java.lang.NullPointerException na android.content.Context.getString (Context.java:343) at com.myapp.android.api.singletons.AppTrackingInstance.initAdjust (AppTrackingInstance.java:114) na com.myapp.android.api.singletons.AppTrackingInstance. (AppTrackingInstance.java:92) at com.myapp.android.injection .modules.ApplicationScopeModule.provideAppTrackingInstance (ApplicationScopeModule.java:326) na com.myapp.android.injection.modules.ApplicationScopeModule $$ ModuleAdapter $ ProvideAppTrackingInstanceProvidesAdapter.get (ApplicationScopeModule $$ ModuleAdapter.java: 1618) na com.myapp.android .injection.modules.ApplicationScopeModule $$ ModuleAdapter $ ProvideAppTrackingInst anceProvidesAdapter.get (ApplicationScopeModule $$ ModuleAdapter.java: 1552) na dagger.internal.Linker $ SingletonBinding.get (Linker.java:364) at com.myapp.android.main.MyApp $$ InjectAdapter.injectMembers (MyApp $ $ InjectAdapter.java: 70) at com.myapp.android.main.MyApp $$ InjectAdapter.injectMembers (MyApp $$ InjectAdapter.java: 23) na dagger.ObjectGraph $ DaggerObjectGraph.inject (ObjectGraph.java:281) at com.myapp.android.main.MyApp $ 1.run (MyApp.java:57) na com.myapp.android.main.MyApp.onCreate (MyApp.java:51) na Androidzie.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1007) at android.app.LoadedApk.makeApplication (LoadedApk.java:526) na android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2292) na android.app. ActivityThread.handleLaunchActivity (ActivityThread.java:2429) at android.app.ActivityThread.access 800 $ (ActivityThread.java:151) at android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1342) na android.os .Handler.dispatchMessage (Handler.java:110) na android.os.Looper.loop (Looper.java:193) na android.app.ActivityThread.main (ActivityThread.java:5333) na java.lang.reflect .Method.invokeNative (Method.java) w java.lang.reflect. Method.invoke (Method.java:515) na com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:828) na com.android.internal.os.ZygoteInit.main (ZygoteInit.java: 644) na dalvik.system.NativeStart.main (NativeStart.java)

używamy Dagger 1 nasza aplikacja jest -ed. Moduł

Dagger class

@Module(
    library = true, 
    injects = { 
    MyApp.class 
    } 
) 

public class ApplicationScopeModule { 
    private final MyApp application; 

    public ApplicationScopeModule(MyApp application) { 
    this.application = application; 
    } 

    @Provides 
    @Singleton 
    @ForApplication 
    Context provideApplicationContext() { 
    return application.getApplicationContext(); 
    } 

    @Provides 
    @Singleton 
    AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) { 
    return new AppTrackingInstance(context); 
    } 
} 

MojaApl class

package com.myapp.android.main; 

public class MyApp extends MultiDexApplication { 
    private ObjectGraph objectGraph; 

    @Inject 
    AppTrackingInstance appTrackingInstance; 

    @Override 
    public void onCreate() { 
    super.onCreate(); 
    // workaround for multi-dex enabled projects 
    // taken from http://frogermcs.github.io/MultiDex-solution-for-64k-limit-in-Dalvik/ 
    // multi-dex separates dex files, and some classes going to additional dex file. 
    // Additional .dex files are loaded in Application.attachBaseContext(Context) method 
    // (by MultiDex.install(Context) invokation). It means, that before this moment 
    // we can’t use classes from them. So i.e. we cannot declare static fields 
    // with types attached out of main .dex file. 
    // Otherwise we’ll get java.lang.NoClassDefFoundError. 
    // 
    // the issue should be fixed on the Android level 
    // 
    new Runnable() { 
     @Override 
     public void run() { 
     initFabric(); 
     objectGraph = ObjectGraph.create(getModules().toArray()); 
     objectGraph.inject(MyApp.this); 
     appTrackingInstance.trackAppLaunch(); 
     } 
    }.run(); 
    } 

    private void initFabric() { 
    Fabric.with(MyApp.this, new Crashlytics.Builder().core(new CrashlyticsCore.Builder().disabled(BuildConfig.IS_DEBUG_BUILD).build()).build()); 
    } 

    public List<Object> getModules() { 
    return Arrays.<Object>asList(new ApplicationScopeModule(this)); 
    } 

    public ObjectGraph getObjectGraph() { 
    return objectGraph; 
    } 
} 

AppTrackingInstance:

package com.myapp.android.api.singletons; 

public class AppTrackingInstance { 
    Context context; 
    public AppTrackingInstance(Context context) { 
    this.context = context; 
    initAdjust(); 
    } 

    private void initAdjust() { 
    // "broken" context here 
    String variable = context.getString(R.string.adjust_variable); 
    } 
} 

F ROM wdrażania i StackTrace otrzymujemy przyczyny awarii:

Spowodowane java.lang.NullPointerException na android.content.Context.getString (Context.java:343)

Oznacza to, że kiedy użytkownik uruchamia aplikację, Dagger wstrzykuje do "złamanego" kontekstu aplikacji . Jak to możliwe? Używamy szeroko, Dagger, a ten kontekst jest wstrzykiwany w wielu miejscach bez problemów. Tylko w niektórych szczególnych przypadkach (których nie mogę odtworzyć) aplikacja ulega awarii podczas uruchamiania z powodu zepsutego kontekstu. na różnych urządzeniach i wersje OS pojawi

Crash, głównie na OS 4.x, ale rzadko pojawia się w niektórych wersjach systemu operacyjnego 5.0.2 też: 1 screenshot 2 screenshot 3 screenshot

Ponieważ jest to krach na początku aplikacji, Sprawdziłem to bardzo często i znalazłem dość podobne problemy (1, 2, app crash on update).

Niż zabrałem kilka urządzeń testowych - Nexus 4 (Android 5.0.1), Samsung S3 (Android 4.3) - i starał się odtworzyć problem:

  • otwarta aplikacja z/bez połączenia z Internetem
  • otwórz/zamknij 50x razy aplikacyjne
  • otwarte aplikacje, odinstaluj z rynku gry, zainstalować z powrotem rynku forma zabaw i otwórz ponownie
  • otwartej aplikacji z różnych deeplinks
  • otwartą aplikację z witryny mobilnej
  • zainstalować aplikację z rynku zabaw i nie otwieraj jej. Zimny ​​start z deeplinks
  • otwarta aplikacja z powiadomień Push
  • otwarta aplikacja z różnych lokalizacjach
  • otwarta aplikacja z Niedawnych
  • Wyczyść dane aplikacji i otwartych
  • ręcznie zainstalować stary build produkcji, aktualizację do najnowszej wersji produkcyjnej
  • zainstaluj stary build produkcji, zaktualizuj go do najnowszego z rynku gier
  • przejdź przez aplikację XXX minuty, a następnie zaktualizuj do najnowszej wersji z Play

0 awarii podczas tych testów, ale awarie nadal pojawiają się na urządzeniach użytkowników i nie mam pojęcia, dlaczego tak się dzieje.

Prawdopodobnie przyczyną jest lub Dagger 1, ale nie mogę powiedzieć z pełnym przekonaniem.

+0

Nie jest to prawdziwe rozwiązanie (ale do tego komentarz), ale ulepszenie, które naprawiłoby Twój problem przez uniemożliwienie wywołania getString(): Ustawienia konfiguracji nie powinny być umieszczane w pliku strings.xml! Jeśli masz identyfikator dostosowania śledzenia, powinieneś dodać to jako buildConfigField do swoich smaków i odnieść je za pomocą BuildConfig.ADJUST_TRACKING_ID lub podobnego nazewnictwa. – WarrenFaith

+0

dziękuję, używamy zmiennych i smaków "BuildConfig' dla takich przypadków. –

+0

Czy upewniłeś się, że 'R.string.adjust_variable' została poprawnie przetłumaczona? Ponadto, próbując odtworzyć rozbite, powinieneś spróbować użyć innych języków, –

Odpowiedz

2

krytyczny wyjątek: java.lang.RuntimeException: Nie można rozpocząć działalność ComponentInfo {.......

miałem takie StackTrace raz i to nie jest absolutnie tak przerażające, co brzmi . To znaczy, wyjątek został zgłoszony w onCreate() z MyApp.

tj. context.getResources(), pod warunkiem, że klasa AppTrackingInstance ma wartość NULL i powoduje awarię.

Powodem, dlaczego getResources() powrót null (=> awaria zdarza) dla mnie brzmi jak sytuacji wyścigu, zwłaszcza, że ​​to nie dzieje się za każdym razem (z tego co zrozumiałem z postu).

Ponieważ używam także Dagger1 i MultiDex i nie mam tego problemu, mogę zgadnąć, że możliwe rozwiązanie to rozpoczęcie leniwie ObjectGraph.

Ten fragment działa jak czar dla mnie:

public final class ApplicationScopeModule { 

    private final Context applicationContext; 

    public ApplicationScopeModule(final Context applicationContext) { 
     this.applicationContext = applicationContext; 
    } 

    @Provides 
    @Singleton 
    @SuppressWarnings("unused") // invoked by Dagger 
    public Context provideApplicationContext() { 
     return applicationContext; 
    } 

    @Provides 
    @Singleton 
    @SuppressWarnings("unused") // invoked by Dagger 
    public Analytics provideAnalytics(Context context) { 
     return new DefaultAnalytics(context); 
    } 

    //...<other providers>.. 
} 

MyApplication, która rozciąga Application:

public class MyApplication extends Application { 

    private ObjectGraph objectGraph; 

    private final Object lock = new Object(); 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
    } 

    protected List<Object> getModules() { 
     final ArrayList<Object> modules = new ArrayList<>(); 
     modules.add(new ApplicationScopeModule(getApplicationContext())); 
     return modules; 
    } 

    public ObjectGraph getApplicationGraph() { 
     synchronized (lock) { 
      if (objectGraph == null) { 
       objectGraph = ObjectGraph.create(getModules().toArray()); 
      } 

      return objectGraph; 
     } 
    } 
} 

Następnie w ActivityBase „S - klasę bazową dla każdego Activity używam w aplikacji:

public abstract class FragmentActivityBase extends ActionBarActivity { 

    private ObjectGraph activityGraph; 

    @Override 
    protected void onCreate(final Bundle savedInstanceState) { 
     inject(this); 
     super.onCreate(savedInstanceState); 
    } 

    public void inject(final Object object) { 
     try { 
      if (activityGraph == null) { 
       final MyApplication application = (MyApplication) getApplication(); 
       activityGraph = application.getApplicationGraph(); 
      } 

      activityGraph.inject(object); 
     } catch (IllegalArgumentException e) { 
      //log error 
     } 
    } 
} 

To powinno ci pomóc, ponieważ podczas onCreate() pierwszego Activity (rozszerzenie ActivityBase) zasoby są definitywnie zdefiniowane, więc getResources() nie powinien zwracać wartości null.

Kolejne dwie opcje są

  • Unikaj Multidex
  • Ankietę do momentu, gdy kontekst jest pełny (choć raz getResources() w przypadku braku - kto wie, co jeszcze może być źle, a ja boję to doprowadzi do innych awarii - imho)

Mam nadzieję, że pomoże.

+0

używamy 'Runnable', ponieważ jest to obejście dla aplikacji Multidex. O kontekście - tak, to jest wstrzykiwane poprawnie wszędzie, i nie sądzę, by oddzielny seter naprawił problem. Obiekt nie jest pusty, 'getResources()' wewnątrz kontekstu wyrzuca NPE, to jest dziwne. Dzięki za pomoc. –

+0

@VeaceslavGaidarji hm .. Stacktrace wygląda tak, jakby obiekt Context był pusty i spowodował NPE. W każdym razie proponuję wypróbować Lazy inicjację obiektu obj.graph i trackAppLaunch. Zaktualizuję odpowiedź, gdy wrócę dziś ze stoków narciarskich. Sprawdź także, czy 343-ta linia w klasie Context faktycznie to robi :-) –

+0

nie, obiekt 'context' sam nie jest pusty, niektóre zasoby wewnątrz niego są puste. –

0

Wygląda na to, że obiekt kontekstu nie jest inicjowany. Wystąpił błąd w tym wywołaniu:

@Provides 
@Singleton 
AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) { 
    return new AppTrackingInstance(context); 
} 

Sprawdź, czy w tej metodzie kontekst ma wartość zerową. Uważam, że ten problem istnieje.

+0

Jak widać powyżej, omówiliśmy to - nie, kontekst to ** nie ** null - zasoby kontekstu są puste. –

Powiązane problemy