JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
查看类的继承关系,Android中的Activity和Service,是基于Context的。Context代表了用户和系统的交互过程。侠义理解,获得Context就获得了和系统进行交互的可能性。
说明:
- Room A, Room B, Room N,是没有被安装的APK格式文件,为了简单说明,里面只有一个Activity;
- Hall:被安装的APK,用于提供Context给Room A, Room B, Room N等Activity;
- Hall启动Room X使用的是反射技术和提供给Room X自己的Context,用于交互操作;
Room工具类:
import java.lang.reflect.Constructor; import java.lang.reflect.Method; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import dalvik.system.DexClassLoader; public class RoomUtils implements OnClickListener { private Context mContext; /** * 目标Activity的Class */ private Class<?> localClass; /** * 目标Activity的实例 */ private Object localInstance; /** * * @param context * @param dexPath * @param dexOutputPath */ public RoomUtils(Context context, String dexPath, String dexOutputPath) { mContext = context; loadAPK(dexPath, dexOutputPath); } /** * 获得相关的View * @return 返回一个可点击的按钮 */ public View getView() { Button button = new Button(mContext); String message = null; try { message = getRemoteMessage(); button.setText(message); button.setOnClickListener(this); } catch (Exception e) { e.printStackTrace(); message = null; } if (message == null) { button.setVisibility(View.GONE); } return button; } /** * 通过反射,获得描述性信息 * @return * @throws Exception */ private String getRemoteMessage() throws Exception { Method remoteMessageMethod = localClass.getDeclaredMethod("getRoomMessage", null); remoteMessageMethod.setAccessible(true); return (String) remoteMessageMethod.invoke(localInstance, null); } /** * 通过反射,获得Activity的实例和Class * @param dexPath * @param dexOutputPath */ private void loadAPK(String dexPath, String dexOutputPath) { try { ClassLoader localClassLoader = ClassLoader.getSystemClassLoader(); DexClassLoader localDexClassLoader = new DexClassLoader(dexPath, dexOutputPath, null, localClassLoader); PackageInfo localPackageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES); if ((localPackageInfo.activities != null) && (localPackageInfo.activities.length > 0)) { String activityName = localPackageInfo.activities[0].name; localClass = localDexClassLoader.loadClass(activityName); Constructor<?> localConstructor = localClass.getConstructor(new Class[] {Context.class}); localInstance = localConstructor.newInstance(new Object[] {mContext}); } } catch (Exception ex) { ex.printStackTrace(); } } /** * 通过反射,启动Activity * @throws Exception */ private void startRemoteActivity() throws Exception { Method onCreateMethod = localClass.getDeclaredMethod("onCreate", new Class[] {Bundle.class}); onCreateMethod.setAccessible(true); onCreateMethod.invoke(localInstance, new Object[] {null}); } @Override public void onClick(View v) { try { startRemoteActivity(); } catch (Exception e) { e.printStackTrace(); } } }
说明:
- 作用:使用DexClassLoader加载指定路径的APK文件,并提供按钮和按钮点击事件;
- localClass:通过反射获得的Room X中的Activity的Class引用,注意String activityName = localPackageInfo.activities[0].name;中指定的是第0个Activity,如果有多个Activity,要做其它处理;
- localInstance:通过反射获得的Room X中的Activity示例;
- Constructor<?> localConstructor = localClass.getConstructor(new Class[] {Context.class});在加载构造函数时,自定义了含有Context参数的构造函数;
- getView:返回一个和Room X相关联的按钮;
Hall中调用过程:
public class TestHallActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 获得根容器 LinearLayout root = (LinearLayout) findViewById(R.id.root); // 分别获得SDCARD下的APK并添加相关View到根容器 RoomUtils roomA = new RoomUtils(this, "/mnt/sdcard/TestRoomA.apk", "/mnt/sdcard/"); root.addView(roomA.getView()); RoomUtils roomB = new RoomUtils(this, "/mnt/sdcard/TestRoomB.apk", "/mnt/sdcard/"); root.addView(roomB.getView()); RoomUtils roomC = new RoomUtils(this, "/mnt/sdcard/TestRoomC.apk", "/mnt/sdcard/"); root.addView(roomC.getView()); } }
说明:
- 创建RoomUtils实例,看到APK所在目录和DEX输出目录都在SDCARD下;
- 添加按钮到根视图中;
Room A中主要操作:
import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.Toast; public class TestRoomAActivity extends Activity implements OnClickListener { /** * TestHallActivity的上下文引用 */ private Activity otherActivity; /** * 无参构造函数 */ public TestRoomAActivity() { super(); } /** * 有参构造函数 * @param context */ public TestRoomAActivity(Context context) { super(); otherActivity = (Activity) context; } @Override public void onCreate(Bundle savedInstanceState) { if (otherActivity != null) { // 使用TestHallActivity的上下文引用创建View并添加到根视图 Button button = new Button(otherActivity); button.setText("Room A"); button.setOnClickListener(this); LinearLayout root = new LinearLayout(otherActivity); root.addView(button); // setContentView(root);使用的上下文是当前Activity的,而不是指定的TestHallActivity otherActivity.setContentView(root); } else { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } /** * 返回当前Activity的描述信息 * @return */ private String getRoomMessage() { return "Room A"; } @Override public void onClick(View v) { if (otherActivity != null) { // Toast.makeText(this, "This is Room A!", Toast.LENGTH_SHORT).show();是不合适的调用 Toast.makeText(otherActivity, "This is Room A!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "This is Room A!", Toast.LENGTH_SHORT).show(); } } }
说明:
- otherActivity:Hall对应上下文的引用;
- public TestRoomAActivity(Context context);有参构造函数;
- 当用到上下文的地方使用otherActivity代替,这样就可以使用otherActivity对应的Context做相关交互操作;
效果图如下:
说明:
- 由于使用的Context是Hall的,所以如果点击Back按钮,直接退出示例程序;
- Room B, Room C都和Room A类似,详见附件;
多说一句:
- 由于Hall和Room X使用同一个Context,在点击Back后,直接退出应用程序,是否有比较合适的解决方案(肯定有,要看具体要求而定罢了!+_+);
- Room X中只添加了一个Activity,对于多Activity的加载,跳转操作还需要测试;
- 如何做进一步调整;
- 相关代码见附件!:)
相关推荐
[使用Android Stuido导入项目](Android Studio.md) APK动态加载框架(DL)解析 DL 2.0的新特性 支持多进程模式,插件可以运行在单独的DL进程中(代码在lab分支) 支持插件中的so库(代码在dev分支) DL支持的功能 ...
(3)Hook技术动态加载APK文件中的Activity 二、Android 动态加载dex文件 1.反射方式加载(较麻烦,不介绍),需要反射出具体的方法等 2.接口编程的方式加载(以下重点介绍),只需要反射出具体的类即可,转换成接口...
1种是fragment运行在任意支持fragment的activity中,这种方式,在开发fragment的时候,fragmeng中凡是要使用context的地方,都需要使用通过PluginLoader.getPluginContext()获取的context,那么这种fragment对其运行...
静默安装就是偷偷的把一个应用安装到手机上,就是屏蔽确认框,通过反射 只能写个主要的代码,这个是在linux编译用到,因为静默安装需要调用系统服务,在源码下编译,我也是搞了好久 InstallActivity.java package ...
好了上面介绍了之前准备的知识点后今天我们做一个真正的可运行的启动插件demo,要知道一个插件可能是随时从网上下载下来的,那么也就是说其实这个apk不会被安装,那么如果不被安装,怎么能被加载呢, 又如何管理插件...
无法在插件中注册一些具有特殊Intent Filter的Service、Activity、BroadcastReceiver、ContentProvider等组件以供Android系统、已经安装的其他APP调用。 - f. 缺乏对Native层的Hook,对某些带native代码的apk支持...
|--Intent启动应用apk安装 |--Intent常用功能 |--IO将输入流转成字节 |--Json读js资源文件 |--layout布局样式之style配置 |--listview 页面 图片加文字 |--ListView之CursorAdapter异步查询框架之短信 |--ListView...
5、插件中的fragment、activity、receiver、contentprovider组件拥有真正生命周期,完全交由系统管理、无反射无代理(插件service 也可交由系统管理,但是不支持多实例,因此插件service仍然通过代理方式实现。) 6...
APK安装监听: Android垃圾清理: Android混淆机制: Android获取应用总大小: Android沉浸式状态栏: , Android沉浸式状态栏标题解决: Android ViewModel与LiveData结合: Android ViewModel与Activity结合处理...