`
wangleyiang
  • 浏览: 220764 次
社区版块
存档分类
最新评论

Android 通过反射启动未安装的APK中的Activity

阅读更多

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的加载,跳转操作还需要测试;
  • 如何做进一步调整;
  • 相关代码见附件!:)

 

 

 

 

  • 大小: 6.7 KB
  • 大小: 18.6 KB
  • 大小: 14.6 KB
  • 大小: 18.6 KB
分享到:
评论
3 楼 lmjmn456 2014-11-04  
为什么我的TestHall里面看不到RomeA RomeB RomeC这几个按钮呢?
2 楼 wangleyiang 2014-03-11  
uyerp 写道
这样的话,只能将插件的元素显示到Hall中。
有什么办法能在不安装的情况下,将插件的界面就在插件中初始化吗?

暴力调试一下,看看Room X中的执行顺序,然后在合适的地方添加初始化代码。如果上述方法不可行,是否可以考虑调用Room X的某段代码来初始化呢...
1 楼 uyerp 2013-12-24  
这样的话,只能将插件的元素显示到Hall中。
有什么办法能在不安装的情况下,将插件的界面就在插件中初始化吗?

相关推荐

    Android动态加载未安装apk

    在Android开发中,有时我们需要实现一个功能,即在运行时动态加载未安装的APK文件。这主要应用于插件化开发、热更新或者模块化的应用架构。动态加载未安装的APK可以让应用在不更新主应用程序的情况下添加或更新功能...

    android开发实现插件化开发,使用hook启动未注册的activity实现demo

    在Android开发中,插件化是一项重要的技术,它允许开发者在不重新编译或安装整个应用的情况下添加、更新或移除应用的部分功能。本示例"android开发实现插件化开发,使用hook启动未注册的activity实现demo",提供了一...

    Android实行插件化开发启动未注册activity完整demo

    "Android实行插件化开发启动未注册Activity完整demo"展示了如何在Android系统中突破常规限制,通过Hook技术和动态加载实现插件化。这不仅提高了应用的灵活性,也为开发者提供了更广阔的设计空间。然而,实现这一技术...

    Android插件化动态加载 APK 文件中(Activity,Servicer,Broadcast)等一些资源

    总的来说,Android插件化动态加载APK文件涉及到类加载机制、组件动态启动、资源加载等多个方面,需要深入理解Android系统的工作原理和反射机制。通过这样的设计,我们可以构建出更加灵活、可扩展的应用架构,适应...

    android 启动手机内包含固定字符串的apk应用

    在Android系统中,启动特定APK应用通常涉及到对AndroidManifest.xml文件的理解、权限管理、Activity启动模式以及Intent过滤器的应用。下面将详细讲解如何在Android设备上启动包含固定字符串的APK应用。 首先,理解...

    Android获取已安装的apk信息

    1. **获取PackageManager对象**:在Android应用中,通常在Activity或者Service中,可以通过`getPackageManager()`方法获取到`PackageManager`实例。例如: ```java PackageManager packageManager = ...

    android插件开发---通过预注册方式打开插件中activity

    在Android插件开发中,我们需要在宿主应用中通过反射获取插件Activity的Class对象,以便在需要时创建实例或执行方法。首先,我们需要知道插件Activity的全限定类名,然后通过`Class&lt;?&gt; clazz = Class.forName("全...

    静默安装APK Demo.zip

    通过"静默安装APK Demo.zip"中的示例代码,您可以进一步学习如何结合反射机制来调用非公开API,实现跨版本兼容的静默安装。请注意,由于Android系统的安全性不断加强,某些版本可能不再支持静默安装,或者需要额外的...

    android加载sd卡上没安装的apk的类

    在Android系统中,加载SD卡上未安装的APK文件并执行其Activity是一项涉及系统安全性和权限管理的任务。为了实现这一功能,开发者需要对Android的沙盒机制、Intent解析、文件系统访问以及权限请求有深入的理解。下面...

    android 插件打开其他apk并实现跳转下级页面

    在Android应用开发中,实现一个插件能够打开其他APK并跳转到其下级页面是一项常见的需求,尤其是在构建插件化或者动态加载系统时。本文将详细讲解如何实现这一功能,特别关注解决CSDN和GitHub上可能出现的代码bug。 ...

    android java反射,通过图片名获取图片

    在给定的场景中,“android java反射,通过图片名获取图片”这个主题涉及到利用反射机制来动态地加载和使用资源图片。下面将详细阐述这一知识点。 首先,我们需要理解Java反射的基本概念。Java反射是Java语言提供的...

    Android代码-dynamic-load-apk

    [使用Android Stuido导入项目](Android Studio.md) APK动态加载框架(DL)解析 DL 2.0的新特性 支持多进程模式,插件可以运行在单独的DL进程中(代码在lab分支) 支持插件中的so库(代码在dev分支) DL支持的功能 ...

    apk动态加载研究(1)

    6. **Activity和Service的启动**:动态加载的代码中可能包含Activity或Service,需要考虑如何在运行时正确启动它们。 然而,动态加载也存在一些挑战,如性能影响、兼容性问题、调试困难等。为了克服这些挑战,...

    android加载第三方apk插件

    本文将深入探讨如何在Android应用中加载并运行第三方的APK插件,以此来提升应用的可维护性和用户体验。 首先,我们要理解的是Android的组件生命周期和权限管理。Android应用由四大组件构成:Activity、Service、...

    动态加载apk demp

    在Android开发中,动态加载APK(Dynamic APK Loading)是一种重要的技术,它允许应用程序在运行时下载并安装额外的模块,而无需用户手动更新整个应用。这种技术在提高用户体验、节省存储空间以及实现热修复等方面...

    动态加载Activity

    4. 启动Activity:有了注册过的Activity实例后,就可以通过Intent启动它,通常会配合FLAG_ACTIVITY_NEW_TASK标志,以确保Activity在新的任务栈中启动。 三、插件化框架 动态加载Activity在实际应用中通常与插件化...

    插件式开发(三)

    本篇文章主要探讨的是如何在插件化开发中打开未安装的APK中的Activity,尽管存在无法直接使用资源文件的限制。 首先,理解插件化开发的核心思想:主应用(宿主)提供运行环境,而各个功能模块(插件)以独立的APK...

    android根据layout文件生成activity及自动注册 Demo

    在Android开发中,通常我们需要为每个用户界面创建一个对应的Activity,并手动在主应用程序的AndroidManifest.xml文件中注册这些Activity。然而,这样的过程可能会变得繁琐,尤其是在项目变得庞大时。"android根据...

    Android4.0静默安装源码

    在Android系统中,静默安装(Silent Installation)是指在用户无感知的情况下,通过编程方式自动完成APK的安装和升级。这种技术常用于企业级应用分发、系统更新等场景,可以提高效率并减少用户的操作步骤。Android ...

Global site tag (gtag.js) - Google Analytics