2015-10-19 30 views
1

我使用DexClassLoader類使用DexClassLoader

final String libPath = Environment.getExternalStorageDirectory() +  "/test.jar"; 
     final File tmpDir = getDir("dex", 0); 

     final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader()); 
     final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass"); 

     final Object myInstance = classToLoad.newInstance(); 
     final Method doSomething = classToLoad.getMethod("doSomething"); 



     doSomething.invoke(myInstance); 

在我的jar文件我正在印刷這是工作fine.Now我想做一些日誌從SD卡運行時加載一個jar文件動態加載庫交互要做的是從jar文件中打印Toast。雖然這樣做,我得到的異常是java.lang.reflect.InvocationTargetException。我知道爲什麼我們得到這個異常,並且它背後的原因是在打印吐司時使用它的上下文中的空指針異常。因此,它不是

What could cause java.lang.reflect.InvocationTargetException?

其背後原因的重複是

java.lang.NullPointerException: Attempt to invoke virtual method  'android.content.Context android.content.Context.getApplicationContext()' on a null object reference 

代碼的jar文件

public class MyClass extends Activity { 


@Override 
protected void onCreate(Bundle savedInstanceState) { 
    // TODO Auto-generated method stub 
    super.onCreate(savedInstanceState); 
} 


public void doSomething() { 

    Toast.makeText(getApplicationContext(), "MyClass: doSomething() called.", Toast.LENGTH_LONG).show(); 

    Log.e(MyClass.class.getName(), "MyClass: doSomething() called."); 
}} 

誰能幫助我實現this.Any幫助會讚賞。

編輯: 我想要做的是我有我自己的圖書館,我的許多客戶正在使用...我想要的是從用戶的SD卡加載我的圖書館..我想在沒有用戶的知識且沒有任何版本更新的情況下更新圖書館。圖書館包括幾個界面,碎片和活動。所以現在我可以從SD卡加載我的庫並且可以調用基本函數。現在主要的挑戰是從庫中實現接口並調用在其中使用上下文的函數。

任何涉及此類操作的示例或提示都將非常有幫助。

+2

的可能重複[這是什麼原因Java的。 lang.reflect.InvocationTargetException?](http://stackoverflow.com/questions/6020719/what-c​​ould-cause-java-lang-reflect-invocationtargetexception) – Emil

+0

@Boss異常不是我的問題here.i知道它的起因。我需要幫助來實現所需的東西..所以它不是重複的那個java.lang.reflect.InvocationTargetException問題 –

+0

...是的,它是一個重複的...你需要解開這個異常,以瞭解是什麼導致它... – Selvin

回答

1

我看到解決這個,這取決於你是願意做的兩種方式:

  • 如果MyClass必須是一個Activity

Activity已正常啓動,使用startActivity()或任何變體。它也必須在清單中聲明。因此,只有當您的所有MyClass變體具有相同的簽名時,以下才有效。

我假設你的啓動代碼位於Activity。已經加載classToLoad之後,你可以做到以下幾點:

final File tmpDir = getDir("dex", 0); 
final String libPath = Environment.getExternalStorageDirectory() + "/test.jar"; 
final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader()); 

try { 
    final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass"); 

    // CHANGED: THIS STARTS PROPERLY YOUR ACTIVITY ONCE THE CLASS LOADED 
    final Intent intent = new Intent(this, classToLoad); 
    startActivity(intent); 

} catch (ClassNotFoundException e) { 
    // handle that Exception properly here 
} 

在它使用的getApplicationContext()Activity不是基礎Context方式現在改變doSomething()。然後從MyClass.onCreate()例如調用它,看到它的工作原理:

public class MyClass extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     doSomething(); // CHANGED: just as an example 
    } 

    private void doSomething() { 
     // CHANGED: now the following line uses 'this' instead of `getApplicationContext()` 
     Toast.makeText(this, "MyClass: doSomething() called.", Toast.LENGTH_LONG).show(); 

     Log.d(MyClass.class.getName(), "MyClass: doSomething() called."); 
    } 
} 

其餘的 - 當調用doSomething(),爲什麼,等等 - 一切都取決於你願意做什麼。

  • 如果MyClass只是要顯示Toast

沒有必要在這種情況下,以創建另一個ActivitydoSomething()只需要收到正確的Context即可顯示Toast

變化MyClass如下:

public class MyClass { 
    private void doSomething(Context ctx) { 
     Toast.makeText(ctx, "MyClass: doSomething() called.", Toast.LENGTH_LONG).show(); 

     Log.d(MyClass.class.getName(), "MyClass: doSomething() called."); 
    } 
} 

,改變你的啓動代碼通過thisdoSomething(),假設它是從Activity運行:

final File tmpDir = getDir("dex", 0); 
final String libPath = Environment.getExternalStorageDirectory() + "/test.jar"; 
final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader()); 

try { 
    final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass"); 

    // CHANGED: LOADS THE METHOD doSomething(Context). EXECUTES IT WITH this AS AN ARGUMENT 
    final Class[] args = new Class[1]; 
    args[0] = Context.class; 
    final Method doSomething = classToLoad.getMethod("doSomething", args); 

    final Object myInstance = classToLoad.newInstance(); 

    doSomething.invoke(myInstance, this);  
} catch (ClassNotFoundException e) { 
    // handle that Exception properly here 
} 
+0

@shublu非常感謝您的回覆。第一種方法將無法正常工作,因爲如果我們正在使用Intent,它會在當前項目中找到活動...您能請告訴我們如何將它傳遞給doSomething()作爲參數..我的意思是如何在classToLoad.getMethod(「doSomething」)中實現這一點。 –

+0

正如我以爲第一種方法是拋出ActivityNotFoundException ... :( –

+0

'Method.invoke()'可以接收你需要的所有參數:http://developer.android.com/reference/java/lang/reflect/Method。 html#invoke(java.lang.Object,java.lang.Object ...) 我更新了我的答案。 – Shlublu