3

私は、プラグインビューを許可するホームオートメーションアプリケーションを作成しています。動的にロードされたクラスをそのインターフェイスにキャストするにはどうすればよいですか?

私はコンセプトの証明として、次のコードを持っている:

私はプラグイン用のインターフェイスを持つアンドロイドのライブラリプロジェクトを作成しました:

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()で)T:

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; 
} 

私はプラグインがプラグインで定義された設定ダイアログを表示できるようにしたいです。私は、次のClassCastExceptionを得るbuildConfigDialog(id)を呼び出すとき:

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): at com.strutton.android.testplugin.TestpluginActivity.onCreate(TestpluginActivity.java:57)

私はここで何をしないのですか?私は今一日半のグーグルで解決策を見つけることができません。

ありがとうございます。

+0

これはどのAndroid版ですか? – JesusFreke

答えて

3

あなたのアプリケーションと読み込んだdexファイルの両方にIRDroidInterfaceインターフェイスが存在することが問題であると思われます。 MyTestClass_IRDroidUIPlugInクラスを動的にロードすると、dexファイルからのものであるため、アプリケーションのクラスではなく、dexファイルからインターフェイスクラスを実装します。

インターフェイスクラスがプラグインのdexファイルに含まれないようにすることができれば、うまくいくはずです。

eclipseまたはantを使用してプラグインapkを構築する場合、残念ながら、これはやりにくいかもしれません。 1つの(むしろ醜い)解決策は、インターフェースクラスが作成された後にdexファイルからインターフェースクラスを削除することです。つまり、baksmaliで逆アセンブルし、インタフェースの.smaliファイルを削除して、smaliで再アセンブルします。

+0

私は、両方のプロジェクトでInterfaceをライブラリとして使用することで、私のことを考えてくれることを期待していました。私がClassCastExceptionについて読んだことはすべてあなたの右に伝えます。 – cstrutton

+0

プラグインのdexファイルにIRDroidInterfaceクラスが存在するかどうか確認できますか?すなわち、baksmaliを使用して逆アセンブルし、com/strutton/android/UIPlugInLib/IRDroidInterface.smaliが存在するかどうかを確認します。 – JesusFreke

+0

ああ。ダイナミックにロードされていたdexファイルにインターフェイスが存在しないことを確認したら、これをちょっと試してみて(インターフェイスに直接キャストできる)、ちょっと試してみてください。 – JesusFreke

0

この問題を解決するには、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); 
} 
} 
関連する問題