Android WindowManagerService解析(5)
来源:互联网 发布:淘宝联盟验证失败 编辑:程序博客网 时间:2024/06/07 07:18
强烈建议看这篇文章前把前面的几篇都看看,这样理解更深刻
下面来看看Toast的显示过程
一、Toast的创建
public static Toast makeText(Context context, CharSequence text, int duration) { Toast result = new Toast(context); LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration; return result;}
这里就是创建了一个Toast实例,并且得到一个需要显示的view,对应的就是Toast的mNextView。如果我们需要自定义一个view,也可以调用setView方法。
public void setView(View view) { mNextView = view;}
二、显示过程
这个显示过程比前面要复杂了,但是原理其实是一样的,下面我们来看看。
public void show() { // 1、在显示前mNextView必须不为空,这个mNextView就是我们需要设置的view if (mNextView == null) { throw new RuntimeException("setView must have been called"); } // 2、获取一个NotificationManagerService的远程服务代理对象 INotificationManager service = getService(); String pkg = mContext.getPackageName(); // 3、TN是一个binder的跨进程对象,提供给NotificationManagerService远程服务进行回调 TN tn = mTN; tn.mNextView = mNextView; try { // 4、这里维系了一个队列来显示Toast service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty }}
首先来看看getService方法。
static private INotificationManager getService() { if (sService != null) { return sService; } sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification")); return sService;}
它得到的就是一个NotificationManagerService的远程代理对象。
我们来看看NotificationManagerService的远程服务的enqueueToast方法。
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 ; } final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { if (!isSystemToast) { Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); return; } } synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); long callingId = Binder.clearCallingIdentity(); try { 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) { 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++; if (count >= MAX_PACKAGE_NOTIFICATIONS) { Slog.e(TAG, "Package has already posted " + count + " toasts. Not showing more. Package=" + pkg); return; } } } } // 可以看到将Toast封装成一个ToastRecord对象,加入到mToastQueue队列中去 record = new ToastRecord(callingPid, pkg, callback, duration); mToastQueue.add(record); index = mToastQueue.size() - 1; 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) { showNextToastLocked(); } } finally { Binder.restoreCallingIdentity(callingId); } }}
上面就是将需要显示的Toast封装成一个ToastRecord一个一个的加入到mToastQueue队列中去,一个一个的调用showNextToastLocked方法进行显示。下面看看showNextToastLocked方法
private void showNextToastLocked() { // 1、得到ToastRecord对象 ToastRecord record = mToastQueue.get(0); while (record != null) { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { // record中有一个callback,它就是前面说的TN对象,是一个跨进程的binder对象 // 这里就是调用的TN的show方法。 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; } } }}
从代码中可以看到它调用的就是TN的show方法,TN类在Toast文件中。
private static class TN extends ITransientNotification.Stub { final Runnable mShow = new Runnable() { @Override public void run() { // 4、在mShow的Runnable中调用的是handleShow方法 handleShow(); } }; final Runnable mHide = new Runnable() { @Override public void run() { handleHide(); // Don't do this in handleHide() because it is also invoked by handleShow() mNextView = null; } }; // 1、创建WindowManager的布局属性 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); final Handler mHandler = new Handler(); int mGravity; int mX, mY; float mHorizontalMargin; float mVerticalMargin; View mView; View mNextView; WindowManager mWM; TN() { // 2、对布局属性进行设置 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; } // 3、这个就是回调的show方法 @Override public void show() { // 它直接往handler中放入一个mShow,mShow是Runnable对象 mHandler.post(mShow); } // 5、处理最终的显示 public void handleShow() { if (mView != mNextView) { handleHide(); mView = mNextView; Context context = mView.getContext().getApplicationContext(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); final Configuration config = mView.getContext().getResources().getConfiguration(); final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection()); mParams.gravity = gravity; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { mParams.verticalWeight = 1.0f; } mParams.x = mX; mParams.y = mY; mParams.verticalMargin = mVerticalMargin; mParams.horizontalMargin = mHorizontalMargin; if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this); mWM.addView(mView, mParams); trySendAccessibilityEvent(); } }}
从上面我们可以看到,它最终还是调用的WindowManager的addView方法。看来UI显示是永远也绕不过WindowManagerService,这里我们可以看到,虽然我们提供了这么多UI的显示窗口,但最终的实现都是一样,都是有WindowManager来处理的。
阅读全文
0 0
- Android WindowManagerService解析(5)
- Android WindowManagerService解析(1)
- Android WindowManagerService解析(2)
- Android WindowManagerService解析(3)
- Android WindowManagerService解析(4)
- Android WindowManagerService解析(6)
- Android解析WindowManagerService(一)WMS的诞生
- Android WindowManagerService
- Android WindowManagerService
- Android解析WindowManagerService(二)WMS的重要成员和Window的添加过程
- android WindowManagerService addFakeWindow 研究
- Android按键消息传播流程(WindowManagerService.java)
- Android按键消息传播流程(WindowManagerService.java)
- Android WindowManagerService机制分析(一):窗口的显示层级
- WindowManagerService
- Android WindowManagerService相关的Session
- Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析
- Android窗口管理服务WindowManagerService对输入法窗口(Input Method Window)的管理分析
- 自定义股票键盘
- 关于git初步使用以及遇到的坑有借鉴
- ue4 创建简易动画
- C#_需要注意的地方
- C++中四种强制转换
- Android WindowManagerService解析(5)
- 动态规划(状态压缩)--铺地板
- iOS 获取系统声音
- datstage处理文本文件中存在多余换行符的数据
- ant table 添加双击展开扩展显示信息
- Java继承
- uva1606 Amphiphilic Carbon Molecules
- getOSType
- DP—最长下降子序列(代码)