41 Stimmen

Wie kann ich in Android einen Dialog ohne Aktivitätskontext anzeigen?

Das sollte eigentlich ganz einfach sein, aber ich finde nirgendwo eine Antwort. Ich habe eine Android-Anwendung, die Netzwerkaufgaben im Hintergrund ausführt. Wenn ein Fehler zurückkommt, möchte ich einen Fehlerdialog anzeigen. Wenn die Aufgabe zurückkehrt, weiß ich nicht, welche Aktivität im Vordergrund ist. Basierend auf diese Stelle Es sieht so aus, als ob wir den Anwendungskontext nicht verwenden können, um ein Dialogfeld anzuzeigen (und in der Tat bekomme ich den Absturz, wenn ich es versuche).

Wie kann ich also den Kontext der aktuellen Aktivität ermitteln? Auch hier gilt, dass der Empfänger für die Netzwerkaufgabe im Anwendungskontext läuft, nicht in einer bestimmten Aktivität. Irgendwelche anderen Ideen?

Edit: Ich sollte das klarstellen. Ich möchte keinen Fehlerdialog anzeigen, wenn ich nicht die Vordergrundanwendung bin. Ich interessiere mich nur für den Fall, wo unsere App im Vordergrund ist für jetzt.

27voto

CommonsWare Punkte 950864

Wenn ein Fehler zurückkommt, möchte ich einen Fehlerdialog anzeigen.

Bitte tun Sie dies nur, wenn Sie wissen, dass der Benutzer Ihre Anwendung aktiv nutzt. Der Benutzer wird sehr verärgert sein, wenn Sie ihn mitten in etwas anderem unterbrechen (ein Spiel spielen, einen Film ansehen, ein Buch lesen).

Wie kann ich also den Kontext der aktuellen Aktivität ermitteln?

Sie haben keine. Höchstens teilen Sie der aktuellen Aktivität mit, dass sie etwas tun muss.

Haben Sie eine andere Idee?

Eine Möglichkeit ist die Verwendung einer geordneten Übertragung, also wenn Sie eine Aktivität im Vordergrund haben, erhält diese die Kontrolle, andernfalls lösen Sie eine Notification um den Benutzer über das Problem zu informieren, ohne dass ein Dialog erscheint. Die Aktivität, die den bestellten Broadcast empfängt, kann eine AlertDialog oder den Benutzer auf andere Weise über das Problem informieren. Ich habe über die Details geschrieben, wie man das macht in einem Blogeintrag (und ein Buchkapitel ), und Hier ist ein Anwendungsbeispiel die Technik zu demonstrieren.

Oder lassen Sie den Dienst anrufen startActivity() zum Anfahren eine Aktivität zum Thema Dialog .

6voto

ManicBlowfish Punkte 2189

Ich habe eine Hilfsklasse erstellt, die die Idee von CommonsWare implementiert. Aktivitäten, die Warnungen anzeigen wollen, müssen nur Alerts.register() und Alerts.unregister() aufrufen. Dann kann jeder Alerts.displayError() aufrufen.

Kommentare sind willkommen.

public class Alerts {

    private static class AlertReceiver extends BroadcastReceiver {

        private static HashMap<Activity, AlertReceiver> registrations;
        private Context activityContext;

        static {
            registrations = new HashMap<Activity, AlertReceiver>();
        }

        static void register(Activity activity) {
            AlertReceiver receiver = new AlertReceiver(activity);
            activity.registerReceiver(receiver, new IntentFilter(MyApplication.INTENT_DISPLAYERROR));
            registrations.put(activity, receiver);
        }

        static void unregister(Activity activity) {
            AlertReceiver receiver = registrations.get(activity);
            if(receiver != null) {
                activity.unregisterReceiver(receiver);
                registrations.remove(activity);
            }
        }

        private AlertReceiver(Activity activity) {
            activityContext = activity;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            abortBroadcast();
            String msg = intent.getStringExtra(Intent.EXTRA_TEXT);
            displayErrorInternal(activityContext, msg);
        }
    }

    public static void register(Activity activity) {
        AlertReceiver.register(activity);
    }

    public static void unregister(Activity activity) {
        AlertReceiver.unregister(activity);
    }

    public static void displayError(Context context, String msg) {
        Intent intent = new Intent(MyApplication.INTENT_DISPLAYERROR);
        intent.putExtra(Intent.EXTRA_TEXT, msg);
        context.sendOrderedBroadcast(intent, null);
    }

    private static void displayErrorInternal(Context context, String msg) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("Error")
               .setMessage(msg)
               .setCancelable(false)
               .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       dialog.cancel();
                   }
               });
        final AlertDialog alert = builder.create();

        alert.show();
    }

}

6voto

Michael Troger Punkte 3256

Diese Frage ist zwar schon ziemlich alt, aber inzwischen gibt es eine gute Lösung, wenn Sie wollen, dass es mit jedem aktiven Activity . Sie brauchen sich also nicht einzeln zu registrieren. Activities wie es in anderen Antworten vorgeschlagen wird.

Ich möchte betonen, dass dies in der Regel etwas ist, das Sie vermeiden sollten - das Erstellen von Dialogen ohne Kenntnis des Ansichtskontextes, aber es kann besondere Umstände geben, unter denen dies nützlich sein kann.

Bei dieser Lösung mit ActivityLifecycleCallbacks Sie sind sich immer der aktiven Activity auf Application Ebene. Diese aktive Ebene können Sie dann verwenden Activity zum Öffnen von Dialogen oder anderen Activities von z. B. Netzwerkcode, bei dem Sie nur Zugriff auf die Application Klasse:

class YourApplication : Application() {

    private val activeActivityCallbacks = ActiveActivityLifecycleCallbacks()

    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(activeActivityCallbacks)
    }

    override fun onTerminate() {
       unregisterActivityLifecycleCallbacks(activeActivityCallbacks)
       super.onTerminate()
    }

    fun getActiveActivity(): Activity? = activeActivityCallbacks.getActiveActivity()
// ...
}

class ActiveActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks {

   private var activeActivity: Activity? = null

   fun getActiveActivity(): Activity? = activeActivity

   override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
      activeActivity = activity
   }

   override fun onActivityDestroyed(activity: Activity) {
      if (activity === activeActivity) {
         activeActivity = null
      }
   }
// ...
}

Von überall her:

YourApplication.get().getActiveActivity()?.let { activity ->
    activity.runOnUiThread {
       AlertDialog.Builder(activity).setMessage("test").show()
    }
}

Bitte schauen Sie in anderen SO-Beiträgen nach, wie man einen Getter für die Application : z.B. Kotlin Singleton-Anwendungsklasse

4voto

V.March Punkte 1730

Ich verwende einen selbst erstellten Dialog. Er wird vom Dienst oder Handler auch dann aufgerufen, wenn die aktuelle Aktivität den Fokus verliert.

Aktivität meines benutzerdefinierten Dialogs :

public class AlertDialogue extends AppCompatActivity {

    Button btnOk;
    TextView textDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE); //comment this line if you need to show Title.
        setContentView(R.layout.activity_alert_dialogue);

        textDialog = (TextView)findViewById(R.id.text_dialog) ;
        textDialog.setText("Hello, I'm the dialog text!");

        btnOk = (Button) findViewById(R.id.button_dialog);
        btnOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

Sie können diesen Dialog mit aufrufen:

Intent intent = new Intent(this, AlertDialogue.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Im Manifest :

<activity android:name=".AlertDialogue"
    android:theme="@style/AlertDialogMy">

</activity>

Stil:

<resources>

    <style name="AlertDialogMy" parent="Theme.AppCompat.Light.Dialog">
        <item name="android:windowNoTitle">true</item> //delete this line if you need to show Title.
    </style>

</resources>

Hier ist der vollständige Code dafür Beispiel .

2voto

Allen Chan Punkte 3983

Warum verwenden Sie nicht den Ereignisbus ( https://github.com/greenrobot/EventBus ) ?

  1. Definieren Sie Ereignisse:

    public class ShowErrorMessageEvent {
    
        public String errorMessage;
    
        public ShowErrorMessageEvent(String errorMessage) {
            this.errorMessage = errorMessage;
        }
        public String getErrorMessage() {
            return this.errorMessage;
        }
    }
  2. Bereiten Sie Abonnenten für alle benötigten Aktivitäten vor:

    @Override
    public void onStart() { 
        super.onStart(); 
        EventBus.getDefault().register(this);
    }
    
    @Override
    public void onStop() {
        EventBus.getDefault().unregister(this);
        super.onStop();
    }
  3. Zeigen Sie in allen Aktivitäten den Dialog an, wenn das Ereignis empfangen wurde

     public void onEvent(ShowErrorMessageEvent event) {
         /* Show dialog with event.getErrorMessage() from background thread */
     };
  4. Posten Sie in Ihrem Hintergrund-Thread das Fehlerereignis:

    EventBus.getDefault().post(new ShowErrorMessageEvent("This is an error message!"));

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X