Android使用类反射实现无Context调用Toast的AndHook场景问题求解
解决方案
原有代码错误原因
你编写的Class.forName("com.example.activity.MainActivity").this属于语法错误:
Class.forName()返回的是类的描述对象Class<?>,不是MainActivity的运行时实例类名.this是Java内部类引用外部类实例的专属语法,不能直接作用于反射拿到的Class对象,因此会直接编译失败
方案1:使用全局Application Context(优先推荐)
这个方案不需要依赖任何业务侧的Activity类,通用性最强,也不会出现内存泄漏风险,核心通过系统API反射拿到全局应用上下文:
第一步:新增获取全局Context的工具方法
private static Context getApplicationContext() throws Exception { Class<?> activityThreadCls = Class.forName("android.app.ActivityThread"); // 获取当前ActivityThread实例 Object activityThread = activityThreadCls.getDeclaredMethod("currentActivityThread").invoke(null); // 获取Application实例,本身就是Context子类 return (Context) activityThreadCls.getDeclaredMethod("getApplication").invoke(activityThread); }
第二步:修改Hook逻辑,注意Toast要在主线程执行
@HookHelper.Hook(clazz = BitmapFactory.class) private static Bitmap decodeFile(BitmapFactory thiz, String path) { try { Context appContext = getApplicationContext(); // 切主线程弹Toast,避免子线程调用时报错 appContext.getMainLooper().runWithScissors(() -> Toast.makeText(appContext, "Bitmap.decodeFile(" + path + ")", Toast.LENGTH_LONG).show(), 0); } catch (Exception e) { // 异常捕获避免影响原方法执行 e.printStackTrace(); } return (Bitmap) HookHelper.invokeObjectOrigin(thiz, path); }
方案2:缓存Activity实例(仅在需要Activity上下文时使用)
如果你必须使用Activity作为Context,可以提前Hook目标Activity的生命周期方法缓存实例,注意用弱引用避免内存泄漏:
第一步:定义弱引用存储Activity实例
private static WeakReference<Activity> sCachedActivity = new WeakReference<>(null);
第二步:Hook MainActivity的onCreate方法缓存实例
@HookHelper.Hook(clazz = "com.example.activity.MainActivity") private static void onCreate(Activity thiz, Bundle savedInstanceState) { sCachedActivity = new WeakReference<>(thiz); // 调用原onCreate逻辑,不能省略 HookHelper.invokeVoidOrigin(thiz, savedInstanceState); }
第三步:在decodeFile中使用缓存的Activity
@HookHelper.Hook(clazz = BitmapFactory.class) private static Bitmap decodeFile(BitmapFactory thiz, String path) { Activity targetActivity = sCachedActivity.get(); if (targetActivity != null) { targetActivity.runOnUiThread(() -> Toast.makeText(targetActivity, "Bitmap.decodeFile(" + path + ")", Toast.LENGTH_LONG).show() ); } return (Bitmap) HookHelper.invokeObjectOrigin(thiz, path); }
该方案缺点是如果目标Activity被销毁,缓存会为空导致无法弹出Toast,非必要场景优先选择方案1。
内容的提问来源于stack exchange,提问作者Amin Guermazi
相关产品推荐
相关产品推荐

