Toast实现源码解析

news/2025/2/8 18:07:12 标签: 移动开发, 操作系统

说明

本篇文章用于介绍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

转载于:https://www.cnblogs.com/amiko/p/7906219.html


http://www.niftyadmin.cn/n/712250.html

相关文章

java fastjson解析json_java JSON解析库Alibaba Fastjson用法详解

本文实例讲述了java JSON解析库Alibaba Fastjson用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;Json是一种轻量级的数据交换格式&#xff0c;应该在一个程序员的开发生涯中是常接触的。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写…

二元函数泰勒公式例题_高数031泰勒公式求极限 真香!

今天我们来看看泰勒公式的一些综合应用&#xff0c;先来如何利用泰勒公式求极限&#xff0c;有一些极限十分复杂难看&#xff0c;对函数密集恐惧症患者刺激巨大&#xff0c;这时候看似复杂的泰勒展开反而会将其巧妙化简&#xff0c;让问题迎刃而解。并且在原则上&#xff0c;任…

LVS-DR负载均衡-03

LB端脚本 # vim /usr/local/sbin/lvs-dr.sh #!/bin/bash#description : start LVS OF DIRECTORSERVER #GW192.168.28.253#WEBSITE DIRECTOR VIP WEB_VIP192.168.28.111 WEB_RIP1192.168.28.134 WEB_RIP2192.168.28.135 . /etc/rc.d/init.d/functions logger $0 called with $1 …

web前端学习(二十七)——CSS3导航栏的相关设置

1.CSS导航栏 垂直 水平 导航栏 熟练使用导航栏&#xff0c;对于任何网站都非常重要。 使用CSS你可以转换成好看的导航栏而不是枯燥的HTML菜单。 2.导航栏链接列表 作为标准的HTML基础一个导航栏是必须的&#xff0c;在我们的例子中我们将建立一个标准的HTML列表导航栏。 导航条…

ABP官方文档翻译 7.2 Hangfire集成

Hangfire集成 介绍ASP.NET Core集成ASP.NET MVC 5.x集成 面板授权介绍 Hangfire是一个综合的后台job管理器。你可以 把它集成到ABP&#xff0c;用来取代默认的后台job管理器。Hangfire可以使用相同的后台jobAPI。因此&#xff0c;你的代码与Hangfire是独立的。但是&#xff0c;…

vscode还用装git_VScode 搭配Git

VScode搭配Git一、Visual Studio Code安装Windows版下载地址2、安装一般默认下一步即可3、安装完&#xff0c;默认语言为英文&#xff0c;需要安装中文语言&#xff0c;步骤如下(1)点击左侧的小方块(拓展)(2)搜索框输入“Chinese”,点击“Install”(3)稍等一会&#xff0c;软件…

一步一步学习Redis——五大数据类型之哈希(Hash)的相关命令

文章目录&#xff1a; 1.开篇 2.Redis哈希&#xff08;Hash&#xff09;的相关命令 2.1 HSET命令 语法 返回值 2.2 HGET命令 语法 返回值 2.3 HMSET命令 语法 返回值 2.4 HMGET命令 语法 返回值 2.5 HSETNX命令 语法 返回值 2.6 HDEL命令 语法 返回值 …

从java中安装webolgc_Javaweb| 文件下载

后台&#xff1a;Conteoller/*** 下载文件* param fileurl* param session* param response* throws IOException*/RequestMapping("/download")public void getFile(String fileurl, HttpSession session, HttpServletResponse response)throws IOException {Strin…