「Android Tips」Toast 的一些使用技巧

96
_Ryeeeeee
2015.07.22 23:59* 字数 267

改变 Toast 的位置


Toast toast = Toast.makeText(this, "Gravity", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.BOTTOM|Gravity.RIGHT, 0, 0);
toast.show();

自定义 Toast 的布局


LayoutInflater inflater = getLayoutInflater();
# 解析自定义布局,父布局传入肯
View layout = inflater.inflate(R.layout.toast_layout, (ViewGroup) findViewById(R.id.toast_layout_root));
# 填充信息,比如设置文字,图片等
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText("This is a custom Toast.");
# 显示 Toast
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();

避免内存泄露


下列这段简单的代码,居然可能会出现内存泄露!

Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();

原因在于:如果在 Toast 消失之前,Toast 持有了当前 Activity,而此时,用户点击了返回键,导致 Activity 无法被 GC 销毁, 这个 Activity 就引起了内存泄露.

一个很简单的解决方法:所有只要和布局无关的 Context 都可以传入 ApplicationContext, 此避免内存泄露的方法同样试用于其他需要传入 Context 的地方。就像这样:

Toast toast = Toast.makeText(getApplicationContext(), "Gravity", Toast.LENGTH_SHORT);

显示时间只有 LENGTH_SHORT, LENGTH_LONG 可以选择


frameworks/base/services/java/com/android/server/NotificationManagerService.java 查看源码可以发现,没有其他显示时间长度可以选择。

private static final int LONG_DELAY = 3500; // 3.5 seconds
private static final int SHORT_DELAY = 2000; // 2 seconds
private void scheduleTimeoutLocked(ToastRecord r) {
    mHandler.removeCallbacksAndMessages(r);
    Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
    long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
    mHandler.sendMessageDelayed(m, delay);
}

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
    }
}

普通应用的 Toast 显示数量是有限制的


查看
frameworks/base/services/java/com/android/server/NotificationManagerService.java 中的入队方法
enqueueToast 发现非系统应用是有最大总数限制的,而系统应用没有

// 非系统Toast,每个pkg在当前mToastQueue中Toast有总数限制,不能超过MAX_PACKAGE_NOTIFICATIONS
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;
            }
         }
     }
 }