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); }
Toast.makeText(this,"吐司",Toast.LENGTH_SHORT).show();
/** * 吐司工具类 避免点击屡次致使吐司屡次,最后致使Toast就长时间关闭不掉了 * 注意:这里若是传入context会报内存泄漏;传递activity..getApplicationContext() * @param content 吐司内容 */ private static Toast toast; @SuppressLint("ShowToast") public static void showToast(String content) { checkContext(); if (toast == null) { toast = Toast.makeText(mApp, content, Toast.LENGTH_SHORT); } else { toast.setText(content); } toast.show(); }
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类中,能够看到,实现了AIDL的show与hide方法php
/** * schedule handleShow into the right thread */ @Override public void show(IBinder windowToken) { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.obtainMessage(0, windowToken).sendToTarget(); }
/**android
/** @hide */ oneway interface ITransientNotification { void show(); void hide(); }
经过AIDL(Binder)通讯拿到NotificationManagerService的服务访问接口,而后把TN对象和一些参数传递到远程NotificationManagerService中去git
当 Toast在show的时候,而后把这个请求放在 NotificationManager 所管理的队列中,而且为了保证 NotificationManager 能跟进程交互,会传递一个TN类型的 Binder对象给NotificationManager系统服务,接着看下面getService方法作了什么?程序员
public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } //经过AIDL(Binder)通讯拿到NotificationManagerService的服务访问接口,当前Toast类至关于上面例子的客户端!!!至关重要!!! INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { //把TN对象和一些参数传递到远程NotificationManagerService中去 service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }
//远程NotificationManagerService的服务访问接口 private static INotificationManager sService; static private INotificationManager getService() { //单例模式 if (sService != null) { return sService; } //经过AIDL(Binder)通讯拿到NotificationManagerService的服务访问接口 sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification")); return sService; }
synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); long callingId = Binder.clearCallingIdentity(); try { ToastRecord record; int index; //判断是不是系统级别的吐司 if (!isSystemToast) { index = indexOfToastPackageLocked(pkg); } else { index = indexOfToastLocked(pkg, callback); } if (index >= 0) { record = mToastQueue.get(index); record.update(duration); record.update(callback); } else { //建立一个Binder类型的token对象 Binder token = new Binder(); //生成一个Toast窗口,而且传递token等参数 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY); record = new ToastRecord(callingPid, pkg, callback, duration, token); //添加到吐司队列之中 mToastQueue.add(record); //对当前索引从新进行赋值 index = mToastQueue.size() - 1; } //将当前Toast所在的进程设置为前台进程 keepProcessAliveIfNeededLocked(callingPid); if (index == 0) { //若是index为0,说明当前入队的Toast在队头,须要调用showNextToastLocked方法直接显示 showNextToastLocked(); } } finally { Binder.restoreCallingIdentity(callingId); } }
同时,当toast执行show以后,过了一下子会自动销毁,那么这又是为啥呢?那么是哪里调用了hide方法呢?github
回调了Toast的TN的show,当timeout可能就是hide呢。从上面我分析NotificationManagerService源码中的showNextToastLocked()的scheduleTimeoutLocked(record)源码,能够知道在NotificationManagerService经过handler延迟delay时间发送消息,而后经过callback调用hide,因为callback是TN中Binder的代理对象, 因此即可以调用到TN中的hide方法达到销毁吐司的目的。handleHide()源码以下所示面试
public void handleHide() { if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView); if (mView != null) { // note: checking parent() just to make sure the view has // been added... i have seen cases where we get here when // the view isn't yet added, so let's try not to crash. if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeViewImmediate(mView); } mView = null; } }
final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
接着看看isCallerSystem()方法源码,isCallerSystem的源码也比较简单,就是判断当前Toast所属进程的uid是否为SYSTEM_UID、0、PHONE_UID中的一个,若是是,则为系统Toast;若是不是,则不为系统Toast。编程
private static boolean isUidSystem(int uid) { final int appid = UserHandle.getAppId(uid); return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); } private static boolean isCallerSystem() { return isUidSystem(Binder.getCallingUid()); }
具体能够参考个人弹窗封装库:https://github.com/yangchong211/YCDialogsegmentfault
//判断是否有权限 NotificationManagerCompat.from(context).areNotificationsEnabled()
//若是没有通知权限,则直接跳转设置中心设置br/>@SuppressLint("ObsoleteSdkInt")
private static void toSetting(Context context) {
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", context.getPackageName(), null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings",
"com.android.setting.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
}
context.startActivity(localIntent);
}markdown
为了不静态toast对象内存泄漏,固可使用应用级别的上下文context。因此这里我就直接采用了应用级别Application上下文,须要在application进行初始化一下。便可调用……app
//初始化 ToastUtils.init(this); //能够自由设置吐司的背景颜色,默认是纯黑色 ToastUtils.setToastBackColor(this.getResources().getColor(R.color.color_7f000000)); //直接设置最简单吐司,只有吐司内容 ToastUtils.showRoundRectToast("自定义吐司"); //设置吐司标题和内容 ToastUtils.showRoundRectToast("吐司一下","他发的撒经济法的解放军"); //第三种直接设置自定义布局的吐司 ToastUtils.showRoundRectToast(R.layout.view_layout_toast_delete); //或者直接采用bulider模式建立 ToastUtils.Builder builder = new ToastUtils.Builder(this.getApplication()); builder .setDuration(Toast.LENGTH_SHORT) .setFill(false) .setGravity(Gravity.CENTER) .setOffset(0) .setDesc("内容内容") .setTitle("标题") .setTextColor(Color.WHITE) .setBackgroundColor(this.getResources().getColor(R.color.blackText)) .build() .show();
/** * 检查上下文不能为空,必须先进性初始化操做 */ private static void checkContext(){ if(mApp==null){ throw new NullPointerException("ToastUtils context is not null,please first init"); } }
android.view.WindowManager$BadTokenException Unable to add window -- token android.os.BinderProxy@7f652b2 is not valid; is your activity running?
Toast.makeText(this,"潇湘剑雨-yc",Toast.LENGTH_SHORT).show(); try { Thread.sleep(20000); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(new Runnable() { @Override public void run() { ToastUtils.showRoundRectToast("潇湘剑雨-杨充"); } }).start();
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); ToastUtils.showRoundRectToast("潇湘剑雨-杨充"); Looper.loop(); } }).start();