为什么 Toast 在应用程序调用 onDestroy() 时仍然显示?
Why Toast keeps showing even when the app called onDestroy()?
假设我在 onCreate()
中有这段代码
for (int i = 0; i < 20; i++) {
Toast.makeText(MainActivity.this, "Toast "+i, Toast.LENGTH_SHORT).show();
}
当我启动应用程序时,Toasts 开始弹出。
现在,当我按下后退按钮时(比方说在 Toast 5 之后)。 onDestroy()
被调用,应用程序被关闭。
但我仍然可以看到 Toast 弹出,直到达到 20 或我从内存中清除应用程序。
问题:
为什么我的代码 运行 在应用程序之外?
我已经给了我的activity的上下文,那么activity被销毁它不应该立即停止吗?
这里 context
不重要吗?
如果您link任何文档,将会很有帮助。
在Toast
class中,Toast.makeText()
是静态的method
。当您调用此方法时,将创建一个新的 Toast
对象并将您传递的 Context
保存在其中,系统的默认布局用于创建一个 view
附加到您的 Toast
对象和重力也被设置为管理你的 toast
将在屏幕上显示的位置。
您的toast
被系统服务显示。此服务维护要显示的 queue
个 toast messages
,并使用其自己的 Thread
显示它们。当您在 toast object
上调用 show()
时,它会将您的 toast
排队到系统服务的消息队列中。因此,当您的 activity
在创建 20 toast
后被销毁时,系统服务已经开始运行并且它的 message queue
中有消息要显示。通过向后按 activity
(销毁)系统无法断定您可能不打算显示剩余的 toast 消息。只有当您从内存中清除您的应用程序时,系统才能自信地推断它不再需要从您的应用程序中显示 toast message
。
有关更多信息,您可以查看 Toast class
的源代码。我为您提供了相关方法。顺便说一句,好问题
Implementation of Toast.makeText
/**
* Make a standard toast to display using the specified looper.
* If looper is null, Looper.myLooper() is used.
* @hide
*/
public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
@NonNull CharSequence text, @Duration int duration) {
Toast result = new Toast(context, looper);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
Creation of new Toast :
/**
* Constructs an empty Toast object. If looper is null, Looper.myLooper() is used.
* @hide
*/
public Toast(@NonNull Context context, @Nullable Looper looper) {
mContext = context; // your passed `context` is saved.
mTN = new TN(context.getPackageName(), looper);
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
Implementation of show()
/**
* Show the view for the specified duration.
*/
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
final int displayId = mContext.getDisplayId();
try {
service.enqueueToast(pkg, tn, mDuration, displayId);
} catch (RemoteException e) {
// Empty
}
}
假设我在 onCreate()
for (int i = 0; i < 20; i++) {
Toast.makeText(MainActivity.this, "Toast "+i, Toast.LENGTH_SHORT).show();
}
当我启动应用程序时,Toasts 开始弹出。
现在,当我按下后退按钮时(比方说在 Toast 5 之后)。 onDestroy()
被调用,应用程序被关闭。
但我仍然可以看到 Toast 弹出,直到达到 20 或我从内存中清除应用程序。
问题:
为什么我的代码 运行 在应用程序之外?
我已经给了我的activity的上下文,那么activity被销毁它不应该立即停止吗?
这里 context
不重要吗?
如果您link任何文档,将会很有帮助。
在Toast
class中,Toast.makeText()
是静态的method
。当您调用此方法时,将创建一个新的 Toast
对象并将您传递的 Context
保存在其中,系统的默认布局用于创建一个 view
附加到您的 Toast
对象和重力也被设置为管理你的 toast
将在屏幕上显示的位置。
您的toast
被系统服务显示。此服务维护要显示的 queue
个 toast messages
,并使用其自己的 Thread
显示它们。当您在 toast object
上调用 show()
时,它会将您的 toast
排队到系统服务的消息队列中。因此,当您的 activity
在创建 20 toast
后被销毁时,系统服务已经开始运行并且它的 message queue
中有消息要显示。通过向后按 activity
(销毁)系统无法断定您可能不打算显示剩余的 toast 消息。只有当您从内存中清除您的应用程序时,系统才能自信地推断它不再需要从您的应用程序中显示 toast message
。
有关更多信息,您可以查看 Toast class
的源代码。我为您提供了相关方法。顺便说一句,好问题
Implementation of Toast.makeText
/**
* Make a standard toast to display using the specified looper.
* If looper is null, Looper.myLooper() is used.
* @hide
*/
public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
@NonNull CharSequence text, @Duration int duration) {
Toast result = new Toast(context, looper);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
Creation of new Toast :
/**
* Constructs an empty Toast object. If looper is null, Looper.myLooper() is used.
* @hide
*/
public Toast(@NonNull Context context, @Nullable Looper looper) {
mContext = context; // your passed `context` is saved.
mTN = new TN(context.getPackageName(), looper);
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
Implementation of show()
/**
* Show the view for the specified duration.
*/
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
final int displayId = mContext.getDisplayId();
try {
service.enqueueToast(pkg, tn, mDuration, displayId);
} catch (RemoteException e) {
// Empty
}
}