Toast的window创建过程以及源码分析
Posted show_wjy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Toast的window创建过程以及源码分析相关的知识,希望对你有一定的参考价值。
Toast的window创建过程以及源码分析
- Toast是基于Window来实现的,系统中继承了handler做定时处理
- 内部实现IPC过程,第一类访问NotificationManagerService(NMS)
- 通过回调TN接口,实现Toast的显示隐藏
- Toast属于Window,它内部的视图由两种方式指定,一种是系统默认的样式,另一种是通过setView方法
来指定一个自定义View
源码分析
- 从Toast的方法开始
public void show()
if (mNextView == null)
throw new RuntimeException("setView must have been called");
//远程调用AIDL
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try
//在NMS中实现插入Toast
service.enqueueToast(pkg, tn, mDuration);
catch (RemoteException e)
// Empty
NotificationManagerService(NMS)
- NMS下的INotificationManager
private final IBinder mService = new INotificationManager.Stub()
//将Toast请求封装成一个ToastRecord对象
ToastRecord record;
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)
//有ArrayList集合保存这个请求对象
record = mToastQueue.get(index);
record.update(duration);
else
//若是非系统应用,最大弹框数为50,DOS
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++;
if (count >= MAX_PACKAGE_NOTIFICATIONS)
//打印
return;
record = new ToastRecord(callingPid, pkg, callback, duration);
mToastQueue.add(record);
index = mToastQueue.size() - 1;
keepProcessAliveLocked(callingPid);
- ToastRecord类介绍,封装请求Toast的对象
private static final class ToastRecord
final int pid;`这里写代码片`
final String pkg;
//用于回调Toast中TN对象
final ITransientNotification callback;
int duration;
//.....
- 最终显示在NMS下的showNextToastLocked中,callback实际上就是Toast中的TN对象的远程Binder,
最终会运行在发起Toast请求的应用的Binder线程池中
void showNextToastLocked()
ToastRecord record = mToastQueue.get(0);
while (record != null)
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try
//回调TN的show方法
record.callback.show();
scheduleTimeoutLocked(record);
return;
//.....
- Toast显示之后,NMS会通过scheduleTimeoutLocked发送延迟消息,时长取决于Toast的时长
private void scheduleTimeoutLocked(ToastRecord r)
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
//默认3.5s和2s
long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
//发送延迟消息
mHandler.sendMessageDelayed(m, delay);
- 同样,Toast显示之后消失,也是通过callback来完成的
void cancelToastLocked(int index)
ToastRecord record = mToastQueue.get(index);
try
//同样使用回调隐藏
record.callback.hide();
catch (RemoteException e)
//打印
//移除已处理过的弹框
mToastQueue.remove(index);
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0)
//如果还有未完成的弹框,继续调用
showNextToastLocked();
Toast中的TN
- Toast的显示和影响过程实际上是通过TN这个类实现的,两个主要方法show和hide,对应显示和隐藏,
这两个方法被NMS跨进程调用,因此它们运行在Binder线程池中,为了将执行环境切换到Toast的请求线程,在内部使用了handler
private static class TN extends ITransientNotification.Stub
final Runnable mShow = new Runnable()
@Override
public void run()
//处理显示的逻辑
handleShow();
;
final Runnable mHide = new Runnable()
@Override
public void run()
//处理隐藏的逻辑
handleHide();
mNextView = null;
;
//构建windManger对象,设置相应params参数
TN()
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@Override
public void show()
if (localLOGV) Log.v(TAG, "SHOW: " + this);
//通过handler传递消息
mHandler.post(mShow);
@Override
public void hide()
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
通过handleShow和handleHide才能真正显示和隐藏Toast的地方
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
//添加
mWM.addView(mView, mParams);
//删除
if (mView.getParent() != null)
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
两个重要的AIDL文件
//TN实现的接口
oneway interface ITransientNotification
void show();
void hide();
interface INotificationManager
/** @deprecated use @link #enqueueNotificationWithTag instead */
void enqueueNotification(String pkg, int id, in Notification notification, inout int[] idReceived);
/** @deprecated use @link #cancelNotificationWithTag instead */
void cancelNotification(String pkg, int id);
void cancelAllNotifications(String pkg);
//管理Toast显示和隐藏
void enqueueToast(String pkg, ITransientNotification callback, int duration);
void cancelToast(String pkg, ITransientNotification callback);
void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
void cancelNotificationWithTag(String pkg, String tag, int id);
以上是关于Toast的window创建过程以及源码分析的主要内容,如果未能解决你的问题,请参考以下文章