如何在没有Activity上下文的情况下在android中显示对话框?

时间:2012-03-17 15:11:57

标签: android alertdialog

这看起来应该很简单,但我在任何地方都找不到答案。我有一个Android应用程序,在后台执行网络任务。如果出现错误,我想显示错误对话框。当任务返回时,我不知道哪个Activity在前台。基于this post,看起来我们无法使用应用程序上下文来显示对话框(事实上,如果我尝试的话,我确实会遇到崩溃)。

那么我怎样才能获得当前活动的背景?同样,网络任务的接收器在Application上下文中运行,而不是在特定的Activity中运行。还有其他想法吗?

编辑:我应该澄清一下。如果我不是前台应用程序,我不想显示错误对话框。我只对我们的应用程序暂时处于前台的情况感兴趣。

9 个答案:

答案 0 :(得分:26)

  

如果出现错误,我想显示错误对话框。

如果您知道用户正在使用您的应用程序,请仅执行此操作。如果你在其他东西中打断它们(玩游戏,看电影,看书),用户会非常恼火。

  

那么我怎样才能获得当前活动的背景?

你没有。最多,你让当前的活动知道它需要做些什么。

  

还有其他想法吗?

一种可能性是使用有序广播,所以如果你有前台活动,它就会获得控制权,否则你会引发Notification让用户知道问题而不会弹出一个对话框。接收有序广播的活动可以显示AlertDialog或以其他方式让用户知道问题。我写了关于如何执行此操作的详细信息in a blog post(以及a book chapter),并here is a sample application演示了该技术。

或者,让服务电话startActivity()启动a dialog-themed activity

答案 1 :(得分:6)

我创建了一个实现CommonsWare想法的帮助器类。希望显示警报的活动只需要调用Alerts.register()和Alerts.unregister()。然后任何人都可以调用Alerts.displayError()。

欢迎评论。

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

}

答案 2 :(得分:4)

我正在使用自定义创建的对话框。即使当前的Activity失去焦点,它也会被Service或Handler调用。

我的自定义对话框的活动

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

您可以使用以下命令调用此对话框:

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

在清单中

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

</activity>

样式:

<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>

这是此example的完整代码。

答案 3 :(得分:1)

这是一个允许AlertDialog显示在当前活动活动之上的实现(这是一个消息对话框的示例,但也可以用于警报)。

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

即使实例化它的上下文不再有效,也会显示该对话框,就像Toast一样。

使用new AlertsDialogue(MyActivity.this, "title", "message");

进行通话

AndroidManifest文件中不需要其他权限。

答案 4 :(得分:1)

虽然这个问题已经很老了,但如果您希望它与任何活动的活动一起工作,那么有一个很好的解决方案。因此,您无需像其他答案中建议的那样注册单个活动。

我想强调的是,这通常是您应该尽量避免的事情 - 在不知道视图上下文的情况下创建对话框,但在特殊情况下这可能很有用。

使用此解决方案,您始终可以了解应用程序级别的活动活动,并且可以使用活动上下文来打开对话框或其他活动,例如您只能访问应用程序的网络代码:

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
      }
   }
// ...
}

从任何地方:

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

请查看其他 SO 帖子如何为应用程序实现 getter:例如Kotlin Singleton Application Class

答案 5 :(得分:0)

后台正在进行哪种类型的网络任务。我建议可能重新考虑设计。也许通知会更好?或者也许是&#34;结果摘要&#34;屏幕。作为用户,如果我没有主动等待任务完成,我宁愿发出非突然的错误信号。

答案 6 :(得分:0)

我也面临这个问题。我找到了一个简单有效的解决方案。 Usuallay,我们有一个用于处理一些常见逻辑的基本活动。所以有这个:

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

当前字段是当前的Context Actitvity。我相信没有内存泄漏问题。这对我来说很有效!希望有用

答案 7 :(得分:0)

您需要重置附加对话框的Window类型,如下所示: dialog.getWindow()的setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

不要忘记在清单中声明“android.permission.SYSTEM_ALERT_WINDOW”权限。

答案 8 :(得分:-4)

嗯,我认为最适合你的是AsyncTask。这里有一个例子和文档: http://developer.android.com/reference/android/os/AsyncTask.html