Kann nicht auf einen Thread toasten, der Looper.prepare() nicht aufgerufen hat

Lesezeit: 12 Minuten

Ich versuche, einen Test für meine Android-App auszuführen, aber ich erhalte diese Ablaufverfolgung. Was bedeutet das?

java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
at android.widget.Toast$TN.<init>(Toast.java:390)
at android.widget.Toast.<init>(Toast.java:114)
at android.widget.Toast.makeText(Toast.java:277)
at android.widget.Toast.makeText(Toast.java:267)
at dev.android.gamex.CatchGame.onDraw(MainActivity.java:317)
at dev.android.gamex.JamieTest.useAppContext(JamieTest.java:45)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074)

Tests ran to completion.

Meine Testklasse

package dev.android.gamex;


import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.test.InstrumentationRegistry;
import android.widget.TextView;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class JamieTest {

    private static final String FAKE_STRING = "HELLO WORLD";

    private OnScoreListener onScoreListener = new OnScoreListener() {
        @Override
        public void onScore(int score) {
        }
    };

    @Mock
    Canvas can;

    @Test
    public void useAppContext() throws Exception {
        Context appContext = InstrumentationRegistry.getTargetContext();
        assertEquals("dev.android.gamex", appContext.getPackageName());
        CatchGame cg = new CatchGame(appContext, 5, "Jamie", onScoreListener);
        cg.initialize();
        assertTrue(! cg.gameOver);
        cg.onDraw(new Canvas());
        assertTrue(! cg.paused);

    }
}

Der Code, den ich testen möchte, ist unten.

package dev.android.gamex;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import java.util.Random;

public class MainActivity extends AppCompatActivity {
    CatchGame cg;
    public TextView textView;
    public LinearLayout mainLayout;
    String[] spinnerValue = {"Rookie", "Advanced", "Expert", "Master"};
    // start app
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainLayout = new LinearLayout(this);
        mainLayout.setOrientation(LinearLayout.VERTICAL);

        LinearLayout menuLayout = new LinearLayout(this);
        menuLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));

        textView = new TextView(this);
        textView.setVisibility(View.VISIBLE);
        String str = "Score: 0";
        textView.setText(str);
        menuLayout.addView(textView);

        Button button = new Button(this);
        button.setText("Pause");
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                togglePausePlay();
            }
        });
        menuLayout.addView(button);


        Spinner spinner2 =new Spinner(this);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, spinnerValue);
        spinner2.setAdapter(adapter);
        menuLayout.addView(spinner2);

        mainLayout.addView(menuLayout);

        cg = new CatchGame(this, 5, "Jamie", onScoreListener);
        cg.setBackground(getResources().getDrawable(R.drawable.bg_land_mdpi));
        mainLayout.addView(cg);
        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
        getSupportActionBar().hide();
        setContentView(mainLayout);
    }

    private void togglePausePlay() {
        if (cg.paused) {
            // play
            //  getSupportActionBar().hide();
            Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show();
        } else {
            // pause
            //    getSupportActionBar().show();
            Toast.makeText(MainActivity.this, "Pause", Toast.LENGTH_SHORT).show();
        }

        cg.paused = !cg.paused;
    }

    private OnScoreListener onScoreListener = new OnScoreListener() {
        @Override
        public void onScore(int score) {
            textView.setText("Score: " + score);
        }
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }

    // method called when top right menu is tapped
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        int difficulty = cg.NBRSTEPS;
        String name = cg.heroName;

        switch (item.getItemId()) {
            case R.id.item11:
                cg = new CatchGame(this, 3, name, onScoreListener);
                setContentView(cg);
                mainLayout.addView(cg);
                getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                setContentView(mainLayout);
                return true;
            case R.id.item12:
                cg = new CatchGame(this, 5, name, onScoreListener);
                setContentView(cg);
                mainLayout.addView(cg);
                getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                setContentView(mainLayout);
                return true;
            case R.id.item13:
                cg = new CatchGame(this, 7, name, onScoreListener);
                setContentView(cg);
                mainLayout.addView(cg);
                getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                setContentView(mainLayout);
                return true;
            case R.id.item14:
                cg = new CatchGame(this, 9, name, onScoreListener);
                setContentView(cg);
                mainLayout.addView(cg);
                getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                setContentView(mainLayout);
                return true;
            case R.id.item15:
                cg = new CatchGame(this, 11, name, onScoreListener);
                setContentView(cg);
                mainLayout.addView(cg);
                getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                setContentView(mainLayout);
                return true;
            case R.id.item21:
                cg = new CatchGame(this, difficulty, "Jamie", onScoreListener);
                setContentView(cg);
                mainLayout.addView(cg);
                getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                setContentView(mainLayout);
                return true;
            case R.id.item22:
                cg = new CatchGame(this, difficulty, "Spaceship", onScoreListener);
                setContentView(cg);
                //mainLayout.addView(cg);
                //getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                getSupportActionBar().hide();
                //setContentView(mainLayout);
                return true;
            default:
                cg.paused = true;
                return super.onOptionsItemSelected(item);
        }
    }

}

interface OnScoreListener {
    void onScore(int score);
}

class CatchGame extends View {
    int NBRSTEPS; // number of discrete positions in the x-dimension; must be uneven
    String heroName;
    int screenW;
    int screenH;
    int[] x; // x-coordinates for falling objects
    int[] y; // y-coordinates for falling objects
    int[] hero_positions; // x-coordinates for hero
    Random random = new Random();
    int ballW; // width of each falling object
    int ballH; // height of ditto
    float dY; //vertical speed
    Bitmap falling, hero, jamie2, jamieleft, jamieright;
    int heroXCoord;
    int heroYCoord;
    int xsteps;
    int score;
    int offset;
    boolean gameOver; // default value is false
    boolean toastDisplayed;
    boolean paused = false;

    OnScoreListener onScoreListener;

    // constructor, load images and get sizes
    public CatchGame(Context context, int difficulty, String name, OnScoreListener onScoreListener) {
        super(context);
        NBRSTEPS = difficulty;
        heroName = name;
        this.onScoreListener = onScoreListener;

        x = new int[NBRSTEPS];
        y = new int[NBRSTEPS];
        hero_positions = new int[NBRSTEPS];
        int resourceIdFalling = 0;
        int resourceIdHero = 0;
        if (heroName.equals("Jamie")) {
            resourceIdFalling = R.mipmap.falling_object2;
            resourceIdHero = R.drawable.left_side_hdpi;
            setBackground(getResources().getDrawable(R.mipmap.background));
        }
        if (heroName.equals("Spaceship")) {
            resourceIdFalling = R.mipmap.falling_object;
            resourceIdHero = R.mipmap.ufo;
            setBackground(getResources().getDrawable(R.mipmap.space));
        }
        falling = BitmapFactory.decodeResource(getResources(), resourceIdFalling); //load a falling image
        hero = BitmapFactory.decodeResource(getResources(), resourceIdHero); //load a hero image
        jamieleft = BitmapFactory.decodeResource(getResources(), R.drawable.left_side_hdpi); //load a hero image
        jamieright = BitmapFactory.decodeResource(getResources(), R.drawable.right_side_hdpi); //load a hero image

        ballW = falling.getWidth();
        ballH = falling.getHeight();
    }

    public CatchGame(Context context, int difficulty, String name, OnScoreListener onScoreListener, Drawable background) {
        this(context, difficulty, name, onScoreListener);
        this.setBackground(background);
    }

    // set coordinates, etc.
    void initialize() {
        if (!gameOver) { // run only once, when the game is first started
            int maxOffset = (NBRSTEPS - 1) / 2;
            for (int i = 0; i < x.length; i++) {
                int origin = (screenW / 2) + xsteps * (i - maxOffset);
                x[i] = origin - (ballW / 2);
                hero_positions[i] = origin - hero.getWidth();
            }
            int heroWidth = hero.getWidth();
            int heroHeight = hero.getHeight();

            hero = Bitmap.createScaledBitmap(hero, heroWidth * 2, heroHeight * 2, true);
            hero = Bitmap.createScaledBitmap(hero, heroWidth * 2, heroHeight * 2, true);
            jamieleft = Bitmap.createScaledBitmap(jamieleft, jamieleft.getWidth()* 2, jamieright.getWidth() * 2, true);
            jamieright = Bitmap.createScaledBitmap(jamieright, jamieright.getWidth()* 2, jamieright.getWidth() * 2, true);

            heroYCoord = screenH - 2 * heroHeight; // bottom of screen

        }
        for (int i = 0; i < y.length; i++) {
            y[i] = -random.nextInt(1000); // place items randomly in vertical direction
        }

        offset = (NBRSTEPS - 1) / 2; // place hero at centre of the screen
        heroXCoord = hero_positions[offset];

        // initialize or reset global attributes
        dY = 2.0f;
        score = 0;
        gameOver = false;
        toastDisplayed = false;
    }

    // method called when the screen opens
    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        screenW = w;
        screenH = h;
        xsteps = w / NBRSTEPS;
        initialize();
    }

    // method called when the "game over" toast has finished displaying
    void restart(Canvas canvas) {

        toastDisplayed = true;
        initialize();
        draw(canvas);
    }

    // update the canvas in order to display the game action
    @Override
    public void onDraw(Canvas canvas) {
        if (toastDisplayed) {
            restart(canvas);
            return;
        }
        super.onDraw(canvas);

        int heroHeight = hero.getHeight();
        int heroWidth = hero.getWidth();
        int heroCentre = heroXCoord + heroWidth / 2;

        Context context = this.getContext();

        // compute locations of falling objects
        for (int i = 0; i < y.length; i++) {
            if (!paused) {
                y[i] += (int) dY;
            }
            // if falling object hits bottom of screen
            if (y[i] > (screenH - ballH) && !gameOver) {
                dY = 0;
                gameOver = true;
                paused = true;
                int duration = Toast.LENGTH_SHORT;

                final Toast toast = Toast.makeText(context, "GAME OVER!\nScore: " + score, duration);
                toast.show();
                Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        toast.cancel();
                        toastDisplayed = true;
                    }
                }, 3000);
                //Vibrator v = (Vibrator) context.getSystemService(context.VIBRATOR_SERVICE);
                // Vibrate for 3000 milliseconds
                //v.vibrate(3000);

            }
            // if the hero catches a falling object
            if (x[i] < heroCentre && x[i] + ballW > heroCentre &&
                    y[i] > screenH - ballH - heroHeight) {

                y[i] = -random.nextInt(1000); // reset to new vertical position
                score += 1;
                onScoreListener.onScore(score);
            }

        }

        canvas.save(); //Save the position of the canvas.

        for (int i = 0; i < y.length; i++) {
            canvas.drawBitmap(falling, x[i], y[i], null); //Draw the falling on the canvas.
        }
        canvas.drawBitmap(hero, heroXCoord, heroYCoord, null); //Draw the hero on the canvas.

        canvas.restore();
        //Call the next frame.
        invalidate();
    }

    // event listener for when the user touches the screen
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (paused) {
            paused = false;
        }
        int action = MotionEventCompat.getActionMasked(event);
        if (action != MotionEvent.ACTION_DOWN || gameOver) { // non-touchdown event or gameover
            return true; // do nothing
        }
        int coordX = (int) event.getX();
        int xCentre = (screenW / 2) - (hero.getWidth() / 2);
        int maxOffset = hero_positions.length - 1; // can't move outside right edge of screen
        int minOffset = 0; // ditto left edge of screen

        if (coordX < xCentre && offset > minOffset) { // touch event left of the centre of screen
            offset--; // move hero to the left

            if(coordX < heroXCoord)// + heroWidth / 2)
                hero = Bitmap.createScaledBitmap(jamieleft, jamieleft.getWidth() , jamieleft.getHeight() , true);

        }
        if (coordX > xCentre && offset < maxOffset) { // touch event right of the centre of screen
            offset++; // move hero to the right

            if(coordX > heroXCoord)
                hero  = Bitmap.createScaledBitmap(jamieright, jamieright.getWidth() , jamieright.getHeight() , true);

        }
        heroXCoord = hero_positions[offset];

        return true;
    }
}

Das Depot ist Online verfügbar.

  • Überprüfen Sie dies: stackoverflow.com/a/35189629/4031815

    – CommonSenseCode

    28. November 2017 um 16:12 Uhr

  • Beantwortet das deine Frage? Kann keinen Handler innerhalb eines Threads erstellen, der Looper.prepare() nicht aufgerufen hat

    – Bink

    15. September um 21:16 Uhr

Benutzeravatar von nhoxbypass
nhoxbypass

Du KANNST NICHT zeigen a Toast in Nicht-UI-Thread. Sie müssen anrufen Toast.makeText() (und die meisten anderen Funktionen, die sich mit der Benutzeroberfläche befassen) aus innerhalb des Hauptthreads.


Du könntest benutzen Aktivität#runOnUiThread():

runOnUiThread(new Runnable() {
      public void run() {
         final Toast toast = Toast.makeText(context, "GAME OVER!\nScore: " + score, duration);
         toast.show();
      }
 });

Wenn Sie einen Instrumentierungstest im Hauptthread ausführen möchten, fügen Sie hinzu @UiThreadTest Anmerkung:

@Test
@UiThreadTest
public void useAppContext() {
    // ...
}

Ps: Es gibt auch viele andere Möglichkeiten mit Erklären (mit Handler, Looper, Observable..) in diesen Beiträgen: Android: Toast in einem Thread und Kann keinen Handler innerhalb eines Threads erstellen, der Looper.prepare() nicht aufgerufen hat

  • Ich verstehe nicht. Soll der Thread in meiner Hauptklasse oder in meiner Testerklasse sein?

    – Niklas Rosenkranz

    28. November 2017 um 16:23 Uhr

  • @DacSaunders ja! wann immer Sie anrufen möchten Toast.makeText().show()es muss Hauptthread sein

    – nhoxbypass

    28. November 2017 um 16:30 Uhr

  • Aber wie kann es von einem anderen Thread als dem Hauptthread aus getestet werden? Der Aufruf ist implizit, wenn man sich meinen Testcode ansieht. Ich verstehe immer noch nicht, wie ich das testen soll.

    – Niklas Rosenkranz

    28. November 2017 um 16:43 Uhr

  • Ihr CatchGame funktioniert also gut, wenn es läuft, aber schlägt beim Test fehl? @DacSaunders. Versuchen Sie hinzufügen @UiThreadTest zu deiner Prüfung.

    – nhoxbypass

    28. November 2017 um 16:44 Uhr


  • Ich habe Ihren Code in der Testklasse verwendet. Es sieht so aus, als hätte es funktioniert, weil meine Testabdeckung zugenommen hat: coveralls.io/github/montao/gamex

    – Niklas Rosenkranz

    28. November 2017 um 17:13 Uhr


Man kann keinen Toast von einem Nicht-UI-Thread zeigen. Sie können also vom Worker-Thread aus Folgendes tun, und es ist keine Aktivität oder Kontext erforderlich

JAVA

 new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
    Toast toast = Toast.makeText(mContext, "Toast", Toast.LENGTH_SHORT);
    toast.show();
}
});

KOTLIN

Handler(Looper.getMainLooper()).post {
    Toast.makeText(mContext, "Toast", Toast.LENGTH_SHORT).show()
}

  • Sie sagen “es erfordert keinen Kontext”, also was ist mContext??

    – Benutzer2342558

    2. Juni 2021 um 7:32 Uhr

  • @ user2342558 mContext ist der Anwendungskontext.

    – Achil Nair

    7. Juni 2021 um 13:22 Uhr

  • “Es erfordert keine Aktivität oder Kontext” ist also nicht wahr

    – Benutzer2342558

    7. Juni 2021 um 20:32 Uhr

  • @ user2342558 Der Handler ermöglicht es, den Code in einem UI/Hauptthread auszuführen, und dafür brauchen Sie keine Aktivität oder keinen Kontext. Der Code kann alles sein, was auf dem UI-Thread ausgeführt werden muss. In diesem Beispielcode zeigen wir einen Toast an und ein Toast erfordert einen Kontext. Daher verwenden wir a mContext.

    – Achil Nair

    8. Juni 2021 um 7:31 Uhr

  • Vielen Dank! ES KLAPPT

    – Teekam Suthar

    22. September 2021 um 16:53 Uhr

Benutzeravatar von Marlon
Marlon

Geben Sie in kotlin Ihren Code in Folgendes ein:

runOnUiThread { 
    Log.i(TAG, "runOnUiThread")
    Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show()
}

Führen Sie Ihre UI-Komponente mit runOnUiThread aus, das habe ich getan – KOTLIN

inner class yourThread : Thread() {
    override fun run() {
    // this is for fragment, you can use 'this' for Activity
        requireActivity().runOnUiThread { 
       // Your Toast
             Toast.makeText(requireContext(), "Toasted",Toast.LENGTH_LONG).show()
    }
}

1431930cookie-checkKann nicht auf einen Thread toasten, der Looper.prepare() nicht aufgerufen hat

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy