Android 如何将动态加载的类强制转换到其接口?

Android 如何将动态加载的类强制转换到其接口?,android,dialog,classloader,classcastexception,dynamic-controls,Android,Dialog,Classloader,Classcastexception,Dynamic Controls,我正在创建一个家庭自动化应用程序,允许插件视图 我有以下代码作为概念证明: 我已经创建了一个android库项目,其中包含插件的接口: package com.strutton.android.UIPlugInLib; import android.app.Dialog; public interface IRDroidInterface { public Dialog buildConfigDialog(int ID); // Other method signature

我正在创建一个家庭自动化应用程序,允许插件视图

我有以下代码作为概念证明:

我已经创建了一个android库项目,其中包含插件的接口:

package com.strutton.android.UIPlugInLib;

import android.app.Dialog;

public interface IRDroidInterface  {
    public Dialog buildConfigDialog(int ID);
    // Other method signatures here
}
我已经能够在一个单独的项目中创建一个类作为示例插件(并将其导出到apk),并将其推送到我的应用程序的文件目录:

package com.strutton.android.testloadclass;

import com.strutton.android.UIPlugInLib.IRDroidInterface;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.widget.Button;

public class MyTestClass_IRDroidUIPlugIn extends Button implements IRDroidInterface{
    public final Activity mActivity;
    public MyTestClass_IRDroidUIPlugIn(Activity activity) {
        super((Context)activity);
        mActivity = activity;
        setText("I was loaded dynamically! (1)");
        setOnClickListener(new View.OnClickListener() {  
            public void onClick(View v) {  
                setText("I was Clicked dynamically! (" + getId() +")");
            }}  
                );
    }

    public Dialog buildConfigDialog(int ID){
        AlertDialog.Builder builder = new AlertDialog.Builder((Context)mActivity);
        builder.setMessage("Click the Button...(1)")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    mActivity.dialogDismiss();
               }           
           });
        return builder.create();
    }
}
我可以在运行时加载这个类,并使用以下代码创建它的实例(在我的
onCreate()
中):

package com.strutton.android.testplugin;

        try {
        final File filesDir = this.getFilesDir();
        final File tmpDir = getDir("dex", 0);
        final DexClassLoader classloader = new DexClassLoader( filesDir.getAbsolutePath()+"/testloadclass.apk",
                tmpDir.getAbsolutePath(),
                null, this.getClass().getClassLoader());
        final Class<View> classToLoad = 
                (Class<View>) classloader.loadClass("com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn");
        mybutton = (View) classToLoad.getDeclaredConstructor(Context.class).newInstance(this);
        mybutton.setId(2);
        main.addView((View)mybutton);
      } catch (Exception e) {
        e.printStackTrace();
    }

    setContentView(main);
}
protected Dialog onCreateDialog(int id) {
    switch (id) {
        case 1:
                    // this is the offending line 57
            return ((IRDroidInterface) mybutton).buildConfigDialog(id);
    }
    return null;
}
package com.strutton.android.testplugin;
试一试{
final File filesDir=this.getFilesDir();
最终文件tmpDir=getDir(“dex”,0);
final DexClassLoader classloader=新的DexClassLoader(filesDir.getAbsolutePath()+“/testloadclass.apk”,
tmpDir.getAbsolutePath(),
null,this.getClass().getClassLoader());
最终类classToLoad=
(Class)classloader.loadClass(“com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn”);
mybutton=(视图)classToLoad.getDeclaredConstructor(Context.class).newInstance(此);
mybutton.setId(2);
main.addView((视图)mybutton);
}捕获(例外e){
e、 printStackTrace();
}
setContentView(主);
}
受保护的对话框onCreateDialog(int id){
开关(id){
案例1:
//这是第57行
返回((IRDroidInterface)mybutton).buildConfigDialog(id);
}
返回null;
}
我希望插件能够显示插件中定义的配置对话框。当我调用
buildConfigDialog(id)
时,我得到以下
ClassCastException

04-20 20:49:45.865:W/System.err(354):java.lang.ClassCastException:com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn
04-20 20:49:45.894:W/System.err(354):位于com.strutton.android.testplugin.TestpluginActivity.onCreate(TestpluginActivity.java:57)

我错过了什么?我已经在谷歌上搜索了一天半,找不到解决办法


提前感谢。

我怀疑问题在于您的应用程序和加载的dex文件中都存在IRDroidInterface接口。动态加载MyTestClass_irdroduipLugin类时,由于它来自dex文件,因此它实现了来自dex文件的接口类,而不是来自应用程序的接口类

如果您可以确保接口类不会包含在插件的dex文件中,我相信您所拥有的应该可以正常工作


不幸的是,如果您使用eclipse或ant构建插件apk,这可能会很棘手。一个(相当难看的)解决方案是在构建dex文件后从中删除接口类。也就是说,使用baksmali将其分解,删除接口的.smali文件,然后使用smali重新组装。

您可以使用DexClassLoader的自定义实现来解决此问题。 重写findclass并仅返回应用程序库中的接口,而不是库中的接口

package re.execute;

import dalvik.system.DexClassLoader;

public class DexLoader extends DexClassLoader{
    public DexLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
    super(dexPath, optimizedDirectory, libraryPath, parent);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    if("re.execute.ILoader".equals(name)){
        return ILoader.class;
    }
    return super.findClass(name);
}
}
重新执行包;
导入dalvik.system.DexClassLoader;
公共类DexLoader扩展了DexClassLoader{
公共DexLoader(字符串dexPath、字符串optimizedDirectory、字符串库路径、类装入器父级){
super(dexPath,optimizedDirectory,libraryPath,parent);
}
@凌驾
受保护类findClass(字符串名称)引发ClassNotFoundException{
if(“re.execute.ILoader.equals(name)){
返回ILoader.class;
}
返回super.findClass(名称);
}
}

您正在试用哪一版本的android?我希望在这两个项目中都使用该界面作为库,这样我就可以解决这个问题。我读到的所有关于ClassCastException的内容都告诉我你的权利。你能检查一下你插件的dex文件中是否存在IRDroidInterface类吗?i、 e.使用baksmali进行分解,看看com/strutton/android/UIPlugInLib/IRDroidInterface.smali是否存在。啊哈。我只是在我的一个小测试中实现了这一点(能够直接转换到接口)——如果我确保动态加载的dex文件中不存在接口。好的。。。我不知道你的意思。。。你没有在类def中包含
接口
,或者你是如何做到的?嗯,我有点作弊。我从命令行手动执行所有操作。所以我有一个fooInterface.java文件和一个foo.java文件,并编译了这两个文件。然后在使用dx创建dex文件之前删除fooInterface.class