Java.lang.RuntimeException: невозможно создать обработчик внутри потока, который не вызвал Looper.prepare();
У меня есть приложение для Android, в котором работает поток. Я хочу, чтобы сообщение Toast отображалось с сообщением.
Когда я это сделаю, я получаю следующее исключение:
Трассировка Logcat:
FATAL EXCEPTION: Timer-0
java.lang.RuntimeException: Can't create handler inside thread that has not
called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at android.widget.Toast$TN.<init>(Toast.java:322)
at android.widget.Toast.<init>(Toast.java:91)
at android.widget.Toast.makeText(Toast.java:238)
Есть ли работа для того, чтобы нажимать Toast-сообщения из потоков в пользовательский интерфейс?
Ответы
Ответ 1
У меня есть это исключение, потому что я пытался создать всплывающее окно Toast из фонового потока.
Toast требует действия для нажатия на пользовательский интерфейс, а потоки не имеют этого.
Поэтому одним из способов является предоставление потоку ссылки на родительскую активность и тосты.
Поместите этот код в поток, в который вы хотите отправить сообщение Toast:
parent.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(parent.getBaseContext(), "Hello", Toast.LENGTH_LONG).show();
}
});
Держите ссылку на родительскую активность в фоновом потоке, который создал этот поток. Используйте родительскую переменную в вашем классе потоков:
private static YourActivity parent;
Когда вы создаете поток, передайте родительскую активность в качестве параметра через конструктор, например:
public YourBackgroundThread(YourActivity parent) {
this.parent = parent;
}
Теперь фоновый поток может вызывать сообщения Toast на экран.
Ответ 2
Android в основном работает с двумя типами потоков, а именно с потоком пользовательского интерфейса и фоновым потоком. Согласно документации по Android -
Не обращайтесь к инструментарию Android UI из-за пределов пользовательского интерфейса, чтобы исправить эту проблему, Android предлагает несколько способов доступа к потоку пользовательского интерфейса из других потоков. Вот список методов, которые могут помочь:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Теперь для решения этой проблемы существуют различные методы. Я объясню это по образцу кода
runOnUiThread
new Thread()
{
public void run()
{
myactivity.this.runOnUiThread(new runnable()
{
public void run()
{
//Do your UI operations like dialog opening or Toast here
}
});
}
}.start();
LOOPER
Класс, используемый для запуска цикла сообщений для потока. Потоки по умолчанию не имеют связанного с ними цикла сообщений; для его создания вызовите prepare() в потоке, который должен запустить цикл, а затем loop(), чтобы обработать сообщения до тех пор, пока цикл не будет остановлен.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
AsyncTask
AsyncTask позволяет выполнять асинхронную работу с вашим пользовательским интерфейсом. Он выполняет операции блокировки в рабочем потоке и затем публикует результаты в потоке пользовательского интерфейса, не требуя, чтобы вы сами обрабатывали потоки и/или обработчики.
public void onClick(View v) {
new CustomTask().execute((Void[])null);
}
private class CustomTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... param) {
//Do some work
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
}
Обработчик
Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable, связанные с потоком MessageQueue.
Message msg = new Message();
new Thread()
{
public void run()
{
msg.arg1=1;
handler.sendMessage(msg);
}
}.start();
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(msg.arg1==1)
{
//Print Toast or open dialog
}
return false;
}
});
Ответ 3
Из http://developer.android.com/guide/components/processes-and-threads.html:
Кроме того, набор инструментов Android UI не является потокобезопасным. Итак, вы не должен управлять вашим пользовательским интерфейсом из рабочего потока - вы должны делать все манипуляции с пользовательским интерфейсом из потока пользовательского интерфейса. Таким образом, это просто два правила для модели с одним потоком в Android:
- Не блокируйте поток пользовательского интерфейса
- Не обращайтесь к инструментарию Android UI из-за пределов пользовательского интерфейса.
Вы должны обнаружить праздность в рабочем потоке и показать тост в основном потоке.
Пожалуйста, разместите код, если вы хотите получить более подробный ответ.
После публикации кода:
В strings.xml
<string name="idleness_toast">"You are getting late do it fast"</string>
В YourWorkerThread.java
Toast.makeText(getApplicationContext(), getString(R.string.idleness_toast),
Toast.LENGTH_LONG).show();
Не используйте AlertDialog, сделайте выбор. AlertDialog и Toast - это две разные вещи.
Ответ 4
Вот что я делал:
public void displayError(final String errorText) {
Runnable doDisplayError = new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), errorText, Toast.LENGTH_LONG).show();
}
};
messageHandler.post(doDisplayError);
}
Это должно позволить вызывать метод из любого потока.
Где messageHandler объявляется в активности как..
Handler messageHandler = new Handler();
Ответ 5
Вы можете просто использовать BeginInvokeOnMainThread(). Он вызывает действие в потоке основного устройства (UI).
Device.BeginInvokeOnMainThread(() => { displayToast("text to display"); });
Это просто и отлично работает для меня!
EDIT: Работает, если вы используете С# Xamarin