- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
2.3. enqueueToast
加入队列,用来显示 Toast,队列最大数 50
@Override public void enqueueToast(String pkg, ITransientNotification callback, int duration) { if (DBG) { Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); } if (pkg == null || callback == null) { Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); return ; } //(1) 判断是否系统的 Toast,如果当前包名是 android 则为系统 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); //判断当前 toast 所属的 pkg 是不是所阻止的 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { if (!isSystemToast) { Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); return; } } //入队列 mToastQueue synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); long callingId = Binder.clearCallingIdentity(); try { ToastRecord record; //(2) 判断 Toast 是否在队列当中 int index = indexOfToastLocked(pkg, callback); // If it's already in the queue, we update it in place, we don't // move it to the end of the queue. if (index >= 0) { record = mToastQueue.get(index); record.update(duration); } else { // Limit the number of toasts that any given package except the android // package can enqueue. Prevents DOS attacks and deals with leaks. if (!isSystemToast) { int count = 0; final int N = mToastQueue.size(); for (int i=0; i<N; i++) { final ToastRecord r = mToastQueue.get(i); if (r.pkg.equals(pkg)) { count++; //toasts 最大数 50 个 if (count >= MAX_PACKAGE_NOTIFICATIONS) { Slog.e(TAG, "Package has already posted " + count + " toasts. Not showing more. Package=" + pkg); return; } } } } //获得 ToastRecord 对象 record = new ToastRecord(callingPid, pkg, callback, duration); //放入 mToastQueue 中 mToastQueue.add(record); index = mToastQueue.size() - 1; //(3) 设置该 Toast 为前台进程 keepProcessAliveLocked(callingPid); } // If it's at index 0, it's the current toast. It doesn't matter if it's // new or just been updated. Call back and tell it to show itself. // If the callback fails, this will remove it from the list, so don't // assume that it's valid after this. if (index == 0) { //(4) 直接显示 Toast showNextToastLocked(); } } finally { Binder.restoreCallingIdentity(callingId); } } }
(1) 判断是否系统的 Toast,源码:
private static boolean isCallerSystem() { return isUidSystem(Binder.getCallingUid()); } private static boolean isUidSystem(int uid) { final int appid = UserHandle.getAppId(uid); // 判断 pid 为系统进程使用的用户 id,值为 1000,或者为系统进程的手机的用户 id,值为 1001 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); }
(2) 判断 Toast 是否在队列当中,源码:
// lock on mToastQueue int indexOfToastLocked(String pkg, ITransientNotification callback) { // IBinder cbak = callback.asBinder(); ArrayList<ToastRecord> list = mToastQueue; int len = list.size(); for (int i=0; i<len; i++) { ToastRecord r = list.get(i); if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { return i; } } return -1; }
(3) 设置该 Toast 为前台进程,源码:
// lock on mToastQueue void keepProcessAliveLocked(int pid) { // toasts from this pid int toastCount = 0; ArrayList<ToastRecord> list = mToastQueue; int N = list.size(); for (int i=0; i<N; i++) { ToastRecord r = list.get(i); if (r.pid == pid) { toastCount++; } } try { // 设置该 Toast 为前台进程 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); } catch (RemoteException e) { // Shouldn't happen. } }
(4) 直接显示 Toast,源码:
void showNextToastLocked() { //直接取第一个 ToastRecord record = mToastQueue.get(0); while (record != null) { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { //回调 TN 类,显示 Toast record.callback.show(); //设置消失 scheduleTimeoutLocked(record); return; } catch (RemoteException e) { Slog.w(TAG, "Object died trying to show notification " + record.callback + " in package " + record.pkg); // remove it from the list and let the process die int index = mToastQueue.indexOf(record); if (index >= 0) { mToastQueue.remove(index); } keepProcessAliveLocked(record.pid); if (mToastQueue.size() > 0) { record = mToastQueue.get(0); } else { record = null; } } } } private void scheduleTimeoutLocked(ToastRecord r) { //移除 ToastRecord mHandler.removeCallbacksAndMessages(r); Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); //static final int LONG_DELAY = 3500; // 3.5 seconds //static final int SHORT_DELAY = 2000; // 2 seconds long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; //发送 Toast 消失的 message mHandler.sendMessageDelayed(m, delay); }
从 enqueueToast 方法可知,先判断是不是系统和合法的 Toast,然后判断是否在 ToastQueue(这里解释了很多 Toast,是一个个显示的),如果存在,只需要更新 Toast 显示的时间,如果不在,就直接显示,回调给 TN 类。 到这里,知道了 Toast 是如何显示的。
还没有结束,继续追踪 mHandler,来到 WorkerHandler :
private final class WorkerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_TIMEOUT: handleTimeout((ToastRecord)msg.obj); break; //…… } } } private void handleTimeout(ToastRecord record) { if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); synchronized (mToastQueue) { //还是判断 Toast 是否在队列当中 int index = indexOfToastLocked(record.pkg, record.callback); if (index >= 0) { cancelToastLocked(index); } } } void cancelToastLocked(int index) { ToastRecord record = mToastQueue.get(index); try { //回调 TN 类,Toast 消失 record.callback.hide(); } catch (RemoteException e) { Slog.w(TAG, "Object died trying to hide notification " + record.callback + " in package " + record.pkg); // don't worry about this, we're about to remove it from // the list anyway } //该 ToastRecord 对象从 mToastQueue 中移除 mToastQueue.remove(index); //设置该 Toast 为前台进程 keepProcessAliveLocked(record.pid); if (mToastQueue.size() > 0) { // Show the next one. If the callback fails, this will remove // it from the list, so don't assume that the list hasn't changed // after this point. //继续 show 下个 Toast showNextToastLocked(); } }
到这里,知道了 Toast 是如何消失的。Toast 核心代码显示和消失源码分析完毕。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论