- 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 核心代码显示和消失源码分析完毕。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论