Toastはブスすぎて、あちこちに散らばって統一的に変更できないので、クラスローダを使ってToastを動的に置き換えてみてください.class
6366 ワード
Androidのヒントは主にToast.makeText().showは、便利で速いので、ダイアログボックスをポップアップする必要がある場所で直接このような場合が多いです.しかし、その後、プロジェクトスタイルが改版され、Toastの提示方法が要求に合わないことが分かった.このときの通常のやり方は、それぞれの場所を置き換えることですが、コードがそれぞれの場所に散らばっているため、修正が複雑すぎます.今日はもう一つの実現を実現し、Toastだけでなく、この考え方は、グローバルクローズLogなど、他の面に拡張することができます.
まずAndroidのクラスローディングメカニズムを説明し,Javaとほぼ一致し,親委任を採用した.サブクラスのロードタスクは、親に渡され、親が見つからない場合は、サブクラスからロードされます.
以上がクラスローダの詳細です.サブクラスは親の参照を持ち、リクエストを受け取ると親によってロードされます.親が自分のfindClassを呼び出してロードされない場合は、親によってロードされます.だから自分で定義したclassLoaderをシステムclassLoaderの前に挿入すれば、私はすべてのロードをブロックすることができて、もちろん途中でToastを置き換えることができます.
コードを注入し、カスタムクラスローダHoldClassLoaderをユーザークラスローダの後ろに挿入します.通常、各アプリケーションは少なくとも3層クラスローダを包みます.よく知られている熱修復とInstant Runは自分でクラスローダを挿入しています.HoldClassLoaderを見てみましょう
論理は簡単で、ロードがToastであることを発見したとき.classは事前にロードしたSuperToast.をclassは戻ります.これにより、私たちが使っているすべてのToastがSuperToastになり、本当にToastを使用する必要があるときにエラーが発生します.だからToastをSignToastに変える必要があります.これでToastは
SuperToast,SignToastがToastになりました.なんとすばらしいことか!!!
SuperToastの方法は以下の通りです.
makeTextもこのように書くしかありません.そうしないと方法が見つかりません.
SignToastはToastに転向するので、最後に戻ったのはToastで、外部が表示しようとすると必ずshowメソッドが呼び出されます.このときの書き方は,インタフェースを介して転送し,具体的に実現する.デフォルト
AlertDialogBehariorは、次のような外部露出を通じて拡張することができます.
Molinaクラスは、外部で使用されているクラスで、機能が露出しています.アプリケーションでinjectionメソッド注入をonCreateで呼び出します.ReaplaceToastBehaviorによってToastの具体的な実装を置き換えます.具体的には次のように使用します.
まず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をユーザークラスローダの後ろに挿入します.通常、各アプリケーションは少なくとも3層クラスローダを包みます.よく知られている熱修復と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);
}
}
論理は簡単で、ロードがToastであることを発見したとき.classは事前にロードしたSuperToast.をclassは戻ります.これにより、私たちが使っているすべてのToastがSuperToastになり、本当にToastを使用する必要があるときにエラーが発生します.だからToastをSignToastに変える必要があります.これでToastは
SuperToast,SignToastがToastになりました.なんとすばらしいことか!!!
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;
}
}
SuperToastの方法は以下の通りです.
makeTextもこのように書くしかありません.そうしないと方法が見つかりません.
SignToastはToastに転向するので、最後に戻ったのはToastで、外部が表示しようとすると必ずshowメソッドが呼び出されます.このときの書き方は,インタフェースを介して転送し,具体的に実現する.デフォルト
AlertDialogBehariorは、次のような外部露出を通じて拡張することができます.
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クラスは、外部で使用されているクラスで、機能が露出しています.アプリケーションでinjectionメソッド注入をonCreateで呼び出します.ReaplaceToastBehaviorによって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();
}
}