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