La aplicación se bloquea al iniciarse debido a NPE en android.content.Context.getString
crash dagger (2)
Tenemos un crash realmente extraño, que apunta a las clases del sistema. Aparece en el inicio de la aplicación.
Excepción grave: java.lang.RuntimeException: No se puede iniciar la actividad ComponentInfo {com.myapp.android/com.myapp.android.main.BaseMainActivity}: java.lang.RuntimeException: No se puede crear la aplicación com.myapp.android.main. MyApp: java.lang.NullPointerException en android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2377) en android.app.ActivityThread.handleLapartAp.P.P.P.P.P. : 151) en android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1342) en android.os.Handler.dispatchMessage (Handler.java:110) en android.os.Looper.loop (Looper.java:193) at android.app.ActivityThread.main (ActivityThread.java:5333) at java.lang.reflect.Method.invokeNative (Method.java) at java.lang.reflect.Method.invoke (Method.java:515) at com. android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:828) en com.android.internal.os.ZygoteInit.main (ZygoteInit.java:644) en dalvik.system.NativeStart.mai n (NativeStart.java) Causado por java.lang.RuntimeException: No se puede crear la aplicación com.myapp.android.main.MyApp: java.lang.NullPointerException en android.app.LoadedApk.makeApplication (LoadedApk.java:529) en android .app.ActivityThread.performLaunchActivity (ActivityThread.java:2292) en android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2429) en android.app.ActivityThread.access $ 800 (ActivityThread.java.151) en and.app. $ H.handleMessage (ActivityThread.java:1342) en android.os.Handler.dispatchMessage (Handler.java:110) en android.os.Looper.loop (Looper.java:193) en android.app.ActivityThread.main ( ActivityThread.java:5333) en java.lang.reflect.Method.invokeNative (Method.java) en java.lang.reflect.Method.invoke (Method.java:515) en com.android.internal.os.ZygoteInit $ MethodAndArgsCaller .run (ZygoteInit.java:828) en com.android.internal.os.ZygoteInit.main (ZygoteInit.java:644) en dalvik.system.NativeStart.main (NativeStart.java) Caused por java.lang.NullPointerExpress ception en android.content.Context.getString (Context.java:343) en com.myapp.android.api.singletons.AppTrackingInstance.initAdjust (AppTrackingInstance.java:114) en com.myapp.android.api.ingletons.AppTrack. (AppTrackingInstance.java:92) en com.myapp.android.injection.modules.ApplicationScopeModule.provideAppTrackingInstance (ApplicationScopeModule.Aparticles.as.as.as.as.as.as.as.as.es.as.Contactos) $ ModuleAdapter.java: 1618) en com.myapp.android.injection.modules.ApplicationScopeModule $$ ModuleAdapter $ ProveerAppTrackingInstanceProvidesAdapter.get (ApplicationScopeModule $$ ModuleAdapter.java: 1552) en dagger.internal.Linker $ Singleton Link. : 364) en com.myapp.android.main.MyApp $$ InjectAdapter.injectMembers (MyApp $$ InjectAdapter.java: 70) en com.myapp.android.main.MyApp $$ InjectAdapter.injectMembers (MyApp $$ InjectAdapter.java : 23) en dagger.ObjectGraph $ DaggerObjectGraph.in ject (ObjectGraph.java:281) en com.myapp.android.main.MyApp $ 1.run (MyApp.java:57) en com.myapp.android.main.MyApp.onCreate (MyApp.java:51) en android. app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1007) en android.app.LoadedApk.makeApplication (LoadedApk.java:526) en android.app las personas de que se trata en este caso las personas de que se trata. (ActivityThread.java:2429) en android.app.ActivityThread.access $ 800 (ActivityThread.java:151) en android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1342) en android.os.Handler.dispatchMessage (Handler .java: 110) en android.os.Looper.loop (Looper.java:193) en android.app.ActivityThread.main (ActivityThread.java:5333) en java.lang.reflect.Method.invokeNative (Method.java) en java.lang.reflect.Method.invoke (Method.java:515) en com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:828) en com.android.internal.os.ZygoteInit.main (ZygoteInit.java:644) en dalvik.syste m.NativeStart.main (NativeStart.java)
Usamos Dagger 1
, nuestra aplicación es multidex
.
Módulo de daga:
@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);
}
}
Clase MyApp:
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;
}
}
Clase 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);
}
}
De la implementación y stacktrace obtenemos la causa del fallo:
Causado por java.lang.NullPointerException en android.content.Context.getString (Context.java:343)
Significa que cuando el usuario inicia la aplicación, Dagger
inyecta en el contexto de aplicación " AppTrackingInstance
" de AppTrackingInstance
. ¿Cómo puede ser posible? Utilizamos Dagger
ampliamente, y este contexto se inyecta en muchos lugares sin problemas. Solo en algunos casos específicos (que no puedo reproducir) la aplicación se bloquea al iniciar debido a un contexto roto.
El bloqueo aparece en diferentes dispositivos y versiones del sistema operativo, principalmente en el sistema operativo 4.x, pero rara vez aparece también en algunas versiones del sistema operativo 5.0.2:
Ya que es un fallo en el inicio de la aplicación, lo he investigado mucho y he encontrado problemas bastante similares ( 1 , 2 , bloqueo de la aplicación en la actualización ).
Entonces tomé algunos dispositivos de prueba - Nexus 4 (Android 5.0.1), Samsung S3 (Android 4.3) y traté de reproducir el problema:
- Aplicación abierta con / sin conexión a internet.
- abrir / cerrar 50 veces la aplicación
- Abra la aplicación, desinstálela de Play Market, instale Back Form Play Market y vuelva a abrir
- Aplicación abierta desde diferentes enlaces
- Abra la aplicación del sitio web móvil
- Instala la aplicación de Play Market y no la abras. Arranque en frío desde los enlaces
- Abra la aplicación de notificaciones push
- Aplicación abierta con diferentes locales.
- Abra la aplicación de los recientes
- Borrar datos de la aplicación y abrir
- Instale la compilación de producción anterior, actualice la compilación de producción más reciente manualmente
- Instale la compilación de producción antigua, actualice a la última desde Play Market.
- navegue por la aplicación XXX minutos, luego actualice a la última versión de Play Market
0 se bloquea durante estas pruebas, pero la falla todavía aparece en los dispositivos de los usuarios, y no tengo idea de por qué está sucediendo.
Probablemente, sea causa de multidex
o Dagger 1
, pero no puedo decir con confianza.
Excepción grave: java.lang.RuntimeException: no se puede iniciar la actividad ComponentInfo {.......
Tuve tal apilamiento una vez y no es absolutamente tan aterrador, por lo que parece. Esto significa que se ha lanzado una excepción en onCreate()
de MyApp
.
Es decir, context.getResources()
, que proporcionó a la clase AppTrackingInstance
es nulo y está causando un bloqueo.
La razón por la que getResources()
return null
(=> crash pasa) para mí suena como una condición de carrera , especialmente, ya que no siempre ocurre (por lo que entendí en el post).
Como también estoy usando Dagger1 y MultiDex y no tengo este problema, puedo adivinar que la posible solución sería comenzar a inicializar ObjectGraph
pereza.
Este fragmento funciona como un encanto para mí:
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
, que extiende la 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;
}
}
}
Luego, en ActivityBase
''s - la clase base para cada Activity
que estoy usando en la aplicación:
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
}
}
}
Le debería ayudar, ya que durante onCreate()
de la primera Activity
(extensión de ActivityBase
), los recursos definitivamente ya están definidos, por lo que getResources()
no debería devolver null.
Otras dos opciones son
- Evitar Multidex
- Encueste por el momento, cuando su contexto esté lleno (aunque, una vez que getResources () esté fallando, quién sabe qué más podría estar mal y me temo que dará lugar a otros bloqueos (imho)
Espero que ayude.
Parece que el objeto de contexto no se está inicializando. El error está en esta llamada:
@Provides
@Singleton
AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) {
return new AppTrackingInstance(context);
}
Verifique si el contexto es nulo en este método. Creo que ese problema está ahí.