可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This seems like it should be simple, but I'm not finding an answer anywhere. I have an Android application that performs network tasks in the background. If an error comes back, I want to display an error dialog. When the task returns, I don't know which Activity is in the foreground. Based on this post, it looks like we can't use the application context to display a dialog (and indeed I do get the crash if I try).
So how can I get the context of the current activity? Again, the receiver for the network task is running in the Application context, not in a particular Activity. Any other ideas?
Edit: I should clarify. I don't want to display an error dialog if I'm not the foreground application. I'm only interested in the case where our app is in the foreground for now.
回答1:
If an error comes back, I want to display an error dialog.
Please only do this if you know that the user is actively using your application. The user will be very very annoyed if you interrupt them in the middle of something else (playing a game, watching a movie, reading a book).
So how can I get the context of the current activity?
You don't. At most, you let the current activity know that it needs to do something.
Any other ideas?
One possibility is to use an ordered broadcast, so if you have a foreground activity, it gets control, otherwise you raise a Notification
to let the user know about the problem without popping a dialog. The activity that receives the ordered broadcast can display an AlertDialog
or otherwise let the user know about the problem. I wrote about the details of how to do this in a blog post (and a book chapter, for that matter), and here is a sample application demonstrating the technique.
Or, have the service call startActivity()
to start up a dialog-themed activity.
回答2:
I've created a helper class that implements CommonsWare's idea. Activities that wish to display alerts just need to call Alerts.register() and Alerts.unregister(). Then anyone can call Alerts.displayError().
Comments welcome.
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();
}
}
回答3:
I'm using custom created Dialog. It will be called by Service or Handler even in the case if the current Activity will lose focus.
Activity of my custom Dialog:
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();
}
});
}
}
You can call this dialog using:
Intent intent = new Intent(this, AlertDialogue.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
In the Manifest:
<activity android:name=".AlertDialogue"
android:theme="@style/AlertDialogMy">
</activity>
style:
<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>
Here is the full code of this example.
回答4:
I also face the problem. I find out a simple and effective solution. Usuallay, we have a base activity used to handle some common logic. So have this:
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;
}
}
current field is the current Context Actitvity. And I believe there is not memory leak question. It works well for me! Hope helpful
回答5:
Here is an implementation that will allow an AlertDialog to be displayed on top of the current active activity (this is an example of a message dialog, but can be used for alerts as well).
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();
}
}
The dialog will be displayed even if the context on which it was instantiated with is no longer active, much like Toast
.
Call with new AlertsDialogue(MyActivity.this, "title", "message");
No additional permissions are required in the AndroidManifest file.
回答6:
What type of network tasks are going on in the background. I would suggest possibly rethinking the design. Perhaps a notification would be better? Or maybe a "results summary" screen. As a user I Would rather a non obtrusive signal of error if I am not actively waiting for the task to complete.
回答7:
Why don't use event bus (https://github.com/greenrobot/EventBus) ?
Define events:
public class ShowErrorMessageEvent {
public String errorMessage;
public ShowErrorMessageEvent(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return this.errorMessage;
}
}
Prepare subscribers for all activities you needed:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
In all activities, show the dialog if the event received
public void onEvent(ShowErrorMessageEvent event) {
/* Show dialog with event.getErrorMessage() from background thread */
};
In your background thread, post the error event:
EventBus.getDefault().post(new ShowErrorMessageEvent("This is an error message!"));
回答8:
you need to reset type of Window which dialog attached, as follow:
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
do not forget declaring "android.permission.SYSTEM_ALERT_WINDOW" permission in your manifest.
回答9:
Well, I think that the best for you would be an AsyncTask. Here an example and doc:
http://developer.android.com/reference/android/os/AsyncTask.html