Skip to content

Commit 06ecaee

Browse files
committed
处理在 Android 16 上 startActivity 时显示自定义的 Toast 会出现残影的问题
1 parent 898e604 commit 06ecaee

File tree

6 files changed

+84
-39
lines changed

6 files changed

+84
-39
lines changed

app/src/main/java/com/hjq/toast/demo/MainActivity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ public void run() {
9898
}).start();
9999
}
100100

101+
public void startActivityShowToast(View v) {
102+
Toaster.show(R.string.demo_show_toast_result);
103+
startActivity(new Intent(this, MainActivity.class));
104+
finish();
105+
}
106+
101107
public void switchToastStyleToWhite(View v) {
102108
ToastParams params = new ToastParams();
103109
params.text = getString(R.string.demo_switch_to_white_style_result);

app/src/main/res/layout/activity_main.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@
7878
android:onClick="threadShowToast"
7979
android:text="@string/demo_show_toast_in_the_subthread" />
8080

81+
<Button
82+
android:layout_width="wrap_content"
83+
android:layout_height="wrap_content"
84+
android:layout_gravity="center_horizontal"
85+
android:layout_marginTop="10dp"
86+
android:onClick="startActivityShowToast"
87+
android:text="@string/demo_show_toast_in_the_start_activity" />
88+
8189
<Button
8290
android:layout_width="wrap_content"
8391
android:layout_height="wrap_content"

app/src/main/res/values-zh/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<string name="demo_show_cross_page_toast">显示一个跨页面的 Toast</string>
88
<string name="demo_show_toast_with_two_second_delay">延迟 2 秒显示 Toast</string>
99
<string name="demo_show_toast_in_the_subthread">在子线程中显示吐司</string>
10+
<string name="demo_show_toast_in_the_start_activity">在跳转 Activity 时显示吐司</string>
1011
<string name="demo_switch_to_white_style">切换为白色样式(局部生效)</string>
1112
<string name="demo_switch_to_black_style">切换为黑色样式(局部生效)</string>
1213
<string name="demo_switch_to_hint_style">切换为提示样式(局部生效)</string>
@@ -25,6 +26,7 @@
2526
<string name="demo_show_cross_page_toast_result">我是跨页面显示的 Toast</string>
2627
<string name="demo_show_toast_with_two_second_delay_result">我是延迟 2 秒显示的 Toast</string>
2728
<string name="demo_show_toast_in_the_subthread_result">我是子线程中弹出的吐司</string>
29+
<string name="demo_show_toast_in_the_start_activity_result">我是在 startActivity 时弹出的吐司</string>
2830
<string name="demo_switch_to_white_style_result">我是白色样式的 Toast</string>
2931
<string name="demo_switch_to_black_style_result">我是黑色样式的 Toast</string>
3032
<string name="demo_switch_to_info_style_result">我是提示样式的 Toast</string>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<string name="demo_show_cross_page_toast">Show a cross page toast</string>
99
<string name="demo_show_toast_with_two_second_delay">Show toast with 2 second delay</string>
1010
<string name="demo_show_toast_in_the_subthread">Show toast in the subthread</string>
11+
<string name="demo_show_toast_in_the_start_activity">Show toast in the startActivity</string>
1112
<string name="demo_switch_to_white_style">Switch to white style (local effect)</string>
1213
<string name="demo_switch_to_black_style">Switch to black style (local effect)</string>
1314
<string name="demo_switch_to_hint_style">Switch to hint style (local effect)</string>
@@ -26,6 +27,7 @@
2627
<string name="demo_show_cross_page_toast_result">I am a cross page toast</string>
2728
<string name="demo_show_toast_with_two_second_delay_result">I am a toast displayed with a delay of 2 seconds</string>
2829
<string name="demo_show_toast_in_the_subthread_result">I am the toast that pops up in the subthread</string>
30+
<string name="demo_show_toast_in_the_start_activity_result">I am the toast that pops up when startActivity</string>
2931
<string name="demo_switch_to_white_style_result">I am toast in white style</string>
3032
<string name="demo_switch_to_black_style_result">I am toast in black style</string>
3133
<string name="demo_switch_to_info_style_result">I am a toast of hint style</string>

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,34 +40,57 @@ public void register(Application application) {
4040
application.registerActivityLifecycleCallbacks(this);
4141
}
4242

43-
/** 前台 Activity 对象 */
44-
private Activity mForegroundActivity;
43+
/** 当前焦点的 Activity 对象 */
44+
private Activity mFocusActivity;
4545

46-
public Activity getForegroundActivity() {
47-
return mForegroundActivity;
46+
/** 当前可见的 Activity 对象 */
47+
private Activity mVisibleActivity;
48+
49+
/** Activity 可见时候的时间戳 */
50+
private long mActivityResumedTime;
51+
52+
public Activity getFocusActivity() {
53+
return mFocusActivity;
54+
}
55+
56+
public Activity getVisibleActivity() {
57+
return mVisibleActivity;
58+
}
59+
60+
public long getActivityResumedTime() {
61+
return mActivityResumedTime;
4862
}
4963

5064
@Override
5165
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
5266

5367
@Override
54-
public void onActivityStarted(Activity activity) {}
68+
public void onActivityStarted(Activity activity) {
69+
mFocusActivity = activity;
70+
}
5571

5672
@Override
5773
public void onActivityResumed(Activity activity) {
58-
mForegroundActivity = activity;
74+
mVisibleActivity = activity;
75+
mActivityResumedTime = System.currentTimeMillis();
5976
}
6077

6178
@Override
6279
public void onActivityPaused(Activity activity) {
63-
if (mForegroundActivity != activity) {
80+
if (mFocusActivity != activity) {
6481
return;
6582
}
66-
mForegroundActivity = null;
83+
mFocusActivity = null;
6784
}
6885

6986
@Override
70-
public void onActivityStopped(Activity activity) {}
87+
public void onActivityStopped(Activity activity) {
88+
if (mVisibleActivity != activity) {
89+
return;
90+
}
91+
mVisibleActivity = null;
92+
mActivityResumedTime = 0;
93+
}
7194

7295
@Override
7396
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}

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

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import android.app.NotificationManager;
88
import android.content.Context;
99
import android.os.Build;
10-
import android.os.Build.VERSION;
11-
import android.os.Build.VERSION_CODES;
1210
import android.os.Handler;
1311
import android.os.Looper;
1412
import android.os.SystemClock;
@@ -95,19 +93,19 @@ public int computeShowDuration(CharSequence text) {
9593

9694
@Override
9795
public IToast createToast(ToastParams params) {
98-
Activity foregroundActivity = getForegroundActivity();
96+
Activity availableActivity = getAvailableActivity();
9997
IToast toast;
10098
if ((params.priorityType == ToastParams.PRIORITY_TYPE_DEFAULT ||
10199
params.priorityType == ToastParams.PRIORITY_TYPE_GLOBAL) &&
102100
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
103101
Settings.canDrawOverlays(mApplication)) {
104102
// 如果有悬浮窗权限,就开启全局的 Toast
105103
toast = new GlobalToast(mApplication);
106-
} else if ((params.priorityType == ToastParams.PRIORITY_TYPE_DEFAULT ||
107-
params.priorityType == ToastParams.PRIORITY_TYPE_LOCAL) &&
108-
isActivityAvailable(foregroundActivity)) {
104+
} else if (availableActivity != null &&
105+
(params.priorityType == ToastParams.PRIORITY_TYPE_DEFAULT ||
106+
params.priorityType == ToastParams.PRIORITY_TYPE_LOCAL)) {
109107
// 如果没有悬浮窗权限,就开启一个依附于 Activity 的 Toast
110-
toast = new ActivityToast(foregroundActivity);
108+
toast = new ActivityToast(availableActivity);
111109
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {
112110
// 处理 Android 7.1 上 Toast 在主线程被阻塞后会导致报错的问题
113111
toast = new SafeToast(mApplication);
@@ -175,14 +173,7 @@ protected static Handler getHandler() {
175173
* 生成默认延迟时间
176174
*/
177175
protected int generateShowDelayTime(ToastParams params) {
178-
// 延迟一段时间之后再执行,因为在没有通知栏权限的情况下,Toast 只能显示在当前 Activity 上面(即使用 ActivityToast)
179-
// 如果当前 Activity 在 showToast 之后立马进行 finish 了,那么这个时候 Toast 可能会显示不出来
180-
// 因为 Toast 会显示在销毁 Activity 界面上,而不会显示在新跳转的 Activity 上面,所以会导致显示不出来的问题
181-
// 另外有悬浮窗权限的情况下,使用全局的 Toast(即使用 GlobalToast),这种立刻显示也会有一些问题
182-
// 我在小米 12 Android 12 的手机上面测试,从权限设置页返回的时候,发现 Toast 有几率会从左上的位置显示,然后会变回正常的位置
183-
// 如果是系统的 Toast(即使用 SystemToast、SafeToast、NotificationToast 任意一个)则不会有这个问题
184-
// 300 只是一个经验值,经过很多次验证得出来的值,当然你觉得这个值不是自己想要的,也可以重写此方法改成自己想要的
185-
return 300;
176+
return 100;
186177
}
187178

188179
/**
@@ -327,27 +318,40 @@ protected boolean areNotificationsEnabled(Context context) {
327318
}
328319

329320
/**
330-
* 获取前台的 Activity
321+
* 获取可用的 Activity
331322
*/
332-
protected Activity getForegroundActivity() {
333-
return ActivityStack.getInstance().getForegroundActivity();
334-
}
323+
protected Activity getAvailableActivity() {
324+
Activity visibleActivity = ActivityStack.getInstance().getVisibleActivity();
325+
Activity focusActivity = ActivityStack.getInstance().getFocusActivity();
326+
if (focusActivity == null || visibleActivity == null) {
327+
return null;
328+
}
335329

336-
/**
337-
* Activity 是否可用
338-
*/
339-
protected boolean isActivityAvailable(Activity activity) {
340-
if (activity == null) {
341-
return false;
330+
if (focusActivity != visibleActivity) {
331+
return null;
342332
}
343333

344-
if (activity.isFinishing()) {
345-
return false;
334+
if (visibleActivity.isFinishing()) {
335+
return null;
346336
}
347337

348-
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
349-
return !activity.isDestroyed();
338+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && visibleActivity.isDestroyed()) {
339+
return null;
350340
}
351-
return true;
341+
342+
long resumedActivityTime = ActivityStack.getInstance().getActivityResumedTime();
343+
// 如果这个 Activity 可见时长没有超过 200 毫秒,则证明 Activity 是刚跳转的,
344+
// 在这种情况下,如果直接拿来显示 WindowManager 是会有问题的,在 Android 16 上面会出现残影的情况
345+
// Github issue 地址:https://github.com/getActivity/Toaster/issues/150
346+
// 目前能想到比较好的解决方案是:必须 Activity 可见时长超过 200 毫秒才可以,
347+
// 否则就不能用 Activity 来显示 Toast,只能用系统的 Toast 来显示了,
348+
// 如果你想要在 startActivity 的时候,又能显示自定义 Toast 的效果,
349+
// 建议在显示 show 的时候加一下延迟显示,建议设置在 500 毫秒及以上,
350+
// 以确保 Toast 在显示的时候不受 Android 16 残影问题的影响。
351+
if (System.currentTimeMillis() - resumedActivityTime < 200) {
352+
return null;
353+
}
354+
355+
return visibleActivity;
352356
}
353357
}

0 commit comments

Comments
 (0)