2016-04-17 32 views
12

Ciò che la mia app fa è visualizzare una vista sovrapposta del sistema collegata tramite il flag windowManager.addView() e WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY. Questo viene fatto con un servizio, che gestisce la visibilità della vista e alcune altre cose.Non riesco a trovare la causa del mio arresto anomalo con questa traccia dello stack

Tuttavia, sto ricevendo rapporti sugli arresti anomali e non riesco a riprodurli. Inoltre, la traccia dello stack di crash non ha nulla a che fare con il pacchetto della mia app, quindi non posso davvero ottenere la radice di questo problema. Di seguito sono due stacktraces che provengono da fonti diverse, ma sembrano essere correlati:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.measure(int, int)' on a null object reference 
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2388) 
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2101) 
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1297) 
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7011) 
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777) 
at android.view.Choreographer.doCallbacks(Choreographer.java:590) 
at android.view.Choreographer.doFrame(Choreographer.java:560) 
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763) 
at android.os.Handler.handleCallback(Handler.java:739) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:145) 
at android.app.ActivityThread.main(ActivityThread.java:6938) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199) 

.

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getMeasuredWidth()' on a null object reference 
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2484) 
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2181) 
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1293) 
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6599) 
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800) 
at android.view.Choreographer.doCallbacks(Choreographer.java:603) 
at android.view.Choreographer.doFrame(Choreographer.java:572) 
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786) 
at android.os.Handler.handleCallback(Handler.java:815) 
at android.os.Handler.dispatchMessage(Handler.java:104) 
at android.os.Looper.loop(Looper.java:194) 
at android.app.ActivityThread.main(ActivityThread.java:5616) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754) 

Sembra che il sistema operativo (ViewRootImpl) sta causando questo problema perché possiede un riferimento null a mio punto di vista. Quindi non riesco a trovare una soluzione per questo.

Sembra che accada su tutte le versioni di Android da 4.4 e la mia app è Proguarded. Queste tracce di stack sono ottenuti da Google Play segnalazioni di crash Negozio

Ecco come io attribuisco al fine di finestra di sistema come una sovrapposizione:

private void attachToSystemWindows(boolean overlayNavigationBar) { 
    final WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 

    final DisplayMetrics metrics = new DisplayMetrics(); 
    windowManager.getDefaultDisplay().getMetrics(metrics); 

    final boolean isNavBarInBottom = isNavBarInBottom(); 

    final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
      calculateWindowWidth(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight), 
      calculateWindowHeight(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight), 
      0, 
      0, 
      WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 
      0x50728, 
      -3 
    ); 
    params.gravity = Gravity.TOP; 

    windowManager.addView(this, params); 
} 

private boolean isNavBarInBottom() { 
    final boolean isLargeDevice = getResources().getConfiguration().smallestScreenWidthDp >= 600; 
    final int orientation = getResources().getConfiguration().orientation; 

    if (BuildConfig.DEBUG) 
     Log.d("MeshView", "Is NavBar in bottom: " + (isLargeDevice || orientation == Configuration.ORIENTATION_PORTRAIT)); 


    return isLargeDevice || orientation == Configuration.ORIENTATION_PORTRAIT; 
} 

E il mio metodo onMeasure:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    if (BuildConfig.DEBUG) Log.d("MeshView", "OnMeasure"); 
    setMeasuredDimension(
      Math.max(getSuggestedMinimumWidth(), resolveSize(SIZE_MIN_WIDTH, widthMeasureSpec)), 
      Math.max(getSuggestedMinimumHeight(), resolveSize(SIZE_MIN_HEIGHT, heightMeasureSpec)) 
    ); 
} 
+0

Può cortesemente dirmi che quale vista si aggiunge qui: windowManager.addView (questo, params); – KishuDroid

+0

@KishuDroid È solo una classe personalizzata che estende View – BamsBamx

+0

@BamsBamx la mia risposta chiarisce il motivo per cui hai ricevuto questo errore? – djodjo

risposta

5

Incredibile. Alla fine ho trovato la radice del mio problema, ma non il motivo ... stavo facendo qualcosa con il mio punto di vista: volevo cambiare i suoi parametri di layout a seconda del suo orientamento, così ho annullato lo .

poi ho usato un brutto modo per aggiornare params di layout: staccare e quindi collegare nuovamente per WindowManager, proprio come questo:

@Override 
protected void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 

    final ViewGroup.LayoutParams layoutParams = getLayoutParams(); 
    if (!(layoutParams instanceof WindowManager.LayoutParams)) return; //Fix for some ClassCastException in Samsung devices 

    final WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 

    final DisplayMetrics metrics = new DisplayMetrics(); 
    windowManager.getDefaultDisplay().getMetrics(metrics); 

    final boolean isNavBarInBottom = isNavBarInBottom(); 

    layoutParams.width = calculateWindowWidth(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight); 
    layoutParams.height = calculateWindowHeight(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight); 
    windowManager.removeView(this); 
    windowManager.addView(this, layoutParams); 
} 

Credo che succede qualcosa di brutto nella classe ViewRootImpl, inducendolo a non gestisce riattaccare correttamente, causando un errore.

Per risolvere questo problema, basta deve passare attraverso il modo corretto: Utilizzare il metodo WindowManager.updateViewLayout();:

@Override 
protected void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 
    if (BuildConfig.DEBUG) Log.d("MeshView", "OnConfigurationChanged"); 

    final ViewGroup.LayoutParams layoutParams = getLayoutParams(); 
    if (!(layoutParams instanceof WindowManager.LayoutParams)) return; //Fix for some ClassCastException in Samsung devices 

    final WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 

    final DisplayMetrics metrics = new DisplayMetrics(); 
    windowManager.getDefaultDisplay().getMetrics(metrics); 

    final boolean isNavBarInBottom = isNavBarInBottom(); 

    layoutParams.width = calculateWindowWidth(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight); 
    layoutParams.height = calculateWindowHeight(mOverlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight); 
    //windowManager.removeView(this); DON'T DO THIS 
    //windowManager.addView(this, layoutParams); DON'T DO THIS 

    windowManager.updateViewLayout(this, layoutParams); //DO THIS INSTEAD 
} 

Questo sembra risolvere il mio problema. Devo ringraziare l'autore di questo post, che mi ha permesso di trovare questa correzione): Android: change LayoutParams of View added by WindowManager

0

Proguard può causare Problema di Stack Trace, anche se avrebbe dovuto esserci un suggerimento del tuo codice, anche se ulteriori letture di questo hanno bisogno di decifrare, quindi questo mi rimprovera, spegni Proguard via Gradle, poi corri di nuovo e vedi.

+0

Sì, hai ragione. Tuttavia, non è il caso, perché ho già provato e non c'è modo di riprodurlo – BamsBamx

4

Penso che non stiate passando il flag corretto durante la creazione di params.

final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
     calculateWindowWidth(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight), 
     calculateWindowHeight(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight), 
     0, 
     0, 
     WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 
     0x50728, // problem might be here. 
     -3 
); 

Dovrebbe essere uno di these values.

Così usarlo qualcosa di simile (evitare di passare direttamente valori esadecimali):

final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
     calculateWindowWidth(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight), 
     calculateWindowHeight(overlayNavigationBar, isNavBarInBottom, metrics, mNavigationBarHeight), 
     0, 
     0, 
     WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 
     WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, //any flag 
     PixelFormat.TRANSLUCENT 
); 

Ho anche referenced PixelFormat.

Provare a cambiare in questo modo e vedere se funziona.

+0

Ok, la tua risposta potrebbe essere giusta. Lo proverò e una volta controllato se funziona pubblicherò il risultato, grazie. Sono aperto a nuove risposte però;) – BamsBamx

+0

@BamsBamx, ok sicuro :) –

+0

Ho finalmente trovato il problema (pubblicherò come risposta in questa domanda), ma la tua risposta non ha avuto nulla a che fare con il mio problema, perché il problema è stato risolto era un altro Grazie comunque – BamsBamx

0

Ho avuto lo stesso problema nella mia app ho risolto il problema utilizzando

public static final int TYPE_SYSTEM_ALERT; 

invece di TYPE_SYSTEM_OVERLAY. Immagino che la vista sia già stata distrutta prima che venga chiamato onMeasure(). Questo tipo di sovrapposizione crea in qualche modo un problema soprattutto sui dispositivi senza nome pre kit kat.

Nota: se si desidera visualizzare la finestra di sovrapposizione dietro la barra di stato e comunque sopra tutte le altre applicazioni, utilizzare;

public static final int TYPE_PHONE; 

Spero che questo aiuti.

+0

Grazie per la risposta, ma non si applica alla mia app, perché deve sovrapporre il sistema, ma lasciare che le finestre dietro gestiscano tutti i tocchi. 'TYPE_SYSTEM_ALERT' non lo lascerà – BamsBamx

+0

No, lo permetterà solo di usare i parametri giusti. LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, sono quelli che ti servono. Fare riferimento alla documentazione: /** Flag finestra: anche quando questa finestra è selezionabile (il suo * {@link #FLAG_NOT_FOCUSABLE} non è impostato), consentire a qualsiasi evento puntatore * esterno alla finestra di essere inviato alle finestre Dietro. Altrimenti * consumerà tutti gli eventi puntatore, indipendentemente dal fatto che siano * all'interno della finestra. */ public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020; – omersem

4

è breve quando si rimuove la vista da (windowManager.removeView(this)) dopo una catena di chiamate ViewRoot.dispatchDetachedFromWindow() che imposta essenzialmente gli viewroot di visualizzare su null, motivo per cui si ha l'NPE durante l'attraversamento.

in dettaglio: facendo

windowManager.removeView(this);

si essenzialmente chiamare morire sulla ViewRoot della vista, che poi invia MSG_DIE al suo gestore in modo che uccide è più tardi, quando sicuro e aggiunge la vista dell'elenco mDyingViews in WindowManagerGlobal. questo è tutto bene,

comunque chiamando:

windowManager.addView(this, layoutParams);

si forza la morte del ViewRoot e chiamare doDie() e consecutivamente ViewRoot.dispatchDetachedFromWindow() che definisce la fine di nulla, mentre in attraversamento e si avere NPE.

per ulteriori informazioni controllare WindowManagerGlobal e ViewRootImpl

Problemi correlati