您的位置:首页 > 编程语言

动态加载apk文件并调用其代码

2016-06-07 21:03 295 查看
转载:文中思路来自链接:http://blog.csdn.net/jiangwei0910410003/article/details/17679823

一、编写动态功能类

编写一个apk,用于导出方法,供其他apk调用

public class Dynamic implements IDynamic{
private Activity mActivity;

public Dynamic() {
// TODO Auto-generated constructor stub
}
@Override
public void init(Activity activity) {
mActivity = activity;
Log.i("TestDynamic", "In Dynamic::Init");
}

@Override
public void showBanner() {
Toast.makeText(mActivity, "我是ShowBannber方法", 1500).show();
}

@Override
public void showDialog() {
Toast.makeText(mActivity, "我是ShowDialog方法", 1500).show();
}

@Override
public void showFullScreen() {
Toast.makeText(mActivity, "我是ShowFullScreen方法", 1500).show();
}

@Override
public void showAppWall() {
Toast.makeText(mActivity, "我是ShowAppWall方法", 1500).show();
}

@Override
public void destory() {
}

}


IDynamic是一个接口,用于外界可以使用接口进行调用

public interface IDynamic {
/**初始化方法*/
public void init(Activity activity);
/**自定义方法*/
public void showBanner();
public void showDialog();
public void showFullScreen();
public void showAppWall();
/**销毁方法*/
public void destory();
}


此外,在manifest.xml中,需要声明一个Action,用于其他apk能通过Intent检索到此apk

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="com.example.dynamiclib"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>


,名字无所谓,只要其他apk通过Intent能检索到此apk就可以

二、调用动态apk

根据目标apk包名,获取其包路径、catche路径等信息,创建DexClassLoader对象,并通过反射机制来调用动态apk中的功能函数代码

public class MainActivity extends Activity implements OnClickListener{

private Button btn_show_banner;
private Button btn_show_dialog;
private Button btn_show_fullscreen;
private Button btn_show_appwall;
private final static String TAG = "TestDynamic";
private IDynamic libInterface = null;
private Method method_init = null;
private Method method_showBanner = null;
private Method method_showDialog = null;
private Method method_showFullScreen = null;
private Method method_showAppWall = null;
private Object object = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_show_banner = (Button) findViewById(R.id.button1);
btn_show_dialog = (Button) findViewById(R.id.button2);
btn_show_fullscreen = (Button) findViewById(R.id.button3);
btn_show_appwall = (Button) findViewById(R.id.button4);

/**
* 调用静态apk时,需要有一个解压路径
* **/
File dexOutputDir = this.getDir("dex", 0);
/**
* 获取apk存放的路径,此apk未安装,在磁盘上,也可能是网络临时拉下来的
* 这里测试,放在SD卡根目录
* 主要是用于DexClassLoader的第一个参数使用
* **/
String dexPath = Environment.getExternalStorageDirectory().toString()
+ File.separator
+ "DynamicLib.apk";
Log.i(TAG, "ApkPath = "+dexPath);
Log.i(TAG, "dexOutPath = "+dexOutputDir.getAbsolutePath());

/**
* 与被调用的apk里名称约定要一致
* 用来找到指定apk,系统中多个时,下面get要注意取哪一个
* **/
Intent intent = new Intent("com.example.dynamiclib");
/**
* 获取包管理器
* **/
PackageManager pm = getPackageManager();
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
/**
* 获得指定Activity信息
* 如果有两个名字都是“com.example.dynamiclib”的Intent,
* 则可能需要get(1),因为它会返回两个activity
* **/
ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
/**
* 获得apk的目录或者jar的目录
* **/
String apkPath = activityInfo.applicationInfo.sourceDir;
/**
* native代码的目录
* **/
String libPath = activityInfo.applicationInfo.nativeLibraryDir;
/**
* 第一种方法
* 创建DexClassLoader
* 把apk加载到虚拟机中
* 注意:这里的路径可以使用两种
*      即可以是dexPath(磁盘上的静态文件)
*      也可以是通过pm获取的已经安装的apk的路径
* **/

DexClassLoader dcl = new DexClassLoader(dexPath,
dexOutputDir.getAbsolutePath(),
null,
this.getClass().getClassLoader());

/**
* 第二种方法
* 使用PathClassLoader
* 这里只能找到已经安装的apk
* 并且路径,必须通过Intent、PM来联合获取,否则不能成功
* 此外,Intent里的名字,如果系统里存在两个名字相同的apk,name可能还需要试试第二个
* 因为不一定哪个在前,哪个在后
* **/
//PathClassLoader dcl = new PathClassLoader(apkPath, libPath, this.getClass().getClassLoader());

Log.i(TAG, "DexClassLoader ="+dcl);
try {
Log.i(TAG, "before dcl.loadClass");
/**
* 加载目标class,名称为目标类名
* **/
Class<?> orgClass = dcl.loadClass("com.example.dynamiclib.Dynamic");
Log.i(TAG, "orgClass load is " + orgClass.getClassLoader().toString());
object = orgClass.newInstance();
Class[] param = new Class[1];
param[0] = Activity.class;
method_init = orgClass.getMethod("init", param);
method_showBanner = orgClass.getMethod("showBanner");
method_showDialog = orgClass.getMethod("showDialog");
method_showFullScreen = orgClass.getMethod("showFullScreen");
method_showAppWall = orgClass.getMethod("showAppWall");
if (method_init != null) {
method_init.invoke(object, this);
}
Log.i(TAG, "libInterFace is "+libInterface);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
Log.i(TAG, "Not Found Class");
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
btn_show_banner.setOnClickListener(this);
btn_show_dialog.setOnClickListener(this);
btn_show_fullscreen.setOnClickListener(this);
btn_show_appwall.setOnClickListener(this);

}
void ShowTips(String tips)
{
Toast.makeText(this, tips, Toast.LENGTH_LONG).show();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
if (method_showBanner != null) {
try {
method_showBanner.invoke(object);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
ShowTips("类加载失败");
}
break;
case R.id.button2:
if (method_showDialog != null) {
try {
method_showDialog.invoke(object);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
ShowTips("类加载失败");
}
break;
case R.id.button3:
if (method_showFullScreen != null) {
try {
method_showFullScreen.invoke(object);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
ShowTips("类加载失败");
}
break;
case R.id.button4:
if (method_showAppWall != null) {
try {
method_showAppWall.invoke(object);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
ShowTips("类加载失败");
}
break;
default:
break;
}
}
}


这里是此处通过反射机制来调用代码,还没有完成通过接口来调用的功能,待继续调试

这里是从已经安装的apk获取路径等信息,如果是目录下静态的apk(并没有安装到手机上),直接把构建DexClassLoader的第一个参数改为静态apk的绝对路径,其他不需要变化,即可正常运行;

不过如果使用PathClassLoader,来加载,必须通过intent查找目标apk,同时目标apk必须已经安装,因为它只能加载固定目录下的apk,不能随意路径的apk

使用了IDynamic后,需要把对应的jar包引入到工程里的lib中,通过“Build Path”->”Configure Build Path”里进行添加;只把jar拷贝到这个目录是没有用的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: