不安裝APK,仍然可以調用APK文件中的Java類,這種訪問Java類的方式稱為“動態引用APK文件”,——相當于傳統的java程序動態調用jar文件。
APK文件本質上是ZIP格式的壓縮文件,要想動態調用APK文件,在APK文件中必須包含一個classes.dex文件(classes.dex文件是Android應用中所有的Java源代碼編譯生成的Davlik虛擬機格式的二進制文件)。每一個編譯過的Android工程目錄的bin目錄下都有一個classes.dex文件和一個相應的APK文件。
動態調用的APK文件的擴展名并不重要,也可以使用任何的擴展名,還甚至可以沒有擴展名。比如XXXX.apk,XXXX.jar,XXXX.abcd,XXXX都沒問題。
下面演示一個動態調用APK文件中的Java類的完整案例:
(1)編寫Remote工程——新建一個Remote項目,并在其中添加一個如下類:
~~~
package songshi.remote;
public class ServiceClass {
public String addService(){
return "MyProject調用Remote工程的AddService方法成功";
}
}
~~~
運行Remote工程,生成Remote.apk(在bin目錄下),將此APK文件push到Android模擬器DDMS的/mnt/sdcard/下。
(2)編寫MyProject工程,布局文件添加一個按鈕
~~~
package com.songshi.myproject;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
import android.media.RemoteControlClient.MetadataEditor;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
/*
* 使用DexClassLoader類動態裝載APK文件
* public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent);
* dexPath參數:表示APK文件的路徑;
* optimizedDirectory參數:表示一個用于寫入優化后的APK文件的目錄,通常為程序的私有數據目錄;
* parent參數:通常為ClassLoader.getSystemClassLoader()
* */
private DexClassLoader dexClassLoader;
private Button btnAdd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//第 1 步:裝載APK文件
//定義優化目錄:/data/data/com.songshi.myproject
String optimizedDirectory= Environment.getDataDirectory().toString() + "/data/" + getPackageName();
dexClassLoader=new DexClassLoader("/mnt/sdcard/Remote.apk", optimizedDirectory, null, ClassLoader.getSystemClassLoader());
btnAdd=(Button) findViewById(R.id.btnAdd);
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try{
//第 2 步:裝載要訪問的類
Class c=dexClassLoader.loadClass("songshi.remote.ServiceClass"); //Call requires API level 14 (current min is 8)
//第 3 步:創建類的對象
Object obj=c.newInstance();
//第 4 步:用Java反射技術調用ServiceClass類中的addService方法
Method method = obj.getClass().getMethod("addService", null);
String add =String.valueOf(method.invoke(obj, null));
Toast.makeText(MainActivity.this, add, Toast.LENGTH_LONG).show();
}
catch(Exception e){
Toast.makeText(MainActivity.this, "error:"+e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
/*
* APK文件并不是什么類都可以調用。例如,有Context類型參數的方法就不能動態訪問,因為只有已經安裝的APK程序才能獲得Context對象。
* 還有四大組件類也不可以使用,例如,由于窗口類是由系統自動創建和維護的,所以 Activity的子類自然就不能通過動態訪問的方式當做窗口類來使用。
* */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
~~~
運行

特別注意:APK文件并不是什么類都可以調用。例如,有Context類型參數的方法就不能動態訪問,因為只有已經安裝的APK程序才能獲得Context對象。還有四大組件類也不可以使用,例如,由于窗口類是由系統自動創建和維護的,所以 Activity的子類自然就不能通過動態訪問的方式當做窗口類來使用。
- 前言
- Java內部類
- 從一個View向一個Activity跳轉
- Android 與 SQLite
- Android工程A依賴B,B依賴C
- Android重要控件概覽(上)
- Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED
- Android布局概覽
- 動態引用APK文件
- Android重要控件概覽(中)
- Android重要控件概覽(下)
- Gallery和ImageSwitcher
- Android之Toast
- Android之Dialog
- Android之Notification
- Android之Menu
- Android Menu中android:showAsAction屬性
- Android SharedPreferences存儲數據的使用方法
- Android手勢識別之GestureDetector
- 不同APP通過SharedPreferences傳遞數據(共享數據)
- 一個自定義的Topbar模板
- 關于Activity回收造成View選中不對應的問題
- Android之Fragment靜態加載