Skip to content

Commit eb930a0

Browse files
committed
修复在跳转 Activity 时无法显示自定义样式 Toast 的问题
1 parent 1fe77db commit eb930a0

File tree

1 file changed

+80
-47
lines changed

1 file changed

+80
-47
lines changed

library/src/main/java/com/hjq/toast/ToastStrategy.java

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ public class ToastStrategy implements IToastStrategy {
5858
/** 吐司显示策略 */
5959
private final int mShowStrategyType;
6060

61-
/** 显示消息 Token */
62-
private final Object mShowMessageToken = new Object();
63-
/** 取消消息 Token */
64-
private final Object mCancelMessageToken = new Object();
65-
6661
/** 上一个 Toast 显示的时间 */
6762
private volatile long mLastShowToastMillis;
6863

@@ -93,24 +88,24 @@ public int computeShowDuration(CharSequence text) {
9388

9489
@Override
9590
public IToast createToast(ToastParams params) {
96-
Activity availableActivity = getAvailableActivity();
91+
Activity toastActivity = getToastActivity();
9792
IToast toast;
9893
if ((params.priorityType == ToastParams.PRIORITY_TYPE_DEFAULT ||
99-
params.priorityType == ToastParams.PRIORITY_TYPE_GLOBAL) &&
100-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
101-
Settings.canDrawOverlays(mApplication)) {
94+
params.priorityType == ToastParams.PRIORITY_TYPE_GLOBAL) &&
95+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
96+
Settings.canDrawOverlays(mApplication)) {
10297
// 如果有悬浮窗权限,就开启全局的 Toast
10398
toast = new GlobalToast(mApplication);
104-
} else if (availableActivity != null &&
105-
(params.priorityType == ToastParams.PRIORITY_TYPE_DEFAULT ||
106-
params.priorityType == ToastParams.PRIORITY_TYPE_LOCAL)) {
99+
} else if (toastActivity != null &&
100+
(params.priorityType == ToastParams.PRIORITY_TYPE_DEFAULT ||
101+
params.priorityType == ToastParams.PRIORITY_TYPE_LOCAL)) {
107102
// 如果没有悬浮窗权限,就开启一个依附于 Activity 的 Toast
108-
toast = new ActivityToast(availableActivity);
103+
toast = new ActivityToast(toastActivity);
109104
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {
110105
// 处理 Android 7.1 上 Toast 在主线程被阻塞后会导致报错的问题
111106
toast = new SafeToast(mApplication);
112107
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
113-
!areNotificationsEnabled(mApplication)) {
108+
!areNotificationsEnabled(mApplication)) {
114109
// 处理 Toast 关闭通知栏权限之后无法弹出的问题
115110
// 通过查看和对比 NotificationManagerService 的源码
116111
// 发现这个问题已经在 Android 10 版本上面修复了
@@ -129,37 +124,13 @@ public IToast createToast(ToastParams params) {
129124

130125
@Override
131126
public void showToast(ToastParams params) {
132-
switch (mShowStrategyType) {
133-
case SHOW_STRATEGY_TYPE_IMMEDIATELY: {
134-
// 移除之前未显示的 Toast 消息
135-
cancelToast();
136-
long uptimeMillis = SystemClock.uptimeMillis() + params.delayMillis + generateShowDelayTime(params);
137-
getHandler().postAtTime(new ShowToastRunnable(params), mShowMessageToken, uptimeMillis);
138-
break;
139-
}
140-
case SHOW_STRATEGY_TYPE_QUEUE: {
141-
// 计算出这个 Toast 显示时间
142-
long showToastMillis = SystemClock.uptimeMillis() + params.delayMillis + generateShowDelayTime(params);
143-
// 根据吐司的长短计算出等待时间
144-
long waitMillis = generateToastWaitMillis(params);
145-
// 如果当前显示的时间在上一个 Toast 的显示范围之内
146-
// 那么就重新计算 Toast 的显示时间
147-
if (showToastMillis < (mLastShowToastMillis + waitMillis)) {
148-
showToastMillis = mLastShowToastMillis + waitMillis;
149-
}
150-
getHandler().postAtTime(new ShowToastRunnable(params), mShowMessageToken, showToastMillis);
151-
mLastShowToastMillis = showToastMillis;
152-
break;
153-
}
154-
default:
155-
break;
156-
}
127+
HANDLER.postDelayed(new ShowRunnable(params), getMustShowDelayDuration(params));
157128
}
158129

159130
@Override
160131
public void cancelToast() {
161132
long uptimeMillis = SystemClock.uptimeMillis();
162-
getHandler().postAtTime(new CancelToastRunnable(), mCancelMessageToken, uptimeMillis);
133+
HANDLER.postAtTime(new CancelRunnable(), uptimeMillis);
163134
}
164135

165136
/**
@@ -170,10 +141,30 @@ protected static Handler getHandler() {
170141
}
171142

172143
/**
173-
* 生成默认延迟时间
144+
* 获取必须的显示延迟时长
145+
*/
146+
protected int getMustShowDelayDuration(ToastParams params) {
147+
if (params.priorityType != ToastParams.PRIORITY_TYPE_GLOBAL) {
148+
return 100;
149+
}
150+
return 0;
151+
}
152+
153+
/**
154+
* 获取最佳的显示延迟时长
174155
*/
175-
protected int generateShowDelayTime(ToastParams params) {
176-
return 100;
156+
protected int getBestShowDelayDuration(ToastParams params) {
157+
if (params.priorityType != ToastParams.PRIORITY_TYPE_GLOBAL && getToastActivity() == null) {
158+
// 延迟一段时间之后再执行,因为在没有通知栏权限的情况下,Toast 只能显示在当前 Activity 上面(即使用 ActivityToast)
159+
// 如果当前 Activity 在 showToast 之后立马进行 finish 了,那么这个时候 Toast 可能会显示不出来
160+
// 因为 Toast 会显示在销毁 Activity 界面上,而不会显示在新跳转的 Activity 上面,所以会导致显示不出来的问题
161+
// 另外有悬浮窗权限的情况下,使用全局的 Toast(即使用 GlobalToast),这种立刻显示也会有一些问题
162+
// 我在小米 12 Android 12 的手机上面测试,从权限设置页返回的时候,发现 Toast 有几率会从左上的位置显示,然后会变回正常的位置
163+
// 如果是系统的 Toast(即使用 SystemToast、SafeToast、NotificationToast 任意一个)则不会有这个问题
164+
// 300 只是一个经验值,经过很多次验证得出来的值,当然你觉得这个值不是自己想要的,也可以重写此方法改成自己想要的
165+
return 300;
166+
}
167+
return 0;
177168
}
178169

179170
/**
@@ -211,6 +202,47 @@ protected int generateToastWaitMillis(ToastParams params) {
211202
/**
212203
* 显示任务
213204
*/
205+
private class ShowRunnable implements Runnable {
206+
207+
private final ToastParams mToastParams;
208+
209+
private ShowRunnable(ToastParams params) {
210+
mToastParams = params;
211+
}
212+
213+
@Override
214+
public void run() {
215+
switch (mShowStrategyType) {
216+
case SHOW_STRATEGY_TYPE_IMMEDIATELY: {
217+
// 移除之前未显示的 Toast 消息
218+
long uptimeMillis = SystemClock.uptimeMillis() + mToastParams.delayMillis + getBestShowDelayDuration(mToastParams);
219+
HANDLER.postAtTime(new ShowToastRunnable(mToastParams), uptimeMillis);
220+
break;
221+
}
222+
case SHOW_STRATEGY_TYPE_QUEUE: {
223+
// 计算出这个 Toast 显示时间
224+
long showToastMillis =
225+
SystemClock.uptimeMillis() + mToastParams.delayMillis + getBestShowDelayDuration(mToastParams);
226+
// 根据吐司的长短计算出等待时间
227+
long waitMillis = generateToastWaitMillis(mToastParams);
228+
// 如果当前显示的时间在上一个 Toast 的显示范围之内
229+
// 那么就重新计算 Toast 的显示时间
230+
if (showToastMillis < (mLastShowToastMillis + waitMillis)) {
231+
showToastMillis = mLastShowToastMillis + waitMillis;
232+
}
233+
HANDLER.postAtTime(new ShowToastRunnable(mToastParams), showToastMillis);
234+
mLastShowToastMillis = showToastMillis;
235+
break;
236+
}
237+
default:
238+
break;
239+
}
240+
}
241+
}
242+
243+
/**
244+
* 显示 Toast 任务
245+
*/
214246
private class ShowToastRunnable implements Runnable {
215247

216248
private final ToastParams mToastParams;
@@ -243,7 +275,7 @@ public void run() {
243275
/**
244276
* 取消任务
245277
*/
246-
private class CancelToastRunnable implements Runnable {
278+
private class CancelRunnable implements Runnable {
247279

248280
@Override
249281
public void run() {
@@ -318,9 +350,9 @@ protected boolean areNotificationsEnabled(Context context) {
318350
}
319351

320352
/**
321-
* 获取可用的 Activity
353+
* 获取显示自定义 Toast 的 Activity 对象
322354
*/
323-
protected Activity getAvailableActivity() {
355+
protected Activity getToastActivity() {
324356
Activity visibleActivity = ActivityStack.getInstance().getVisibleActivity();
325357
Activity focusActivity = ActivityStack.getInstance().getFocusActivity();
326358
if (focusActivity == null || visibleActivity == null) {
@@ -348,7 +380,8 @@ protected Activity getAvailableActivity() {
348380
// 如果你想要在 startActivity 的时候,又能显示自定义 Toast 的效果,
349381
// 建议在显示 show 的时候加一下延迟显示,建议设置在 500 毫秒及以上,
350382
// 以确保 Toast 在显示的时候不受 Android 16 残影问题的影响。
351-
if (System.currentTimeMillis() - resumedActivityTime < 200) {
383+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA &&
384+
System.currentTimeMillis() - resumedActivityTime < 200) {
352385
return null;
353386
}
354387

0 commit comments

Comments
 (0)