Toast太丑,散落到各处无法统一更改,试试使用类加载器动态替换Toast.class
来源:互联网 发布:团队办公软件 编辑:程序博客网 时间:2024/04/30 13:22
Android的提示主要使用Toast.makeText().show,方便又快捷,所以大多数时候我们都是在需要弹出对话框的地方直接这样时候。不过后来项目样式改版,发现Toast的提示方式不符合要求了。这个时候通常的做法,是对每一个地方进行替换,不过因为代码散布到各个地方,修改起来太复杂。今天实现另外一种实现,不仅仅可以用于Toast,这个一种思路,可以扩展到其他方面,比如全局关闭Log等。
首先讲解下Android的类加载机制,大体和Java保持一致,采用双亲委派。子类的加载任务先交给父类,父类找不到,再由子类加载。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats } } return c; }
以上是类加载器加载的具体细节,子类持有父类的引用,拿到请求后由父类进行加载,如果父类找不到调用自己的findClass进行加载。所以如果可以将自己定义的classLoader,插到系统classLoader的前面,我就可以拦截掉所有的加载,当然就可以中途将Toast给替换掉。
public class ToastInjection implements IInjection { @Override public void injection(ClassLoader classLoader) { ClassLoader userClassLoader = getUserClassLoader(classLoader); if (userClassLoader == null) { return; } HoldClassLoader holdClassLoader = new HoldClassLoader(); replaceParentClassLoader(userClassLoader, holdClassLoader); } private ClassLoader getUserClassLoader(ClassLoader classLoader) { ClassLoader parent = classLoader.getParent(); while (parent != null && parent != ClassLoader.getSystemClassLoader().getParent()) { ClassLoader superParent = parent.getParent(); if (superParent == ClassLoader.getSystemClassLoader().getParent()) { return parent; } parent = superParent; } if (parent == ClassLoader.getSystemClassLoader().getParent()) { return classLoader; } return null; } private void replaceParentClassLoader(ClassLoader mine, ClassLoader parent) { try { Field field = ClassLoader.class.getDeclaredField("parent"); field.setAccessible(true); field.set(mine, parent); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }}
注入代码,将自定义类加载器HoldClassLoader插到用户类加载器的后面,通常来说每个应用都至少会包裹三层类加载器,大家熟悉的热修复和Instant Run都是自己插入了一层类加载器。现在看下HoldClassLoader:
class HoldClassLoader extends ClassLoader { public HoldClassLoader() { super(ClassLoader.getSystemClassLoader().getParent()); } private static Class hodeToastClass = SuperToast.class; @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if(name.equals("android.widget.Toast")){ return hodeToastClass; } if(name.equals("com.focustech.supertoast.SignToast")){ name = "android.widget.Toast"; } return super.loadClass(name, resolve); }}
class SuperToast { private static IToastBehavior toastBehavior = new AlertDialogBeharior(); static void setToastBehavior(IToastBehavior toastBehavior) { SuperToast.toastBehavior = toastBehavior; } public static Toast makeText(final Context context, final CharSequence text, final int duration) { SignToast aaaToast = new SignToast(context) { @Override public void show() { toastBehavior.show(context,text,duration); } }; return aaaToast; }}
public class Molina { private static Molina molina = new Molina(); private IInjection injection; private Molina(){ injection = new ToastInjection(); } public static void injection(ClassLoader classLoader){ molina.injection.injection(classLoader); } public static void replaceToastBehavior(IToastBehavior behavior){ SuperToast.setToastBehavior(behavior); }}
Molina 类,为外部使用的类,功能通过它暴露出去。在Application中onCreate中调用injection方法注入。通过replaceToastBehavior将Toast的具体实现替换。具体使用如下:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Molina.injection(getClassLoader());Molina.replaceToastBehavior(new IToastBehavior() { @Override public void show(Context context, CharSequence text, int duration) { Log.i("toast",text.toString()); } }); }}public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onClick(View view){ Toast toast = Toast.makeText(this, "asdasd", Toast.LENGTH_SHORT); toast.show(); }}
0 0
- Toast太丑,散落到各处无法统一更改,试试使用类加载器动态替换Toast.class
- Android中snackBar的使用【替换Toast】
- 使用Toast
- Toast使用
- toast使用
- Toast使用
- 使用Toast
- Toast使用
- Toast
- Toast
- Toast
- Toast
- Toast
- Toast
- toast
- Toast
- Toast
- toast
- 【转】M层SP与IO操作优化方案
- 1013. Battle Over Cities (25)
- Base64编码规则
- 计算几何模板
- 文章标题 CSU 1831: Found(矩阵快速幂)
- Toast太丑,散落到各处无法统一更改,试试使用类加载器动态替换Toast.class
- HTML总体知识摘要
- .net调用http服务
- Windows has trigger a breakpoint
- Mysql启动不了?
- MQTT服务器搭建--Apollo
- Maven assembly实现自定义打包
- Shell Extensions -Context Menu(添加右键菜单项)
- MySQL中的分组聚合查询