对android原生系统中的Settings模块所有的Toast进行统一替换
反射基础(用于获取所需方法及字段,并替换使用我们的代理类)
反射代码块速查
Hook(动态代理)
java动态代理实现与原理详细分析
0.查看android原生Toast代码,内部的sService可以作为hook点
Toast代码代码如下:
public class Toast { ... //HOOK替换的对象 private static INotificationManager sService; static private INotificationManager getService() { if (sService != null) { return sService; } sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification")); return sService; } ... /** * 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 } } }自定义代理类
生成代理类,并且拦截enqueueToast方法,对入参进行修改后再传入目标的原生的方法。
public class ToastProxy implements InvocationHandler { private static final String TAG = "ToastProxy"; private Object mService; private Context mContext; public Object newProxyInstance(Context context, Object sService) { this.mService = sService; this.mContext = context; return Proxy.newProxyInstance(sService.getClass().getClassLoader(), sService.getClass().getInterfaces(), this); } @Override public Object invoke(Object arg0, Method method, Object[] args) throws Throwable { Log.i(TAG, "invoke: method == " + method.getName()); if ("enqueueToast".equals(method.getName())) { if (args != null && args.length > 0) { Field mNextView = args[1].getClass().getDeclaredField("mNextView"); mNextView.setAccessible(true); View mNextView = (View) mNextViewClazz.get(args[1]); if (mNextView != null && mNextView instanceof LinearLayout){ //获取原Toast的文字 String nextString = ((TextView) ((LinearLayout) mNextView).getChildAt(0)).getText().toString(); //构造所需变量 TextView value = new TextView(mContext); value.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); value.setBackgroundColor(Color.RED); value.setText(nextString); mNextViewClazz.set(args[1], value); } } } return method.invoke(mService, args); } }全局替换吐司。
反射调用getService生成sService静态对象,用Hook对象实例为参数,生成一个代理类的实例,用代理类的实例,替换第一步拿到的sService对象。
private void setToast() { try { // 获得HOOK点 Toast toast = new Toast(this); Method getService = toast.getClass().getDeclaredMethod("getService"); getService.setAccessible(true); final Object sService = getService.invoke(toast); // 生成代理对象 ToastProxy toastProxy = new ToastProxy(); Object proxyNotiMng = toastProxy.newProxyInstance(this,sService); // 替换 sService Field sServiceField = Toast.class.getDeclaredField("sService"); sServiceField.setAccessible(true); sServiceField.set(sService, proxyNotiMng); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } }