Question

I am building a home automation app. I am trying to add a plugin system. I have as a test exported a test class (that subclasses Button) as an APK file and put it in my app's file directory. I was able to create a new instance of this class and put it in my View using DexClassLoader and .loadClass.

The next step is to scan all the APK's in this directory and get the names of the classes in them.

I found the DexFile class that does just that however it throws the following exceptions:

04-18 17:26:15.697: E/dalvikvm(726): Can't open dex cache '/data/dalvik-cache/data@data@com.strutton.android.testplugin@files@testloadclass.apk@classes.dex': No such file or directory

04-18 17:26:15.705: I/dalvikvm(726): Unable to open or create cache for /data/data/com.strutton.android.testplugin/files/testloadclass.apk (/data/dalvik-cache/data@data@com.strutton.android.testplugin@files@testloadclass.apk@classes.dex)

It would seem that it is trying to find the optimized DexFile in the system cache but I do not have permissions to the cache directory. From what I can see this is probably by design and I don't have a problem with that. Is there another way to parse a DEX file and get the class names from it?

I have looked at the source for some of the dex decompiler projects. Do I have to roll my own solution?

Here is my test app code (from my Activity OnCreate) just in case I missed something:

    try {
        ArrayList<String> UIPlugins = new ArrayList<String>();
        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<Button> classToLoad = 
                (Class<Button>) classloader.loadClass("com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn");
        Button mybutton = classToLoad.getDeclaredConstructor(Context.class).newInstance(this);
        mybutton.setId(2);
        mybutton.setOnClickListener(this);
        main.addView(mybutton);
        // Up to here everything works as expected
        // This line throws the exceptions
        DexFile mDexFile = new DexFile(filesDir.getAbsolutePath()+"/testloadclass.apk";);
        Enumeration<String> classNames = mDexFile.entries();
        while (classNames.hasMoreElements()){
            String className = classNames.nextElement();
            if (className.endsWith("IRDroidUIPlugIn")){
                UIPlugins.add(className);
            }
        }
        final Class<Button> classToLoad = 
                (Class<Button>) classloader.loadClass("com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn");
        Button mybutton = classToLoad.getDeclaredConstructor(Context.class).newInstance(this);
        mybutton.setId(2);
        mybutton.setOnClickListener(this);
        main.addView(mybutton);
        btn.setText(tmpDir.getAbsolutePath());
      } catch (Exception e) {
        e.printStackTrace();
    }
Was it helpful?

Solution

Instead of:

DexFile mDexFile = new DexFile(filesDir.getAbsolutePath()+"/testloadclass.apk";);

try:

DexFile mDexFile = DexFile.loadDex(filesDir.getAbsolutePath()+"/testloadclass.apk", tmpDir.getAbsolutePath()+"/testloadclass.odex", 0);

OTHER TIPS

Another approach you might consider, would be to install the plugins as separate applications. You could have the plugin application expose a service, using a particular intent that you define. You would also need a pre-defined aidl interface that the service would implement. In your main application, you could then resolve that service intent, to find any plugin services.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top