Toast源码分析与学习
Posted 彼岸花you
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Toast源码分析与学习相关的知识,希望对你有一定的参考价值。
知识点补充
android使用注解替代枚举
android 进程间通信使用aidl和Messenger类
源码分析
涉及源码有些长,下面只截取了部分分析,github toast相关源码
① 从Toast.java中的变量定义开始
我们都知道在实际使用中,Toast显示的时间只有两种情况。我们先从源码看看是怎么回事。
static final String TAG = "Toast";
static final boolean localLOGV = false;
/** @hide */
@IntDef(LENGTH_SHORT, LENGTH_LONG)
@Retention(RetentionPolicy.SOURCE)
public @interface Duration
/**
* Show the view or text notification for a short period of time. This time
* could be user-definable. This is the default.
* @see #setDuration
*/
public static final int LENGTH_SHORT = 0;
/**
* Show the view or text notification for a long period of time. This time
* could be user-definable.
* @see #setDuration
*/
public static final int LENGTH_LONG = 1;
final Context mContext;
final TN mTN;
int mDuration;
View mNextView;
这里使用注解的方式规定了Duration的取值,只有两个值,LENGTH_SHORT和LENGTH_LONG。在看一下源码中对Duration的设置
/**
* Set how long to show the view for.
* @see #LENGTH_SHORT
* @see #LENGTH_LONG
*/
public void setDuration(@Duration int duration)
mDuration = duration;
mTN.mDuration = duration;
/**
* Return the duration.
* @see #setDuration
*/
@Duration
public int getDuration()
return mDuration;
明了了吧,这里使用@Duration对传入的参数进行了规定,如果想传入其他的int值,在代码中是会有标记
② 看看Toast.java的构造函数
/**
* Construct an empty Toast object. You must call @link #setView before you
* can call @link #show.
*
* @param context The context to use. Usually your @link android.app.Application
* or @link android.app.Activity object.
*/
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);
由界面传入一个上下文实例context,后面的代码貌似是创建一个TN对象,我们现在来看看TN是什么东西。
③ 查看构造函数中创建的TN具体定义,这是一个内部类
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();
// Don't do this in handleHide() because it is also invoked by handleShow()
mNextView = null;
;
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;
int mDuration;
WindowManager mWM;
static final long SHORT_DURATION_TIMEOUT = 5000;
static final long LONG_DURATION_TIMEOUT = 1000;
TN()
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
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;
/**
* schedule handleShow into the right thread
*/
@Override
public void show()
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
/**
* schedule handleHide into the right thread
*/
@Override
public void hide()
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
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();
String packageName = mView.getContext().getOpPackageName();
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;
mParams.packageName = packageName;
mParams.removeTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
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();
private void trySendAccessibilityEvent()
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
if (!accessibilityManager.isEnabled())
return;
// treat toasts as notifications since they are used to
// announce a transient piece of information to the user
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(getClass().getName());
event.setPackageName(mView.getContext().getPackageName());
mView.dispatchPopulateAccessibilityEvent(event);
accessibilityManager.sendAccessibilityEvent(event);
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.removeView(mView);
mView = null;
这个类继承ITransientNotification.stub,那么这个ITransientNotification是一个aidl文件
ITransientNotification.aidl(@hind)源码
20/** @hide */
21 oneway interface ITransientNotification
22 void show();
23 void hide();
24
上面的TN继承这个类并提供了具体的方法show和hind,在具体的实现中使用Handler来post两个runnable(TN类开头定义的两个Runnable),两个Runnable中分别执行了handleShow()和handleHind()方法,推测是控制Toast显示和隐藏的具体方法。看具体的实现:
在handleShow()方法中,通过获取WindowManager将创建的View add到界面上,在handleHind()方法中获取WindowManager remove掉这个View.
④ 根据Toast.java的构造函数和上面对TN的分析 小结一下。
使用过程中,创建一个toast对象,会实例化一个内部类TN,这个TN类能够控制Toast显示和隐藏(内部由WindowManager add 和remove view来实现显示和隐藏)。
⑤ 构造toast view 之后,toast.show();
/**
* Show the view for the specified duration.
*/
public void show()
if (mNextView == null)
throw new RuntimeException("setView must have been called");
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try
service.enqueueToast(pkg, tn, mDuration);
catch (RemoteException e)
// Empty
private static INotificationManager sService;
static private INotificationManager getService()
if (sService != null)
return sService;
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
又出现了几个不认识的东西,看看具体是什么。
- ServiceManager.java(@hind)
26 /** @hide */
27public final class ServiceManager
28 private static final String TAG = "ServiceManager";
29
30 private static IServiceManager sServiceManager;
31 private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
32
33 private static IServiceManager getIServiceManager()
34 if (sServiceManager != null)
35 return sServiceManager;
36
37
38 // Find the service manager
39 sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
40 return sServiceManager;
41
42
43 /**
44 * Returns a reference to a service with the given name.
45 *
46 * @param name the name of the service to get
47 * @return a reference to the service, or <code>null</code> if the service doesn't exist
48 */
49 public static IBinder getService(String name)
50 try
51 IBinder service = sCache.get(name);
52 if (service != null)
53 return service;
54 else
55 return getIServiceManager().getService(name);
56
57 catch (RemoteException e)
58 Log.e(TAG, "error in getService", e);
59
60 return null;
61
62
63 /**
64 * Place a new @a service called @a name into the service
65 * manager.
66 *
67 * @param name the name of the new service
68 * @param service the service object
69 */
70 public static void addService(String name, IBinder service)
71 try
72 getIServiceManager().addService(name, service, false);
73 catch (RemoteException e)
74 Log.e(TAG, "error in addService", e);
75
76
...
139
ServiceManager详解
文章重点:ServiceManager管理整个系统的Service,实际功能发面包括:
- 提供IBinder对象(在每个进程中,该IBinder对象是唯一的)就好比我获取一个闹钟的service,那在当前这个app进程中这个service就是唯一的。
- 让各个service注册到ServiceManager中。
简单来说ServiceManager提供getIBinder的方法和每个service都会注册到它。
INotificationManager.aidl(@hind)
/** @hide */
36interface INotificationManager
37
38 void cancelAllNotifications(String pkg, int userId);
39
40 void enqueueToast(String pkg, ITransientNotification callback, int duration);
41 void cancelToast(String pkg, ITransientNotification callback);
...
100
ITransientNotification.aidl(@hind)
20/** @hide */
21 oneway interface ITransientNotification
22 void show();
23 void hide();
24
回到Toast.java 源码
我们操作中调用的Toast show方法在源码中的操作其实是获取这个INotificationManager的实例,然后enqueueToast和cancelToast.
源码分析总结
以上是关于Toast源码分析与学习的主要内容,如果未能解决你的问题,请参考以下文章