我正在尝试开发支持动态活动的Android应用程序。
由于所有活动都需要在Manifest XML中声明,因此我创建了一个" dummy"没有任何作用的活动。
<activity
android:name="com.research.Dummy"
android:label="@string/title_activity_dummy"
android:theme="@style/AppTheme.NoActionBar" />
我希望实现的是使用ByteBuddy我实例化一个新版本的Dummy,它扩展了不同的超类和/或实现了一个或多个接口。
我有这段代码来测试扩展超类: -
final Class<? extends SomeClass> dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V8) .subclass(com.pspdfkit.ui.PdfActivity.class, IMITATE_SUPER_CLASS)
.name("com.research.Dummy")
.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("Hello World!"))
.make()
.load(getClass().getClassLoader(), new AndroidClassLoadingStrategy.Wrapping(this.getDir("dexgen", Context.MODE_PRIVATE))).getLoaded();
final Intent intent = new Intent(this, dynamicType);
startActivity(intent);
我的Dummy
活动延伸android.support.v7.app.AppCompatActivity
,但是,我希望我的ByteBuddy版本能够扩展&#34; SomeClass
&#34;。
上面的代码并没有给我带来理想的效果,Dummy活动会显示但是它会扩展android.support.v7.app.AppCompatActivity
而不是SomeClass。
ByteBuddy可以使用扩展Dummy
所需的版本替换原AppCompatActivity
个SomeClass
类{/ 1}}吗?
更新
当我尝试注入新课程时: -
final File jarFile = new File(getFilesDir(), "buddyDummy.jar");
final DynamicType.Unloaded<? extends AppCompatActivity> dynamicType = new ByteBuddy()
.subclass(AppCompatActivity.class)
.name("com.research.buddy.Dynamic")
.make();
final File dexInternalStoragePath = dynamicType.toJar(jarFile);
// Internal storage where the DexClassLoader writes the optimized dex file to.
final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
// Initialize the class loader with the secondary dex file.
final DexClassLoader dexClassLoader = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(), optimizedDexOutputPath.getAbsolutePath(),null, getClassLoader());
dexClassLoader.loadClass("com.research.buddy.Dynamic");
我收到此异常
java.lang.ClassNotFoundException: Didn't find class "com.research.buddy.Dynamic" on path:
DexPathList[[zip file "/data/user/0/com.research.buddy/files/buddyDummy.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at com.research.buddy.Buddy.findRuntimeDependencies(Buddy.java:117)
at com.research.buddy.Buddy.onClick(Buddy.java:78)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21153)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Suppressed: java.io.IOException: No original dex files found for dex location /data/user/0/com.research.buddy/files/buddyDummy.jar
at dalvik.system.DexFile.openDexFileNative(Native Method)
at dalvik.system.DexFile.openDexFile(DexFile.java:295)
at dalvik.system.DexFile.<init>(DexFile.java:111)
at dalvik.system.DexFile.loadDex(DexFile.java:151)
at dalvik.system.DexPathList.loadDexFile(DexPathList.java:282)
at dalvik.system.DexPathList.makePathElements(DexPathList.java:248)
at dalvik.system.DexPathList.<init>(DexPathList.java:120)
at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
at dalvik.system.DexClassLoader.<init>(DexClassLoader.java:57)
at com.research.buddy.Buddy.findRuntimeDependencies(Buddy.java:116)
... 10 more
Suppressed: java.lang.ClassNotFoundException: Didn't find class "com.research.buddy.Dynamic" on path:
DexPathList[[zip file "/data/app/com.research.buddy-1/base.apk"],nativeLibraryDirectories=[/data/app/com.research.buddy-1/lib/arm, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 12 more
Suppressed: java.lang.ClassNotFoundException: com.research.buddy.Dynamic
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 13 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
当我查看设备上的文件位置时,我可以在正确的位置看到buddyDummy.jar
文件。
为什么我会收到ClassNotFoundException
?
更新
我已经设法用ByteBuddy和&#34; startActivity&#34;来实例化我的Android活动。它带有以下代码。
final DynamicType.Unloaded<? extends AppCompatActivity> dynamicType = new ByteBuddy()
.subclass(AppCompatActivity.class)
.name(CLASS_NAME)
.make();
final Class<? extends AppCompatActivity> dynamicTypeClass = dynamicType.load(getClassLoader(), new AndroidClassLoadingStrategy.Injecting(this.getDir("dexgen", Context.MODE_PRIVATE))).getLoaded();
final Intent intent = new Intent(this, dynamicTypeClass);
startActivity(intent);
我现在需要拦截onCreate
方法以允许我设置content
。
这一步似乎有很多问题,即onCreate
是一个受保护的方法,我需要调用超类onCreate方法传递Bundle。
是否可以使用ByteBuddy拦截受保护的方法? 如何在我的方法拦截器中调用super.onCreate(savedInstance)?
@ Super,@ SuperCall等只有call(),例如没有args,我错过了什么?
更新
我成功地显示了我的动态活动,设置了所需的布局并调用了super.onCreate()。虽然我认为超级电话是不按顺序的。
final DynamicType.Unloaded<? extends AppCompatActivity> dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V8)
.subclass(AppCompatActivity.class)
.name(CLASS_NAME)
.method(named("onCreate").and(takesArguments(1)))
.intercept(MethodDelegation.to(TargetActivity.class).andThen(SuperMethodCall.INSTANCE))
.make();
final Class<? extends AppCompatActivity> dynamicTypeClass = dynamicType.load(getClassLoader(), new AndroidClassLoadingStrategy.Injecting(this.getDir("dexgen", Context.MODE_PRIVATE))).getLoaded();
final Intent intent = new Intent(this, dynamicTypeClass);
startActivity(intent);
我的TargetActivity类似于: -
public class TargetActivity {
public static void intercept(Bundle savedInstanceState, @This AppCompatActivity thiz) {
thiz.setContentView(R.layout.activity_fourth);
}
}
因为我的MethodDelegation调用SuperMethod是&#34;。然后&#34;它让我觉得在设置内容后调用了super.onCreate()
,如何调用超级.previous
而不是.andThen
?
答案 0 :(得分:2)
您正在使用Wrapping
策略将类加载到新的类加载器中。如果你想加载一个类而不是另一个类,你必须注入它。
然而,这个模式可能很尴尬,因为你假设你在加载原始类之前注入了类,这通常依赖于执行JVM的细节。
至于你的问题更新:你是否指定了正确的父类加载器?而不是getClass().getClassLoader()
尝试com.research.buddy.Dynamic.class.getClassLoader()
。您似乎正在将类加载到错误的范围内。