2 votes

Android Erreur fatale "Impossible d'instancier l'activité".

Je reçois une erreur fatale dans une application sur laquelle je travaille. Le fichier journal et le code sont les suivants. D'après ce que je peux voir, il n'arrive même pas à l'activité principale.

Fichier journal :

Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0x401db760)
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{A3.cal/A3.cal.A3Activity}: java.lang.NullPointerException
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1739)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1831)
    at android.app.ActivityThread.access$500(ActivityThread.java:122)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1024)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:132)
    at android.app.ActivityThread.main(ActivityThread.java:4123)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:491)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
    at android.content.ContextWrapper.getResources(ContextWrapper.java:81)
    at android.view.View.<init>(View.java:2366)
    at android.view.View.<init>(View.java:2411)
    at android.widget.TextView.<init>(TextView.java:371)
    at android.widget.Button.<init>(Button.java:108)
    at android.widget.Button.<init>(Button.java:104)
    at android.widget.Button.<init>(Button.java:100)
    at A3.cal.A3Activity.<init>(A3Activity.java:32)
    at java.lang.Class.newInstanceImpl(Native Method)
    at java.lang.Class.newInstance(Class.java:1301)
    at android.app.Instrumentation.newActivity(Instrumentation.java:1022)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1730)
    ... 11 more

Código:

package A3.cal;

import java.util.Calendar;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;

public class A3Activity extends Activity {
    public int currentMonth = 0;
    private TextView[] dayTableTV = new TextView[6*7]; 
    private Integer[] dayTable = null;
    public Integer currentYear = 0;
    public int firstDayOfWeek = 0;
    public boolean leapYear = false;
    private PopupWindow createEventPopup = null;
    boolean click = false;
    private Button closeAddEventButton = new Button(this);
    private Button submitAddEventButton = new Button(this);

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        makeTable();

        Button addEvent = (Button) findViewById(R.id.addEvent);
        addEvent.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if(click == false){
                    addEventPopup();
                    click = true;
                }else{
                    createEventPopup.dismiss();
                    click = false;
                }
            }
        });

        Button nextMonthButton = (Button) findViewById(R.id.nextMonthButton);
        nextMonthButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
               changeMonthNext(getMonth(currentMonth, 1));
            }
        });

        Button previousMonthButton = (Button) findViewById(R.id.previousMonthButton);
        previousMonthButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
               changeMonthBack(getMonth(currentMonth, -1));
            }
        });

        closeAddEventButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                createEventPopup.dismiss();
            }
        });

        submitAddEventButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
               submitEvent();
            }

        });

    }

Il y a plus de code après cela, mais pour économiser des caractères, je ne le posterai pas. Si vous en avez besoin, faites-le moi savoir dans les commentaires.

5voto

Dave Newton Points 93112

Initialiser vos boutons ailleurs, comme dans onCreate .


Le problème est que this (le A3Activity ) lorsque le Button sont instanciées dans les initialisateurs de variables d'instance ( closeAddEventButton y submitAddEventButton ).

Les initialisateurs de variables d'instance sont exécutés avant les constructeurs, mais un objet n'est pas complètement initialisé tant que son ou ses constructeurs ne sont pas exécutés. Les objets référencés avant cela seront dans un état indéterminé.

(L'état pourrait être déterminé ; je veux simplement dire que l'instance n'est peut-être pas encore totalement utilisable).

Quelque chose dans le Button Le flux d'initialisation utilise une valeur non initialisée provenant de l'interface de l'utilisateur. A3Activity et lance un NPE . ( JLS 12.5 : Création de nouvelles instances de classe détaille le processus d'initialisation de l'instance).

Notant que les initialisateurs s'exécutent dans l'ordre textuel ( JLS 12.4.2 ), avant les constructeurs, voici une démonstration simple :

public class ThisHere {
    public String aww;
    public String notYet = this.aww;

    GedankenExperiment wat = new GedankenExperiment(this);

    // public class GedankenExperiment {
    //     public GedankenExperiment(thatThere) {
    //         thatThere.aww = ???
    //     }
    // }

    public ThisHere() {
        this.aww = "Hi there";
    }

    public static void main(String[] args) {
        ThisHere thisHere = new ThisHere();
        System.out.println("aww=" + thisHere.aww);
        // > aww=Hi there (initialized in ctor)
        System.out.println("notYet=" + thisHere.notYet);
        // > notYet=null (initialized before ctor)
    }
}

Pour info, le signe révélateur était cette ligne de journal :

at A3.cal.A3Activity.<init>(A3Activity.java:32)

C'est la seule ligne de journal que j'ai vraiment vue, car ma première étape a été de chercher une référence à quoi que ce soit en dehors des paquets Android et Java. Le site <init> signifie que ce n'était pas dans une méthode, donc que cela avait quelque chose à voir avec les initialisateurs de l'instance.

La trace de la pile comprend Button entrées d'initialisation. Cela, plus l'utilisation de this dans leurs constructeurs signifiait que c'était la seule possibilité réelle, d'où le retournement rapide sans trop creuser.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X