Toast实现源码解析
2017-07-05 21:50
218 查看
说明
本篇文章用于介绍Android中Toast的实现原理。和简单实现一个自定义的Toast.Toast实现
一般常用Toast格式为:Toast.makeText(context,"text.",Toast.LENGTH_LONG).show();
就此,对Toast做一个了解.首先,Toast调用来了一个静态方法makeText(…),具体实现如下:
/** * Make a standard toast that just contains a text view. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. * @param text The text to show. Can be formatted text. * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or * {@link #LENGTH_LONG} * */ 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的三个属性:mNextView、mDuration.mNextView为显示的Toast view,Toast中使用的是系统的一套资源layout,我们其实可以据此替换绘制的View.再看,这里的Toast 对象的生成,代码如下:
public Toast(Context context) { mContext = context; mTN = new TN(); mTN.mY = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.toast_y_offset); mTN.mGravity = context.getResources().getInteger( com.android.internal.R.integer.config_toastDefaultGravity); }
发现生成一个TN对象,发现其是处理Toast显示的一个服务处理类。再看Toast.show()方法,代码如下:
public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }
通过代码发现,这里通过Binder方式将处理发送给了INotificationManager来处理,INotificationManager.aidl的实现类为com.android.server.NotificationManagerService,在源码中找到enqueueToast(…)实现如下:
@Override public void enqueueToast(String pkg, ITransientNotification callback, int duration) { ........... 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; } } } } 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发送的enqueue会被保存在一个List列表中,最后显示操作如下:
void showNextToastLocked() { ToastRecord record = mToastQueue.get(0); while (record != null) { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { 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; } } } }
其中,scheduleTimeoutLocked(record);控制显示的时间,Toast提供的显示时间有俩个。通过设置Toast的duration类型控制,如下:
static final int LONG_DELAY = 3500; // 3.5 seconds static final int SHORT_DELAY = 2000; // 2 seconds
处理回调交由Toast中的ITransientNotification.Stub 处理,即TN对象,show方法启动mShow线程,线程执行代码如下:
public void handleShow() { if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView=" + mNextView); if (mView != mNextView) { // remove the old view if necessary handleHide(); mView = mNextView; Context context = mView.getContext().getApplicationContext(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); // We can resolve the Gravity here by using the Locale for getting // the layout direction 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来加载Toast界面。
自定义Toast
自定义Toast就简单了,两种思路:一个是通过自定义toast的view;一个是通过WindowManager来控制加载view实现一个Toast,这个是最终的解决办法.如下,为简单的替换一个view,代码如下:private void showToast(Context context){ Toast toast=new Toast(context.getApplicationContext()); LayoutInflater inflate = (LayoutInflater) this.getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(R.layout.test, null); toast.setView(v); toast.setDuration(Toast.LENGTH_LONG); toast.show(); }
布局文件代码为:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="5dip" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:text="cancel" android:background="@drawable/aa" android:textColor="@android:color/holo_blue_bright" android:layout_width="150dip" android:layout_height="50dip" /> </LinearLayout>
背景控制xml为:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <stroke android:width="1dp" android:color="#63a219"></stroke> <corners android:radius="5dp" /> </shape>
如上,为我对Toast实现的浅显认识,表做记录。
Enjoytoday,EnjoyCoding
相关文章推荐
- android从源码解析并实现各种Toast效果合集
- android从源码解析并实现各种Toast效果合集
- [绝冬城]Sniff C++实现源码解析
- PHP中实现汉字转区位码应用源码实例解析
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- Latent Dirichlet Allocation(LDA)主题模型算法实现及源码解析
- feathers ui 实现机制深入解析(feathers ui 源码解析-原创)
- GoF 23种设模式解析附C++实现源码(k_eckel转自微软高校博客K_eckel's mindview)
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 解析pcap文件及读取实现源码
- 常见设计模式的解析和实现(C++)文档及源码打包下载
- 【Android常用控件】Toast的几种效果实现(附源码)
- 源码解读:java 解析字符串为boolean四种实现方法的细节
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 两种实现Toast 的源码
- 设计模式精解-GoF 23种设计模式解析附C++实现源码
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- Django源码解析(三) Django开发服务器,WSGI规范实现.
- 使用Android自带Gallery组件实现CoverFlow,源码+解析