1

I have a library that I plan on using in dex form. I want to compile directly against this library, but not export it. Instead I want to drop it in my resources and use a class loader to actually instantiate it.

So here's my library:

public class Foo {
  public doFoo(String message) {
  }

  public doFoo(int count, String message) {
  }
}

Now I want to call doFoo(). A lot. More than it's probably reasonable to use reflection for. Right now it works with:

public class FooConsumer {
  private final DexClassLoader fooLoader;

  public FooConsumer(DexClassLoader fooLoader) {
    this.fooLoader = fooLoader;
  }

  public void go() {
    Class<?> fooClass = fooLoader.loadClass("com.library.Foo");
    Object fooInstance = fooClass.newInstance();
    Method fooMethodDoFoo = fooClass.getMethod("doFoo", String.class);
    fooMethodDoFoo.invoke(fooInstance, "Hello World");
  }

This is obviously fugly. Especially since I haven't included any of the exception handling, as there are half a dozen throwables to catch in there. I could cache a bunch of stuff, helping me with speed a bit, but not a lot.

Normally I'd have both aware of a third library that has an interface, but the library has some static methods and I can't edit it anyway. It'd be really nice if I could do something like:

public class FooConsumer {
  private FooAccessor accessor;

  public FooConsumer(DexClassLoader fooLoader) {
    Object fooInstance = fooLoader.loadClass("com.library.Foo").newInstance();
    Log.i("TEST", "fooInstance: " + fooInstance);
    this.accessor = new FooAccessor(fooInstance);
  }

  public void go() {
    accessor.doFoo("Hello World");
  }

  private static class FooAccessor {
    private Foo fooInstance;

    public FooAccessor(Object instance) {
      fooInstance = (Foo)instance;
    }

    public void doFoo(String message) {
      fooInstance.doFoo(message);
    }
  }
}

See what I did there? The inner class is just a wrapper around the Foo object, I've linked against it, but not exported it, and all is good in the world. But it doesn't work. In logcat I get

I/TEST: fooInstance: com.library.Foo@413b1b68
E/AndroidRuntime: java.lang.NoClassDefFoundError: com.library.Foo
  ...

Is there a way to have FooAccessor use the class loader I passed in? Or is the use of class loaders a damnation into reflection hell.

4

1 回答 1

1

你可能想看看这个要点。

https://gist.github.com/nickcaballero/7045993

它使用反射将新的 DexClassLoader 合并到库存的 BaseDexClassLoader。

于 2014-09-18T01:55:40.697 回答