2012-03-17 9 views
28

Parece que debería ser simple, pero no encuentro una respuesta en ninguna parte. Tengo una aplicación de Android que realiza tareas de red en segundo plano. Si aparece un error, quiero mostrar un cuadro de diálogo de error. Cuando la tarea vuelve, no sé qué actividad está en primer plano. Basado en this post, parece que no podemos usar el contexto de la aplicación para mostrar un cuadro de diálogo (y, de hecho, obtengo el bloqueo si lo intento).¿Cómo visualizo un diálogo en Android sin un contexto de Actividad?

Entonces, ¿cómo puedo obtener el contexto de la actividad actual? Nuevamente, el receptor para la tarea de red se está ejecutando en el contexto de la Aplicación, no en una Actividad particular. ¿Alguna otra idea?

Editar: Debería aclarar. No quiero mostrar un cuadro de diálogo de error si no soy la aplicación de primer plano. Solo estoy interesado en el caso donde nuestra aplicación está en primer plano por el momento.

+0

Puede usar un [Toast] (http://developer.android.com/reference/android/widget/Toast.html). Afaik no hay forma de mostrar un cuadro de diálogo sin ser la aplicación de primer plano activa. – zapl

Respuesta

20

Si un error vuelve, quiero mostrar un diálogo de error.

Haga esto solo si sabe que el usuario está utilizando activamente su aplicación. El usuario se molestará mucho si los interrumpe en medio de otra cosa (jugar un juego, ver una película, leer un libro).

Entonces, ¿cómo puedo obtener el contexto de la actividad actual?

Usted no lo hace. A lo sumo, le permite a la actividad actual saber que necesita hacer algo.

¿Alguna otra idea?

Una posibilidad es utilizar una difusión ordenada, por lo si usted tiene una actividad de primer plano, que toma el control, de lo contrario se eleva un Notification para que el usuario sepa acerca del problema sin que explote un cuadro de diálogo. La actividad que recibe la transmisión ordenada puede mostrar un AlertDialog o de lo contrario informar al usuario sobre el problema. Escribí sobre los detalles de cómo hacer esto in a blog post (y a book chapter, para ese asunto), y here is a sample application demostrando la técnica.

O haga llamar al servicio técnico startActivity() para iniciar a dialog-themed activity.

+0

Si no tiene contexto, ¿cómo puede crear y activar una notificación? Afaik una notificación requiere que se cree un contexto? Estoy ejecutando un hilo de fondo que no está vinculado a ninguna actividad; por lo tanto, no tengo forma de obtener un contexto de actividad para comenzar la notificación en primer lugar. – AgentKnopf

+0

@Zainodis: "Tengo un hilo de fondo en ejecución que no está vinculado a ninguna actividad" - eso es un error. Debería tenerlo administrado por algún componente, como un servicio. Entonces, el 'Servicio' es su' Contexto' para seguir lo que escribí en mi respuesta. – CommonsWare

+0

Gracias por la pista: antes teníamos un servicio (el hilo de fondo maneja las conexiones del servidor), luego revertimos a un hilo de fondo, porque de vez en cuando nuestro servicio sería asesinado (por el sistema operativo que asumimos, ya que no había excepción o similar para causar una falla abrupta) sin razón aparente (que es un no-go en nuestro caso). – AgentKnopf

0

Qué tipo de tareas de red se están realizando en segundo plano. Sugeriría posiblemente repensar el diseño. Tal vez una notificación sería mejor? O tal vez una pantalla de "resumen de resultados". Como usuario, preferiría una señal de error no molesta si no estoy esperando activamente que la tarea se complete.

6

He creado una clase de ayuda que implementa la idea de CommonsWare. Las actividades que desean mostrar alertas solo necesitan llamar a Alerts.register() y Alerts.unregister(). Entonces, cualquiera puede llamar a Alerts.displayError().

Comentarios bienvenidos.

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(); 
    } 

} 
+0

Hola, Cómo uso esta clase, tengo un problema con esto. ¿Qué es la aplicación? – MRT

+0

MyApplication. – Jon

1

También me enfrento al problema. Descubro una solución simple y efectiva.Usuallay, tenemos una actividad base utilizada para manejar alguna lógica común. Así que tenga esto:

public class BaseActionBarActivity extends ActionBarActivity{ //this BaseActionBarActivity is the Base Act 

    private static BaseActionBarActivity current; 

    @Override 
    protected void onStart() { 
    super.onStart(); 
    current=this; 
    } 


    public static BaseActionBarActivity getCurrentContext() { 
    return current; 
    } 
} 

campo actual es el contexto actual Actitvity. Y creo que no hay una pregunta de pérdida de memoria. ¡Me funciona bien! Esperanza útil

0

¿Por qué no utiliza el bus de eventos (https://github.com/greenrobot/EventBus)?

  1. definir eventos:

    public class ShowErrorMessageEvent { 
    
        public String errorMessage; 
    
        public ShowErrorMessageEvent(String errorMessage) { 
         this.errorMessage = errorMessage; 
        } 
        public String getErrorMessage() { 
         return this.errorMessage; 
        } 
    } 
    
  2. Preparar suscriptores para todas las actividades que se necesitan para:

    @Override 
    public void onStart() { 
        super.onStart(); 
        EventBus.getDefault().register(this); 
    } 
    
    @Override 
    public void onStop() { 
        EventBus.getDefault().unregister(this); 
        super.onStop(); 
    } 
    
  3. En todas las actividades, mostrar el cuadro de diálogo si el evento recibido

    public void onEvent(ShowErrorMessageEvent event) { 
        /* Show dialog with event.getErrorMessage() from background thread */ 
    }; 
    
  4. En el subproceso de fondo, publicar el evento de error:

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

tendrá que restablecer el tipo de ventana que se adjunta de diálogo, de la siguiente manera:. dialog.getWindow() setType (WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

no olvide declarar el permiso "android.permission.SYSTEM_ALERT_WINDOW" en su manifiesto.

1

Aquí hay una implementación que permitirá mostrar un AlertDialog además de la actividad activa actual (este es un ejemplo de diálogo de mensaje, pero también se puede usar para alertas).

public class AlertsDialogue 
{ 
    private AlertDialog.Builder alertDialogBuilder; 
    private AlertDialog alert; 

    public AlertsDialogue(Context context, String title, String message) 
    { 
     alertDialogBuilder = new AlertDialog.Builder(context); 
     alertDialogBuilder.setTitle(title); 
     alertDialogBuilder.setIcon(R.drawable.ic_launcher); 
     alertDialogBuilder.setMessage(message) 
      .setCancelable(false) 
      .setPositiveButton(context.getString(R.string.text_ok), new DialogInterface.OnClickListener() { 
       @Override 
       public void onClick(DialogInterface dialog, int which) 
       { 
        alert.dismiss(); 
       } 
      }); 

     alert = alertDialogBuilder.create(); 
     Window window = alert.getWindow(); 
     if (window != null) 
     { 
      // the important stuff.. 
      window.setType(WindowManager.LayoutParams.TYPE_TOAST); 
      alert.show(); 
     } 
     else 
      Toast.makeText(context, message, Toast.LENGTH_LONG).show(); 
    } 
} 

El diálogo se mostrará incluso si el contexto en el que se crea una instancia con ya no está activa, al igual que Toast.

llamada con new AlertsDialogue(MyActivity.this, "title", "message");

No se requieren permisos adicionales en el archivo AndroidManifest.

Cuestiones relacionadas