Вызов AlertDialog из сервиса (другой поток) в Android

185
09 июня 2018, 13:00

Есть активити MainActivity и сервис MyFirebaseMessagingService, который получает пуш-сообщение. Мне это сообщение нужно вывести в виде диалога. Проблема в том, что Toast выводится, а вот AlertDialog нет - приложение крашиться. Вот мой код в сервисе:

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
    private static final String TAG = "Adrenalin:MyFirebaseMsgService";
    /**
     * Called when message is received.
     *
     * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
     */
    // [START receive_message]
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {

        Log.d(TAG, "From: " + remoteMessage.getFrom());
        // Check if message contains a data payload.
        if (remoteMessage.getData().size() > 0) {
            Log.d(TAG, "Message data payload: " + remoteMessage.getData());
            if (/* Check if data needs to be processed by long running job */ true) {
                // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
                //scheduleJob();
            } else {
                // Handle message within 10 seconds
                handleNow();
            }
        }
        // Check if message contains a notification payload.
        if (remoteMessage.getNotification() != null) {
            final String remMesBody = remoteMessage.getNotification().getBody();
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    //Toast.makeText(getApplicationContext(),remMesBody,Toast.LENGTH_LONG).show();
                    AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
                    builder.setTitle("Важное сообщение!")
                            .setMessage(remMesBody)
                            .setIcon(R.drawable.ic_adrenalin)
                            .setCancelable(false)
                            .setNegativeButton("ОК",
                                    new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int id) {
                                            dialog.cancel();
                                        }
                                    });
                    AlertDialog alert = builder.create();
                    alert.show();
                }
            });
        }
        // Also if you intend on generating your own notifications as a result of a received FCM
        // message, here is where that should be initiated. See sendNotification method below.
    }

    /**
     * Handle time allotted to BroadcastReceivers.
     */
    private void handleNow() {
        Log.d(TAG, "Short lived task is done.");
    }
}

Там виден закоментированный Toast, который успешно работает.

Лог краша:

05-25 09:00:32.122 2700-2700/ru.arm_prokat.bicycle.adrenalin E/AndroidRuntime: FATAL EXCEPTION: main
    Process: ru.arm_prokat.bicycle.adrenalin, PID: 2700
    android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:800)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:351)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
        at android.app.Dialog.show(Dialog.java:322)
        at ru.arm_prokat.bicycle.adrenalin.MyFirebaseMessagingService$1.run(MyFirebaseMessagingService.java:65)
        at android.os.Handler.handleCallback(Handler.java:836)
        at android.os.Handler.dispatchMessage(Handler.java:103)
        at android.os.Looper.loop(Looper.java:203)
        at android.app.ActivityThread.main(ActivityThread.java:6251)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
Answer 1

Вы используете режим Data и в таком случае ваш сервис может получить сообщение в том числе когда нет приложения в Foreground, соответственно нет лупера и т.д.

Правильно это делать так - делать нотификацию для открытия активити с определенными параметры, которые уже инициируют dialog - либо более кастомизированную карточку нотификации.

Если все таки вам надо диалог, то думаю что надо делать: - на уровне приложения подписаться на события жизненного цикла - через broadcast listener кинуть нотификацию - если мы в режиме foreground таки показать диалог. Но вообще так делать не принято.

Answer 2

В общем просидел полдня над этой задачей, но решил её! В классе mainActivity добавил следующее:

 @Override
    protected void onResume(){
        super.onResume();
        IntentFilter intentFilter = new IntentFilter(MyFirebaseMessagingService.ACTION_FILTER);
        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        registerReceiver(broadcastReceiver, intentFilter);
    }
    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //do something
            String msg = intent.getStringExtra("text_for_alert");
            Log.d(TAG, "Message from BroadcastReceiver: " + msg);
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setTitle("Важное сообщение!")
                    .setMessage(msg)
                    .setIcon(R.drawable.ic_adrenalin)
                    .setCancelable(false)
                    .setNegativeButton("ОК",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();
                                }
                            });
            AlertDialog alert = builder.create();
            alert.show();
            abortBroadcast(); //отменяет дальнейшую обработку бродкаста
        }
    };

А в сервисе

public static final String ACTION_FILTER  = "ru.adrenalin.DIALOG";
//
//
//
//
if (remoteMessage.getNotification() != null) {
            final String remMesBody = remoteMessage.getNotification().getBody();
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
            Intent intent = new Intent(ACTION_FILTER);
            intent.putExtra("text_for_alert",remMesBody);
            sendOrderedBroadcast(intent, null);
        }

Теперь при получении сообщения, когда активно приложение - открывается алерт. Само приложение у меня основано на webview, там просто подгружаются данные из вебсайта и разная информация. Следующий этап - хочу сделать так, чтобы когда получается пуш-уведомление в бэкграунде, то при тапе по пушу - открывалась в приложении какая-то информация, а то сейчас просто происходит открытие приложения

Возможно где-то криво, но всего несколько дней изучаю андроид! Надеюсь кому-нибудь поможет данный код!

READ ALSO
Как получить первичный ключ?

Как получить первичный ключ?

В бд первичный ключ находится в столбце id_dish(проверено 10 тыщ раз))

220
Как сделать выделение строки?

Как сделать выделение строки?

Есть таблица JTable, на нее вешаю такой рендер:

211
Внутренние и вложенные классы JAVA

Внутренние и вложенные классы JAVA

Для обращения из внутреннего класса Inner к экземпляру обрамляющего класса Outer используется следующая запись:

142
Как добавить javadoc в jar файл

Как добавить javadoc в jar файл

Как добавить javadoc в jar файл при сборке проекта командой gradle build?

163