Using reflection mechanism to control the display time of Toast

  • 2021-10-13 08:42:18
  • OfStack

In this paper, we share the specific code of using reflection mechanism to control the display time of Toast for your reference, the specific contents are as follows

1. Toast source code analysis:

The default view of Toast is one TextView defined in transient_notification. xml. If the interface of Toast needs to be set, it can be realized by setView method; If you need to set the default display position of Toast, you can set it by setGravity or setMargin method. It is worth mentioning that the parameter range of setMargin method is 0-1, that is, it is the percentage of screen, such as setMargin (0.1, 0.1).

The constructor of Toast instantiates the TN object, and the TN object has two methods, show and hide, which can control the display and disappearance of Toast. So what completes the call and when do you call these two methods? We can find the answer from the show method of Toast:


public void show() {
    if (mNextView == null) {
      throw new RuntimeException("setView must have been called");
    }
 
    INotificationManager service = getService();
 
    String pkg = mContext.getPackageName();
 
    TN tn = mTN;
 
    try {
      service.enqueueToast(pkg, tn, mDuration);
    } catch (RemoteException e) {
      // Empty
    }
  }

The method, firstly, judges whether the view object to be displayed is null;; Then get the object of the singleton INotificationManager, and finally add the request of the current Toast to the display queue, and at the same time transfer the time that the Toast needs to be displayed. The method of enqueueToast is as follows:


public void enqueueToast(java.lang.String pkg, android.app.ITransientNotification callback, int duration) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(pkg);
_data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
_data.writeInt(duration);
mRemote.transact(Stub.TRANSACTION_enqueueToast, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}

As you can see, this method invokes the underlying code through the transact method, which uses the mechanism of binder. service eventually invokes show and hide methods of TN based on duration to show and hide the Toast interface.

2. How to modify the display time of Toast

Toast has two display times by default, LENGTH_SHORT and LENGTH_LONG. If we need Toast display time less than LENGTH_SHORT, then it is very easy to implement, just call cancel method of Toast directly. So if we want Toast to display longer than LENGTH_LONG, there is no way to do it, because it is not up to you to call hide method of TN.

Below, through the reflection mechanism of Java, get show and hide methods of TN, and control the display and hiding of Toast by yourself.


public class ReflectToast {
  Context mContext;
 
  private Toast mToast;
  private Field field;
  private Object obj;
  private Method showMethod, hideMethod;
 
  public ReflectToast(Context c, View v) {
    this.mContext = c;
    mToast = new Toast(mContext);
    mToast.setView(v);
 
    reflectionTN();
  }
 
  public void show() {
    try {
      showMethod.invoke(obj, null);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  public void cancel() {
    try {
      hideMethod.invoke(obj, null);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  private void reflectionTN() {
    try {
      field = mToast.getClass().getDeclaredField("mTN");
      field.setAccessible(true);
      obj = field.get(mToast);
      showMethod = obj.getClass().getDeclaredMethod("show", null);
      hideMethod = obj.getClass().getDeclaredMethod("hide", null);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

Externally, we can call show and cancel methods of ReflectToast to control the display and hiding of Toast.

In order to prevent everyone from making mistakes again, the calling code is given as follows:


public class MainActivity extends Activity {
  ReflectToast toast;
  boolean isShown = false;
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final TextView tView = new TextView(this);
    tView.setText("ReflectToast !!!");
    toast = new ReflectToast(this, tView);
    
    findViewById(R.id.click).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        if(isShown){
          toast.cancel();
          isShown = false;
        }else{ 
          toast.show();
          isShown = true;
        }
      }
    });
  }
}


Related articles: