解决Bundle中使用反射时,报ClassNotFoundException异常

本文介绍了一个关于OSGi Bundle部署时出现ClassNotFoundException的问题,并详细解释了解决方案。通过修改pom.xml文件,确保所有必要的包都被正确导入,从而避免运行时错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源代码下载

user-model:

user-model/user:

/**
 
 * @author wumingkun
 * @version 1.0.0
 * @Description
 */

package com.demo.user.user_model;

/**
 * @author wumingkun
 *
 */
public class User {
	private int id;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String toString() {
		return "User [id=" + id + ", name=" + name + "]";
	}
	
	
}


user-modle/pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.demo.user</groupId>
	<artifactId>user-model</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>bundle</packaging>

	<name>user-model</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<configuration>
					<instructions>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>



user-service:

UserService

/**
 * 
 * @author wumingkun
 * @version 1.0.0
 * @Description
 */

package com.demo.user.user_service;

/**
 * @author wumingkun
 *
 */
public class UserSerivce {
	public void add(Object obj){
		System.out.println(obj+" add....");
	}
}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.demo.user</groupId>
  <artifactId>user-service</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>bundle</packaging>

  <name>user-service</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<configuration>
					<instructions>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>



user-action:

Activator

/**

 * @author wumingkun
 * @version 1.0.0
 * @Description
 */

package com.demo.user.user_action;

import java.lang.reflect.Method;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import com.demo.user.user_service.UserSerivce;

/**
 * @author wumingkun
 *
 */
public class Activator implements BundleActivator {

	/* (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext context) throws Exception {
		try {
			Class clz=Class.forName("com.demo.user.user_model.User");
			Object obj=clz.newInstance();
			Method setIdMethod=clz.getMethod("setId", new Class[]{int.class});
			setIdMethod.invoke(obj,1);
			Method setNameMethod=clz.getMethod("setName", new Class[]{String.class});
			setNameMethod.invoke(obj,"张三");
			UserSerivce serivce=new UserSerivce();
			serivce.add(obj);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/* (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext context) throws Exception {

	}

}


</pre>pom.xml<pre name="code" class="html"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.demo.user</groupId>
	<artifactId>user-action</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>bundle</packaging>

	<name>user-action</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.osgi</groupId>
			<artifactId>org.osgi.core</artifactId>
			<version>4.2.0</version>
		</dependency>
		
		<dependency>
			<groupId>com.demo.user</groupId>
			<artifactId>user-service</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<configuration>
					<instructions>
					<Bundle-Activator>com.demo.user.user_action.Activator</Bundle-Activator>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>



分析:

从user-action/pom.xml中可以看到它仅仅依赖了user-service,再看Activator.java文件中,import部份:

import java.lang.reflect.Method;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import com.demo.user.user_service.UserSerivce;

没有导入 com.demo.user.user_model,

查看MANIFEST.MF

Manifest-Version: 1.0
Bnd-LastModified: 1405153337259
Build-Jdk: 1.5.0_22
Built-By: Administrator
Bundle-Activator: com.demo.user.user_action.Activator
Bundle-ManifestVersion: 2
Bundle-Name: user-action
Bundle-SymbolicName: com.demo.user.action
Bundle-Version: 0.0.1.SNAPSHOT
Created-By: Apache Maven Bundle Plugin
Export-Package: com.demo.user.user_action;version="0.0.1.SNAPSHOT";uses:
 ="org.osgi.framework"
Import-Package: com.demo.user.user_service;version="[0.0,1)",org.osgi.fr
 amework;version="[1.5,2)"
Tool: Bnd-2.1.0.20130426-122213

它只导入了 com.demo.user.user_service及org.osgi.framework

当运行Bundle时就报错了:

java.lang.ClassNotFoundException: com.demo.user.user_model.User


解决办法

user-action/pom.xml的maven-bundle-plugin中,加入Bundle使用的所有外部包

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.demo.user</groupId>
	<artifactId>user-action</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>bundle</packaging>

	<name>user-action</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.osgi</groupId>
			<artifactId>org.osgi.core</artifactId>
			<version>4.2.0</version>
		</dependency>
		
		<dependency>
			<groupId>com.demo.user</groupId>
			<artifactId>user-service</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<configuration>
					<instructions>
					<Bundle-Activator>com.demo.user.user_action.Activator</Bundle-Activator>
					<Import-Package> 
							com.demo.user.user_service,
							org.osgi.framework,
							com.demo.user.user_model
					</Import-Package>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

再次查看MANIFEST

Manifest-Version: 1.0
Bnd-LastModified: 1405154955217
Build-Jdk: 1.5.0_22
Built-By: Administrator
Bundle-Activator: com.demo.user.user_action.Activator
Bundle-ManifestVersion: 2
Bundle-Name: user-action
Bundle-SymbolicName: com.demo.user.action
Bundle-Version: 0.0.1.SNAPSHOT
Created-By: Apache Maven Bundle Plugin
Export-Package: com.demo.user.user_action;version="0.0.1.SNAPSHOT";uses:
 ="org.osgi.framework"
Import-Package: com.demo.user.user_service;version="[0.0,1)",org.osgi.fr
 amework;version="[1.5,2)",com.demo.user.user_model
Tool: Bnd-2.1.0.20130426-122213


部署运行:

User [id=1, name=张三] add....




<think>在Android中,AIDL(AndroidInterface DefinitionLanguage)用于进程间通信(IPC)。通常,我们通过ServiceConnection绑定服务并获取AIDL接口的实例。但有可能需要通过反射访问AIDL服务,例如当无法直接访问AIDL接口类。注意:反射访问AIDL服务通常不推荐,因为它破坏了类型安全,且可能在不同Android版本上表现不一致。但在某些特殊场景(如插件化开发)中可能需要。步骤:1.获取服务的IBinder对象(通过ServiceConnection或Context#getSystemService等)。2.通过反射调用AIDL生成的Stub类的asInterface方法,将IBinder转换为接口对象。3.通过反射调用接口方法。以下是一个示例,假设我们有一个AIDL服务接口`IMyAidlInterface`,但我们无法直接访问其类(例如在插件中),我们只知道它的全限定名。首先,定义AIDL接口(通常在客户端和服务端共享): ```aidl// IMyAidlInterface.aidlpackagecom.example; interfaceIMyAidlInterface {intadd(inta,int b); }```编译后,会生成`IMyAidlInterface.Stub`和`IMyAidlInterface.Stub.Proxy`等类。现在,假设我们无法直接导入`com.example.IMyAidlInterface`,我们将通过反射调用。代码实现:```javapublicclass MainActivity extendsActivity {privatestaticfinal StringAIDL_INTERFACE_NAME= "com.example.IMyAidlInterface";private staticfinal StringSERVICE_ACTION ="com.example.MyAidlService";privateObjectremoteService; //通过反射获取的AIDL接口实例private ServiceConnection connection= newServiceConnection() {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {try{//1.获取Stub类的Class对象Class<?>stubClass= Class.forName(AIDL_INTERFACE_NAME +"$Stub");//2.获取asInterface方法Method asInterfaceMethod= stubClass.getMethod("asInterface", IBinder.class);//3.调用asInterface方法,将IBinder转换为接口对象remoteService= asInterfaceMethod.invoke(null,service);Log.d("Reflection", "Got AIDLinterface viareflection");}catch (Exception e) {Log.e("Reflection", "Failed toget AIDLinterface",e);}}@Overridepublicvoid onServiceDisconnected(ComponentNamename){remoteService= null;}};@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//绑定服务Intentintent =new Intent(SERVICE_ACTION);intent.setPackage(getPackageName()); //如果服务在同一个应用,否则指定包名bindService(intent, connection, Context.BIND_AUTO_CREATE);}@Overrideprotectedvoid onDestroy(){super.onDestroy();unbindService(connection);}//示例:通过反射调用AIDL接口的方法public voidonAddButtonClick(Viewview) {if(remoteService ==null){Toast.makeText(this, "Service notconnected", Toast.LENGTH_SHORT).show();return;}try{//获取add方法MethodaddMethod= remoteService.getClass().getMethod("add",int.class, int.class);//调用方法intresult =(int) addMethod.invoke(remoteService,3,5);Toast.makeText(this, "Result:" +result,Toast.LENGTH_SHORT).show();}catch (Exception e) {Log.e("Reflection", "Failed toinvoke addmethod",e);}}} ```注意事项:1.**安全性**:反射绕过了编译类型检查,调用方法需确保参数类型和数量正确。2. **性能**:反射调用比直接调用慢,应避免频繁使用。3.**兼容性**:AIDL生成的代码可能随Android版本变化,反射可能在不同版本上失效。4. **Proguard**:如果使用Proguard混淆,需确保AIDL接口的Stub类不被混淆(通常AIDL接口是默认保持的)。5. **异常处理**:反射调用会抛出大量检查异常,需要妥善处理。替代方案:如果可能,最好直接使用AIDL接口类。如果是因为模块化无法直接引用,可以考虑以下方案: -将AIDL接口定义在共享库中。 -使用HIDL(Android8.0+)或直接使用Binder(更底层)。通过反射访问AIDL服务是一种不得已而为之的方法,应谨慎使用。 §§相关问题§§:1.在Android中,AIDL服务的基本使用步骤是什么?2.如何防止AIDL接口被混淆?3.除了反射,还有哪些方法可以跨进程调用服务?4.在Android中,如何通过Binder直接实现IPC而不使用AIDL?</think># 通过反射访问 AIDL 服务的完整指南 在 Android 开发中,AIDL(Android Interface Definition Language)用于实现跨进程通信(IPC)。虽然通常我们通过接口直接调用 AIDL 服务,但有需要通过反射访问 AIDL 服务,特别是在以下场景: - 无法直接访问 AIDL 接口定义 - 动态加载服务实现 - 插件化开发中调用宿主服务 - 系统服务调用 ## 反射访问 AIDL 服务的核心步骤 1. **获取服务的 IBinder 对象** 2. **通过反射获取 AIDL Stub 类** 3. **调用 `asInterface()` 方法获取代理对象** 4. **反射调用服务方法** ## 完整代码实现 ```java public class AidlReflectionHelper { private static final String TAG = "AidlReflectionHelper"; /** * 通过反射调用 AIDL 服务 * * @param context 上下文 * @param serviceName 服务名称(如 "package") * @param aidlInterfaceClass AIDL 接口全限定类名(如 "android.content.pm.IPackageManager") * @param methodName 要调用的方法名 * @param params 方法参数 * @return 方法调用结果 */ public static Object invokeAidlService(Context context, String serviceName, String aidlInterfaceClass, String methodName, Object... params) { try { // 1. 获取系统服务管理器 Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager"); Method getServiceMethod = serviceManagerClass.getMethod("getService", String.class); // 2. 获取服务的 IBinder 对象 IBinder binder = (IBinder) getServiceMethod.invoke(null, serviceName); if (binder == null) { throw new RuntimeException("Service not found: " + serviceName); } // 3. 加载 AIDL Stub 类 Class<?> stubClass = Class.forName(aidlInterfaceClass + "$Stub"); // 4. 获取 asInterface 方法 Method asInterfaceMethod = stubClass.getMethod("asInterface", IBinder.class); // 5. 创建服务代理对象 Object serviceProxy = asInterfaceMethod.invoke(null, binder); // 6. 准备参数类型数组 Class<?>[] paramTypes = new Class[params.length]; for (int i = 0; i < params.length; i++) { paramTypes[i] = params[i].getClass(); } // 7. 获取目标方法 Method targetMethod = serviceProxy.getClass().getMethod(methodName, paramTypes); // 8. 调用服务方法 return targetMethod.invoke(serviceProxy, params); } catch (Exception e) { Log.e(TAG, "Error invoking AIDL service", e); throw new RuntimeException("Failed to invoke AIDL service", e); } } } ``` ## 使用示例:获取已安装应用列表 ```java public class MainActivity extends AppCompatActivity { private static final String PACKAGE_SERVICE = "package"; private static final String IPACKAGE_MANAGER = "android.content.pm.IPackageManager"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { // 获取已安装应用列表 Object result = AidlReflectionHelper.invokeAidlService( this, PACKAGE_SERVICE, IPACKAGE_MANAGER, "getInstalledApplications", 0, // flags getUserId() // user ID ); // 结果是一个 ParceledListSlice 对象 if (result != null) { List<ApplicationInfo> apps = (List<ApplicationInfo>) result; Log.d("InstalledApps", "Found " + apps.size() + " applications"); for (ApplicationInfo app : apps) { Log.d("AppInfo", app.packageName); } } } catch (Exception e) { Log.e("MainActivity", "Error getting installed apps", e); } } private int getUserId() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return UserHandle.myUserId(); } else { // 兼容旧版本 try { Method myUserIdMethod = UserHandle.class.getMethod("myUserId"); return (int) myUserIdMethod.invoke(null); } catch (Exception e) { return 0; // 默认用户 } } } } ``` ## 关键点解释 ### 1. 获取服务 IBinder - 使用 `ServiceManager.getService()` 获取系统服务的 IBinder 对象 - 系统服务名称列表: - `package` - 包管理器 - `window` - 窗口管理器 - `alarm` - 闹钟服务 - `activity` - Activity 管理器 ### 2. AIDL 类结构 - AIDL 接口编译后生成 `Stub` 和 `Proxy` 类 - `Stub.asInterface(IBinder)` 将 Binder 转换为接口代理 - 接口类名通常以 `I` 开头(如 `IPackageManager`) ### 3. 反射调用注意事项 - **参数类型匹配**:必须精确匹配方法签名 - **跨进程参数**:使用 `Parcelable` 或基本类型 - **异常处理**:捕获所有反射异常 - **性能考虑**:反射调用比直接调用慢 3-5 倍 ### 4. 权限要求 许多系统服务需要特定权限: ```xml <!-- 获取已安装应用列表 --> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- 访问其他服务可能需要 --> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> ``` ## 高级技巧:处理复杂返回类型 当返回类型是 AIDL 自定义类型: ```java // 获取 ParceledListSlice 的 getList() 方法 Class<?> parceledListSliceClass = Class.forName("android.content.pm.ParceledListSlice"); Method getListMethod = parceledListSliceClass.getMethod("getList"); // 调用 getList() 获取实际列表 Object result = AidlReflectionHelper.invokeAidlService(...); Object listSlice = getListMethod.invoke(result); List<ApplicationInfo> apps = (List<ApplicationInfo>) listSlice; ``` ## 安全注意事项 1. **权限验证**:服务端应验证调用者权限 ```java // 在 AIDL 服务实现中 @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) { // 验证调用者权限 if (getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Permission denied"); } return super.onTransact(code, data, reply, flags); } ``` 2. **输入验证**:所有参数都应验证有效性 3. **最小权限原则**:仅暴露必要方法 4. **混淆处理**:使用 `@Keep` 注解保留 AIDL 类 ## 替代方案:直接绑定服务 如果可能,优先使用标准 AIDL 绑定方式: ```java // 标准 AIDL 服务绑定 Intent serviceIntent = new Intent(); serviceIntent.setComponent(new ComponentName("com.example", "com.example.MyService")); bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE); private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IMyAidlInterface.Stub.asInterface(service); // 直接调用方法 } }; ``` ## 常见问题解决 **问题:`ClassNotFoundException` for Stub class** - 确保 AIDL 接口类在 classpath 中 - 检查 ProGuard 规则是否保留了 AIDL 类 **问题:`NoSuchMethodException`** - 验证方法名和参数类型是否匹配 - 使用 `getDeclaredMethods()` 调试可用方法 **问题:跨进程参数传递失败** - 确保参数实现 `Parcelable` - 复杂对象使用 `Bundle`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值