您的位置:首页 > 移动开发 > Android开发

动态引用APK文件

2015-12-07 15:14 309 查看
      不安装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的子类自然就不能通过动态访问的方式当做窗口类来使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息