mirror of
https://github.com/Tencent/QMUI_Android.git
synced 2026-10-26 17:59:34 +08:00
reflect helper and toast fix from booster.
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making QMUI_Android available.
|
||||
*
|
||||
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.qmuiteam.qmui.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Modify from https://github.com/didi/booster/blob/master/booster-android-instrument/src/main/java/com/didiglobal/booster/instrument/Reflection.java
|
||||
public class QMUIReflectHelper {
|
||||
private static final String TAG = "QMUIReflectHelper";
|
||||
|
||||
private QMUIReflectHelper() {
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getStaticFieldValue(final Class<?> cls, final String name) {
|
||||
if (null != cls && null != name) {
|
||||
try {
|
||||
final Field field = getField(cls, name);
|
||||
if (null != field) {
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(cls);
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "get static field " + name + " of " + cls + " error", t);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean setStaticFieldValue(final Class<?> cls, final String name, final Object value) {
|
||||
if (null != cls && null != name) {
|
||||
try {
|
||||
final Field field = getField(cls, name);
|
||||
if (null != field) {
|
||||
field.setAccessible(true);
|
||||
field.set(cls, value);
|
||||
return true;
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "set static field " + name + " of " + cls + " error", t);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getFieldValue(final Object obj, final String name) {
|
||||
if (null != obj && null != name) {
|
||||
try {
|
||||
final Field field = getField(obj.getClass(), name);
|
||||
if (null != field) {
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(obj);
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "get field " + name + " of " + obj + " error", t);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getFieldValue(final Object obj, final Class<?> type) {
|
||||
if (null != obj && null != type) {
|
||||
try {
|
||||
final Field field = getField(obj.getClass(), type);
|
||||
if (null != field) {
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(obj);
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "get field with type " + type + " of " + obj + " error", t);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean setFieldValue(final Object obj, final String name, final Object value) {
|
||||
if (null != obj && null != name) {
|
||||
try {
|
||||
final Field field = getField(obj.getClass(), name);
|
||||
if (null != field) {
|
||||
field.setAccessible(true);
|
||||
field.set(obj, value);
|
||||
return true;
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "set field " + name + " of " + obj + " error", t);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> T newInstance(final String className, final Object... args) {
|
||||
try {
|
||||
return newInstance(Class.forName(className), args);
|
||||
} catch (final ClassNotFoundException e) {
|
||||
Log.w(TAG, "new instance of " + className + " error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T newInstance(final Class<?> clazz, Object... args) {
|
||||
final Constructor<?>[] ctors = clazz.getDeclaredConstructors();
|
||||
|
||||
loop:
|
||||
for (final Constructor<?> ctor : ctors) {
|
||||
final Class<?>[] types = ctor.getParameterTypes();
|
||||
if (types.length == args.length) {
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (null != args[i] && !types[i].isAssignableFrom(args[i].getClass())) {
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ctor.setAccessible(true);
|
||||
return (T) ctor.newInstance(args);
|
||||
} catch (final Throwable t) {
|
||||
Log.w(TAG, "Invoke constructor " + ctor + " error", t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T invokeStaticMethod(final Class<?> klass, final String name) {
|
||||
return invokeStaticMethod(klass, name, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T invokeStaticMethod(final Class<?> klass, final String name, final Class[] types, final Object[] args) {
|
||||
if (null != klass && null != name && null != types && null != args && types.length == args.length) {
|
||||
try {
|
||||
final Method method = getMethod(klass, name, types);
|
||||
if (null != method) {
|
||||
method.setAccessible(true);
|
||||
return (T) method.invoke(klass, args);
|
||||
}
|
||||
} catch (final Throwable e) {
|
||||
Log.w(TAG, "Invoke " + name + "(" + Arrays.toString(types) + ") of " + klass + " error", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T invokeMethod(final Object obj, final String name) {
|
||||
return invokeMethod(obj, name, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T invokeMethod(final Object obj, final String name, final Class[] types, final Object[] args) {
|
||||
if (null != obj && null != name && null != types && null != args && types.length == args.length) {
|
||||
try {
|
||||
final Method method = getMethod(obj.getClass(), name, types);
|
||||
if (null != method) {
|
||||
method.setAccessible(true);
|
||||
return (T) method.invoke(obj, args);
|
||||
}
|
||||
} catch (final Throwable e) {
|
||||
Log.w(TAG, "Invoke " + name + "(" + Arrays.toString(types) + ") of " + obj + " error", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Field getField(final Class<?> cls, final String name) {
|
||||
try {
|
||||
return cls.getDeclaredField(name);
|
||||
} catch (final NoSuchFieldException e) {
|
||||
final Class<?> parent = cls.getSuperclass();
|
||||
if (null == parent) {
|
||||
return null;
|
||||
}
|
||||
return getField(parent, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static Field getField(final Class<?> cls, final Class<?> type) {
|
||||
final Field[] fields = cls.getDeclaredFields();
|
||||
if (fields.length <= 0) {
|
||||
final Class<?> parent = cls.getSuperclass();
|
||||
if (null == parent) {
|
||||
return null;
|
||||
}
|
||||
return getField(parent, type);
|
||||
}
|
||||
|
||||
for (final Field field : fields) {
|
||||
if (field.getType() == type) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Method getMethod(final Class<?> cls, final String name, final Class<?>[] types) {
|
||||
try {
|
||||
return cls.getDeclaredMethod(name, types);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
final Class<?> parent = cls.getSuperclass();
|
||||
if (null == parent) {
|
||||
return null;
|
||||
}
|
||||
return getMethod(parent, name, types);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making QMUI_Android available.
|
||||
*
|
||||
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.qmuiteam.qmui.util;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
// Modify from https://github.com/didi/booster/blob/master/booster-android-instrument-toast/src/main/java/com/didiglobal/booster/instrument/ShadowToast.java
|
||||
public class QMUIToastHelper {
|
||||
private static final String TAG = "QMUIToastHelper";
|
||||
public static void show(Toast toast){
|
||||
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1){
|
||||
fixToastForAndroidN(toast).show();
|
||||
}else{
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
|
||||
private static Toast fixToastForAndroidN(Toast toast){
|
||||
Object tn = QMUIReflectHelper.getFieldValue(toast, "mTN");
|
||||
if(tn == null){
|
||||
Log.w(TAG, "The value of field mTN of " + toast + " is null");
|
||||
return toast;
|
||||
}
|
||||
Object handler = QMUIReflectHelper.getFieldValue(tn, "mHandler");
|
||||
if(handler instanceof Handler){
|
||||
if(QMUIReflectHelper.setFieldValue(
|
||||
handler, "mCallback", new FixCallback((Handler) handler))){
|
||||
return toast;
|
||||
}
|
||||
}
|
||||
|
||||
final Object show = QMUIReflectHelper.getFieldValue(tn, "mShow");
|
||||
if (show instanceof Runnable) {
|
||||
if (QMUIReflectHelper.setFieldValue(tn, "mShow", new FixRunnable((Runnable) show))) {
|
||||
return toast;
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "Neither field mHandler nor mShow of " + tn + " is accessible");
|
||||
return toast;
|
||||
}
|
||||
|
||||
public static class FixCallback implements Handler.Callback {
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
public FixCallback(final Handler handler) {
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(@NonNull Message msg) {
|
||||
try {
|
||||
mHandler.handleMessage(msg);
|
||||
} catch (Throwable e) {
|
||||
// ignore
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FixRunnable implements Runnable {
|
||||
|
||||
private final Runnable mRunnable;
|
||||
|
||||
public FixRunnable(final Runnable runnable) {
|
||||
mRunnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mRunnable.run();
|
||||
} catch (final RuntimeException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user