Toast在我们实际开发中很常用,而且很简单,但是实现原理还是挺复杂的,只是系统帮开发者封装好了,让开发者觉得很简单。 Toast是所有app都得用的,系统为了协调各种app的toast需要,那么就得系统管理,和系统需要管理所有Activity道理差不多,有个NotificationManagerService,通过对Toast源码需要,我们可以需要到怎么用两个Binder的方式,实现进程间的彼此通信。具体原理可以看Toast源码解析。需要说明的是,每个版本对Toast的源码都是有改动的,所以导致我们在定制化使用Toast的时候会遇到各种各样的问题,比如
- 问题一
Android 7.1.1 Toast引起的Crash 这个的解决办法是,通过hack,try catch住崩溃的地方,实现方案也很多 - 问题二
采用一个toast的方案在有些手机上会有问题,比如Android10上,连续调用toast,等toast消失后,再调用就不展示了,这个应该是toast内部实现做了一些限制,对于这种情况,可以采用使用新toast之前把老toast的cancel来解决。 在这里想说的是,在Android 9中很特殊
@Override
1965 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1966 {
1967 if (DBG) {
1968 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1969 + " duration=" + duration);
1970 }
1971
1972 if (pkg == null || callback == null) {
1973 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1974 return ;
1975 }
1976 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1977 final boolean isPackageSuspended =
1978 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1979
1980 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1981 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1982 || isPackageSuspended)) {
1983 Slog.e(TAG, "Suppressing toast from package " + pkg
1984 + (isPackageSuspended
1985 ? " due to package suspended by administrator."
1986 : " by user request."));
1987 return;
1988 }
1989
1990 synchronized (mToastQueue) {
1991 int callingPid = Binder.getCallingPid();
1992 long callingId = Binder.clearCallingIdentity();
1993 try {
1994 ToastRecord record;
1995 int index;
1996
1997 if (!isSystemToast) {
1998 index = indexOfToastPackageLocked(pkg);
1999 } else {
2000 index = indexOfToastLocked(pkg, callback);
2001 }
2002
2003
2004
2005 if (index >= 0) {
2006 record = mToastQueue.get(index);
2007 record.update(duration);
2008 try {
2009 record.callback.hide();
2010 } catch (RemoteException e) {
2011 }
2012 record.update(callback);
2013 } else {
2014 Binder token = new Binder();
2015 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
2016 record = new ToastRecord(callingPid, pkg, callback, duration, token);
2017 mToastQueue.add(record);
2018 index = mToastQueue.size() - 1;
2019 }
2020 keepProcessAliveIfNeededLocked(callingPid);
2021
2022
2023
2024
2025 if (index == 0) {
2026 showNextToastLocked();
2027 }
2028 } finally {
2029 Binder.restoreCallingIdentity(callingId);
2030 }
2031 }
2032 }
对于已经存在于toast列表中的toast,更新了duration之后,居然调用了hide,这个会夸进程执行到我们app中,会将toast UI拿掉,然后再添加。Android 其它版本都没有这种操作。
彻底理解Toast原理和解决小米MIUI系统上没法弹Toast的问题 Android 7.1.1 Toast引起的Crash
|