Android - основной метод в действии вызывает сбой программы на черный экран, но не дает ошибки

Раньше у меня была проблема, когда переход к моей основной активности (ColorMatch.java) из моей активности на странице-заставке приводил к тому, что программа на неопределенный срок зависала на черном экране. Я обнаружил, что это произошло из-за метода run(), вызываемого в моем onCreate в моей основной деятельности. Я пытался комментировать некоторые вещи, чтобы увидеть, работает ли это, но пока run() вызывается в моем onCreate, он автоматически переходит на черный экран, даже не выдавая ошибку для устранения неполадок. Что может быть причиной этой проблемы?

Мой ClassMatch.java:

package com.example.ali.colormatch;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.os.Bundle;
import android.app.Activity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.Random;

public class ColorMatch extends AppCompatActivity {

int answer; //1 = red, 2 = blue, 3 = green, 4 = yellow
TextView textView3, textView2;
int count = 0;
volatile boolean playing = true;
private long timeThisFrame;
private Button testButton;
long fps;
int score;
int correctAnswer;
boolean matchColor = true, matchText = false;
long startFrameTime;
boolean firstTime = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_color_match);
    textView3 = (TextView) findViewById(R.id.textView3);
    textView2 = (TextView) findViewById(R.id.textView2);

    run();
}

public void run() {
    while (playing) {

        startFrameTime = System.currentTimeMillis();

        if (firstTime){
            generateNewWord();
            firstTime = false;
        }

        if (answer == correctAnswer){
            score++;
            count++;
            generateNewWord();}
        //else {
         //   quit();
       // }


        if (count % 5 == 0){
                if (matchColor) {
                    textView3.setText(getString(R.string.gameSetting2)); // might need to add context with this - check http://stackoverflow.com/questions/10698945/reference-string-resource-from-code
                    matchColor = true;
                    matchText = false;
                }
                else if (matchText){
                    textView3.setText(getString(R.string.gameSetting1));
                    matchText = true;
                    matchColor = false;
                }
            }
        }

        //draw();
        timeThisFrame = System.currentTimeMillis() - startFrameTime;
        if (timeThisFrame > 0) {
            fps = 1000 / timeThisFrame;
        }

    }

public void generateNewWord(){

    //randomly select between red, green, blue, yellow
    Random rand = new Random();
    int randomInt1 = rand.nextInt(4) + 1; // assigns randomInt a value between 1 - 4
    int randomInt2 = rand.nextInt(4) + 1;

    if (randomInt1 ==1){
        textView3.setText(R.string.Red);
    }
    else if (randomInt1 ==2){
        textView3.setText(R.string.Green);
    }
    else if (randomInt1 == 3){
        textView3.setText(R.string.Blue);
    }
    else if (randomInt1 == 4){
        textView3.setText(R.string.Yellow);
    }


    //randomly select hex codes between rgby
    if (randomInt2 ==1){
        textView3.setTextColor(0xffcc0000);
    }
    else if (randomInt2 ==2){
        textView3.setTextColor(0xff669900);
    }
    else if (randomInt2 == 3){
        textView3.setTextColor(0xff000080);
    }
    else if (randomInt2 == 4){
        textView3.setTextColor(0xffffff00);
    }


   if (matchColor) {
        correctAnswer = randomInt2;
    }
    else if(matchText){
        correctAnswer = randomInt1;
    }

}
public void quit(){

}


public void sendBlue(View view){
    answer = 2;
}
public void sendRed(View view){
    answer = 1;
}
public void sendYellow(View view){
    answer = 4;
}
public void sendGreen(View view){
    answer = 3;
}


}

Используемый файл activity_color_match.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_color_match"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.ali.colormatch.ColorMatch">

<TextView

    android:text="@string/matchText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/textView3"
    android:textAppearance="@style/TextAppearance.AppCompat.Body2"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" />

<TextView
    android:text="@string/mainColor"
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:textColor="@android:color/background_dark"
    android:textSize="100dp"
    android:layout_marginBottom="104dp"
    android:textAppearance="@style/TextAppearance.AppCompat.Display3"
    android:layout_above="@+id/button3"
    android:layout_centerHorizontal="true" />

<Button
    android:layout_width="150dp"
    android:layout_height="60dp"
    android:id="@+id/button"
    android:background="#000080"
    android:onClick="sendBlue"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true" />

<Button
    android:layout_height="60dp"
    android:id="@+id/button2"
    android:background="@color/yellow"
    android:elevation="0dp"
    android:layout_width="150dp"
    android:onClick="sendYellow"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true" />

<Button
    android:layout_width="150dp"
    android:layout_height="60dp"
    android:id="@+id/button3"
    android:background="@android:color/holo_red_dark"
    android:onClick="sendRed"
    android:layout_above="@+id/button"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_marginBottom="19dp" />

<Button
    android:layout_width="150dp"
    android:layout_height="60dp"
    android:id="@+id/button4"
    android:background="@android:color/holo_green_dark"
    android:onClick="sendGreen"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_alignBottom="@+id/button3" />


</RelativeLayout>

Для завершения, хотя я думаю, что это, вероятно, не имеет значения, вот мой файл MainActivity.java. Кажется, он работает так, как я хотел бы:

package com.example.ali.colormatch;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;


public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.splash_page);
}

public void onSplashPageClick(View view){
    Intent intent = new Intent(MainActivity.this, ColorMatch.class);
    startActivity(intent);
}

public void onQuitClick(View view){
    finish();
}
}

Я использую Nexus 5 для запуска своего приложения и отладки. Спасибо.


person Johnny    schedule 15.10.2016    source источник
comment
onCreate() никогда не возвращает результат, потому что playing в методе run() true приводит к бесконечному циклу. Поскольку onCreate() не может вернуть остальные методы жизненного цикла Activity, их нельзя вызвать. В результате onResume() не возвращается, UI не отрисовывается, черный экран. Вы должны переместить логику цикла while в фоновый поток и опубликовать результаты в основном потоке.   -  person Onik    schedule 15.10.2016
comment
Ты был прав! Я отключил цикл while(playing), и это сработало. Это мой первый опыт кодирования в Android с опытом работы только с java, поэтому цикл для меня был основой, которую я не осознавал, что это может быть проблемой. В настоящее время я возился с этим циклом while (playing), прямо сейчас текст загружается, поэтому мне просто нужно найти способ его обновления.   -  person Johnny    schedule 15.10.2016


Ответы (2)


Привет. Если вы все еще ищете способ время от времени обновлять представление, вы можете использовать Таймер.

Посмотрите этот ответ здесь https://stackoverflow.com/a/18353754/2311117. это объясняет, как использовать таймер.

Дополнительные сведения о таймере и TimerTask.

person Shivansh    schedule 15.10.2016
comment
Я планирую добавить таймер, чтобы ограничить время, в течение которого пользователь должен нажимать кнопку, поэтому, похоже, я смогу использовать для этого таймер. Однако мне нужно, чтобы метод run() выполнялся каждый раз, когда пользователь нажимает кнопку; если ответ правильный, он сгенерирует новое слово и обновит таймер, если неправильный, программа остановится. - person Johnny; 15.10.2016
comment
вы можете использовать Timer.start и Timer.cancel для запуска/остановки таймера. насколько я понимаю, вам нужно что-то обновить, пока пользователь не предпримет какие-либо действия. - person Shivansh; 15.10.2016

Я думаю, вы можете создать поток в onCreate() и поместить в этот поток метод run().

попробуй это:

           new Thread(new Runnable() {
                @Override
                public void run() {
                    ColorMatch.this.run();
                }
            }).start();

** Редактировать **

Причина в том, что вы делаете слишком много в потоке пользовательского интерфейса (основном потоке) при onCreate, и если вы тратите слишком много времени в методе onCreate, это будет ANR (активность не отвечает), вам нужно поставить задачу, которая тратит время в фоновая нить. И это позволит избежать черного экрана при активности onCreate.

person Takuma Lee    schedule 15.10.2016
comment
AsyncTask — это стандартный способ сделать это в Android, по крайней мере, для относительно коротких операций. - person nasch; 15.10.2016
comment
Он не блокирует поток UI, он все еще работает. Также способ, который вы предлагаете, не работает, потому что в методе run() виджеты задеваются. - person Onik; 15.10.2016
comment
Да, AsyncTask - это стандартный способ :). - person Takuma Lee; 15.10.2016
comment
Хорошо, я ошибаюсь, да, он все еще работает. :(, я пытаюсь отредактировать это. - person Takuma Lee; 15.10.2016
comment
Черный экран и ANR - разные вещи. Приложение не вылетает, у него нет ANR, потому что поток пользовательского интерфейса не блокируется. - person Onik; 15.10.2016
comment
Но если приложение не перейдет в следующий жизненный цикл onResume, это будет ANR, не так ли? - person Takuma Lee; 15.10.2016
comment
Скорее всего, если onCreate() не сможет вернуться в течение достаточно долгого времени. - person Onik; 15.10.2016
comment
Да, это я имею в виду. Я думаю, что мое описание не верно, вероятно. - person Takuma Lee; 15.10.2016
comment
@Onik - вы упомянули о перемещении цикла while в фоновый поток и публикации результатов обратно в основной. Я попытался использовать фрагмент кода Такумы здесь, но это не сработало. Почему это не работает, чтобы переместить его в другой поток? Кроме того, возможно, очень глупый вопрос, но когда вы упомянули о перемещении его в фоновый поток, вы имели в виду фактический новый поток? - person Johnny; 15.10.2016
comment
@Ali хм... извините, я потерял метод start(), чтобы начать тему.. Я редактирую ответ, можете ли вы попробовать еще раз? - person Takuma Lee; 15.10.2016
comment
@TakumaLee Итак, ваш поток работает, но, как я боялся, не с оригинальным циклом while, который я использовал; при этом он дает эту ошибку: android.view.ViewRootImpl$CalledFromWrongThreadException: только исходный поток, создавший иерархию представлений, может касаться его представлений. - person Johnny; 15.10.2016
comment
Да, я думаю, что ваша ошибка выглядит следующим образом: textView3.setText(getString(R.string.gameSetting2)); Причина в том, что пользовательский интерфейс изменил потребность в потоке пользовательского интерфейса, но теперь нужно создать новый поток, поэтому его нужно поместить в поток пользовательского интерфейса, а не в задний поток, поэтому вам нужно его отделить. - person Takuma Lee; 15.10.2016