范型&&诡异异常

来源:互联网 发布:鄂尔多斯网络推广招聘 编辑:程序博客网 时间:2024/06/05 22:01

Looper 中是使用一个ThreadLocal 来存储 当前线程的 looper的,
static final ThreadLocal sThreadLocal = new ThreadLocal();
在prepare 方法中,对此 ThreadLocal 方法赋值:

private static void prepare(boolean quitAllowed) {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    sThreadLocal.set(new Looper(quitAllowed));}

在myLooper 中会取出 此 ThreadLocal 中的 looper 对象:

public static Looper myLooper() {        return sThreadLocal.get();}

但是突然有一天见到一个诡异的异常:

java.lang.ClassCastException: java.lang.StringBuilder cannot be cast to android.os.Looper    at android.os.Looper.myLooper(Looper.java:249)    at android.database.sqlite.SQLiteDatabase.isMainThread(SQLiteDatabase.java:404)    at android.database.sqlite.SQLiteDatabase.getThreadDefaultConnectionFlags(SQLiteDatabase.java:395)    at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:59)    at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)    at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1024)    at com.android.providers.contacts.LegacyApiSupport.<init>(LegacyApiSupport.java:532)    at com.android.providers.contacts.ContactsProvider2.initForDefaultLocale(ContactsProvider2.java:1930)    at com.android.providers.contacts.ContactsProvider2.performBackgroundTask(ContactsProvider2.java:2055)    at com.android.providers.contacts.ContactsProvider2$1.handleMessage(ContactsProvider2.java:12238)    at android.os.Handler.dispatchMessage(Handler.java:111)    at android.os.Looper.loop(Looper.java:207)    at android.os.HandlerThread.run(HandlerThread.java:61)

ThreadLocal 里面被存入了一个 StringBuilder ,然后在myLooper 中取出时发生了异常,先不说是谁能干出这档子事,先探究一下他是怎么存进去的:
范型是 存在于代码编译期,在生成字节码时,编译器会将范型去除,做类型擦除,
所以在ThreadLocal 中存入一个StringBuilder 只有可能在运行时完成,我所知道,能在运行时完成这件事的只有 反射了:

Field field = Looper.class.getDeclaredField("sThreadLocal");field.setAccessible(true);ThreadLocal sThreadLocal = ThreadLocal.class.cast(field.get(null));Method method = ThreadLocal.class.getMethod("set", Object.class);method.invoke(sThreadLocal, new StringBuilder());

如果使用这种方法,一般也不需要处理,可是本次发生的问题,是属于系统级apk的,并且系统源码里面没有找到此类的调用,哎,很是 奇怪啊,最后迫于无奈,在ThreadLocal 类的 set 方法里面添加了一下类型判断:

public void set(T value) {    T oldValue = get();    if(oldValue != null && value != null && !((oldValue.getClass()).equals(value.getClass()))){        throw new RuntimeException();    }    Thread currentThread = Thread.currentThread();    Values values = values(currentThread);    if (values == null) {       values = initializeValues(currentThread);    }    values.put(this, value);}

以此来规避,反射调用此方法,产生的异常。