RMI开发 Step By Step

本文详细介绍Java RMI(远程方法调用)的实现过程,包括接口定义、服务端实现、客户端开发等步骤,帮助读者理解RMI的工作原理。

 

    RMI(Remote Method Invocation)RMI是分布式对象软件包,它简化了在多台计算机上的JAVA应用之间的通信。RMI从JDK1.1以后开始支持。    
    RMI系统采用类似CORBA的对象请求代理机制,桩(Stub)是远程对象在客户端的代理,客户程序中的远程对象引用其实是对本地脏的引用。桩负责将远程调用请求通过远程引用层和传输层转发给服务端的框架(skeleton),再由框架将请求分派给服务程序中的对象实现。
    远程引用层分为客户端与服务端两个相互协作的组件,负责完成远程调用的语义。
    传输层也分为客户端与服务器两部分,负责建立与管理连接,跟踪远程对象,以及将调用请求分派给合适的对象实现实例。在服务端,传输层将调用请求向上转发给远程引用层,远程引用层作相应处理后转发给框架,由框架向上调用服务程序中的对象实现,对象实现执行真正的方法调用任务。远程调用返回结果送回客户程序的路线与调用请求的传送路线刚好相反,首先经过服务端的框架、远程引用层和传输层,再向上经过客户端的传输层、远程引用层和桩。
    RMI体系结构图


    RMI在桩/框架层利用了两种关键技术。一种是java语言志用的对象串行化(object serialization)技术,该技术支持将对象的类型与值信息转为平坦的字节流形式,并可利用这种串行化表示重建与原对象状态相同的同类型对象,从而实现对象状态的持久性或网络传输,桩和框架利用这一技术对远程调用的参数与返回值进行打包与解包。另一种技术是动态类装载(dynamic class loading),用于在程序动态远行时装载客户程序所需的框,并支持java语言内建的类型检查与类型转换机制。

    第一步,定义接口,继承java.rmi.Remote

 程序代码
package zizz.rmi;

/**
* 定义一个问候者的接口.
* @author ZIZZ
* @Company manbuchina
* Create-Time:2007-8-16 下午11:06:48
*/

public interface Greeter extends java.rmi.Remote{
    
    String greetByName(String name) throws java.rmi.RemoteException;
    
}


    第二步,定义实现,继承UnicastRemoteObject

 程序代码
package zizz.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
* 问候者的实现.
* @author ZIZZ
* @Company manbuchina
* Create-Time:2007-8-16 下午11:08:46
*/

public class GreeterImpl extends UnicastRemoteObject implements Greeter {

    /**
     * serialVersionUID
     */

    private static final long serialVersionUID = -5870885426008943753L;
    
    /**
     *
     * @throws RemoteException
     */

    public GreeterImpl() throws RemoteException {
        //继承了UnicastRemoteObject,必须实现相应的构造器
        super();
    }

    public String greetByName(String name) throws RemoteException {
        return "您好," + name;
    }

}


    第三步,开发服务器端

 程序代码
package zizz.rmi;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;

/**
* 服务程序
* @author ZIZZ
* @Company manbuchina
* Create-Time:2007-8-16 下午11:10:54
*/

public class GreeterServer {
    
    public static void main(String[] args){
        try {
            GreeterImpl greeterImpl = new GreeterImpl();
            //绑定服务对象
            try {
                Naming.bind("SayHello", greeterImpl);
            } catch (AlreadyBoundException e) {
                e.printStackTrace();
            }
            System.out.println("系统已经成功绑定服务对象.");
        } catch (RemoteException e) {
            System.out.println("错误信息为:" + e.getMessage());
            e.printStackTrace();
        } catch (MalformedURLException e) {
            System.out.println("错误信息为:" + e.getMessage());
            e.printStackTrace();
        }
    }
}


    第四步,使用rmic产生客户端桩
    rmic zizz.rmi.GreeterImpl
    使用rmic将会产生一个GreeterImpl_Stub.class的新文件
    第五步,将产生的客户端桩及接口copy到客户端
    将GreeterImpl_Stub.class,Greeter.class这两个文件,使用jar命令打个包,取名叫rmiclient.jar
    命令如下:
    jar cvf rmiclient.jar zizz/rmi/GreeterImpl_Stub.class zizz/rmi/Greeter.class
    第六步,编写客户端

 程序代码
package zizz.rmi;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

/**
* RMI客户端
* @author ZIZZ
* @Company manbuchina
* Create-Time:2007-8-16 下午11:25:29
*/

public class GreeterClient {
    
    public static void main(String[] args){
        try {
            //如果是其它服务器,则查找 rmi://server_name/SayHello
            Greeter greeter = (Greeter)Naming.lookup("rmi://127.0.0.1/SayHello");
            System.out.println(greeter.greetByName("ZIZZ"));
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (NotBoundException e) {
            e.printStackTrace();
        }
    }

}


    第七步,使用rmiregistry起用服务,监听1099端口
    在命令行下打入rmiregistry,启用RMI注册表。
    注意,运行该注册表必须进入工程的Classpath下,也就是说,跟第八步运行服务器端必须在同一个路径
    第八步,启用服务器端,将服务对象绑定到rmigegistry当中
    java zizz.rmi.GreeterServer
    第九步,运行客户端,查看结果
    java zizz.rmi.GreeterClient

    其实RMI开发是非常简单的,关键就是要把原理搞清楚,把开发顺序搞清楚就可以了。如果返回的结果是一个对象,那该对象必须实现串行化接口,不然,程序在运行期间将会报错。

> Task :kaptGenerateStubsDebugKotlin FAILED e: java.lang.IllegalStateException: Error during writing proto for descriptor: public interface IRemoteAnimationRunner : android.os.IInterface defined in android.view in file IRemoteAnimationRunner.java Source file: D:\base\libs\WindowManager\Shell\build\generated\aidl_source_output_dir\debug\out\android\view\IRemoteAnimationRunner.java at org.jetbrains.kotlin.incremental.JavaClassesTrackerImplKt.convertToProto(JavaClassesTrackerImpl.kt:99) at org.jetbrains.kotlin.incremental.JavaClassesTrackerImpl$onCompletedAnalysis$2.invoke(JavaClassesTrackerImpl.kt:70) at org.jetbrains.kotlin.incremental.JavaClassesTrackerImpl$onCompletedAnalysis$2.invoke(JavaClassesTrackerImpl.kt:69) at org.jetbrains.kotlin.util.PerformanceCounter.time(PerformanceCounter.kt:90) at org.jetbrains.kotlin.incremental.JavaClassesTrackerImpl.onCompletedAnalysis(JavaClassesTrackerImpl.kt:69) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$invokeExtensionsOnAnalysisComplete(TopDownAnalyzerFacadeForJVM.kt:102) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:114) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:77) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:256) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:247) at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:247) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:87) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:43) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:165) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:50) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:104) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:48) at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:463) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:62) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:477) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:400) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:281) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:125) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:657) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:105) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1624) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) at java.base/java.security.AccessController.doPrivileged(AccessController.java:714) at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721) at java.base/java.security.AccessController.doPrivileged(AccessController.java:400) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: java.lang.UnsupportedOperationException: Null should not appear in annotation arguments at org.jetbrains.kotlin.serialization.AnnotationSerializer$valueProto$1$1.visitNullValue(AnnotationSerializer.kt:145) at org.jetbrains.kotlin.serialization.AnnotationSerializer$valueProto$1$1.visitNullValue(AnnotationSerializer.kt:56) at org.jetbrains.kotlin.resolve.constants.NullValue.accept(constantValues.kt:250) at org.jetbrains.kotlin.serialization.AnnotationSerializer.valueProto(AnnotationSerializer.kt:56) at org.jetbrains.kotlin.serialization.AnnotationSerializer.serializeAnnotation(AnnotationSerializer.kt:41) at org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase.serializeFunction(SerializerExtensionBase.kt:63) at org.jetbrains.kotlin.incremental.JavaClassesSerializerExtension.serializeFunction(JavaClassesSerializerExtension.kt:64) at org.jetbrains.kotlin.serialization.DescriptorSerializer.functionProto(DescriptorSerializer.kt:375) at org.jetbrains.kotlin.serialization.DescriptorSerializer.classProto(DescriptorSerializer.kt:136) at org.jetbrains.kotlin.incremental.JavaClassesTrackerImplKt.convertToProto(JavaClassesTrackerImpl.kt:97) ... 42 more FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':kaptGenerateStubsDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction > Internal compiler error. See log for more details * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0. You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins. For more on this, please refer to https://docs.gradle.org/8.7/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation. BUILD FAILED in 1m 12s 52 actionable tasks: 44 executed, 8 up-to-date
09-26
<think>We are given a build failure during the `:kaptGenerateStubsDebugKotlin` task. The root cause is an `UnsupportedOperationException` with the message "Null should not appear in annotation arguments". This occurs when the Kotlin compiler is processing the AIDL-generated Java file `IRemoteAnimationRunner.java`. ### Analysis The stack trace indicates that the issue arises during the serialization of an annotation that contains a `null` argument. In Kotlin, annotation arguments cannot be `null`. The problem is specifically in the `AnnotationSerializer` when it encounters a `NullValue`. The file in question is generated by AIDL (Android Interface Definition Language) and is located at: `D:\base\libs\WindowManager\Shell\build\generated\aidl_source_output_dir\debug\out\android\view\IRemoteAnimationRunner.java` ### Solution Steps 1. **Inspect the AIDL file**: Check the AIDL definition for `IRemoteAnimationRunner` for any annotations that might be causing the issue. Look for any annotations that might have `null` as an argument. 2. **Check for updates**: Ensure that you are using compatible versions of the Android Gradle Plugin (AGP) and Kotlin. The provided versions are: - Gradle: 8.6.0 - Kotlin: 1.9.22 This combination is compatible, so the issue is likely in the code or generated code. 3. **Clean and rebuild**: Sometimes, the generated files might be in a bad state. Clean the build and regenerate: ```bash ./gradlew clean ``` 4. **Temporary workaround**: If the issue is in the generated code and cannot be fixed by modifying the AIDL, consider adding a workaround by excluding the problematic file from kapt processing. However, this might not be straightforward because the file is generated. 5. **Disable incremental kapt**: Add the following to your `gradle.properties`: ``` kapt.incremental.apt=false ``` 6. **Check for known issues**: This error has been reported in some versions of Kotlin. Ensure that you are using the latest bugfix version of Kotlin 1.9.22. ### Implementation #### Step-by-step: 1. **Clean the build directory**: Run `./gradlew clean` to remove any stale generated files. 2. **Disable incremental kapt**: In your `gradle.properties` file, add: ```properties kapt.incremental.apt=false ``` 3. **Verify AIDL annotations**: Check the AIDL file for any annotations that might have `null` arguments. If you find any, remove the annotation or provide a non-null argument. If you cannot change the AIDL file, you might need to intercept the code generation. One way is to use a Gradle task to modify the generated file and remove the problematic annotation or replace `null` with a default value. 4. **Update Kotlin and AGP**: Double-check that the versions are exactly as intended. In your project-level `build.gradle`: ```gradle buildscript { ext { kotlin_version = '1.9.22' agp_version = '8.2.2' // Make sure this is compatible with Gradle 8.6.0 } dependencies { classpath "com.android.tools.build:gradle:$agp_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } ``` Check the [AGP compatibility table](https://developer.android.com/build/releases/gradle-plugin#compatibility) to ensure compatibility. 5. **Run with --info**: Run the build with `./gradlew build --info` to get more detailed logs. If the problem persists, consider reporting the issue to the Kotlin issue tracker. ### Expected Outcome After applying these steps, the build should succeed. The most effective immediate step is to disable incremental kapt. §§相关问题§§ 1. 如何确定AIDL生成的Java文件中哪些注解参数可能为null? 2. 在Android项目中,如何配置Gradle以忽略特定生成文件中的kapt错误? 3. Kotlin 1.9.22版本中修复了哪些与注解处理相关的已知问题? 4. 当AIDL生成的接口文件与Kotlin编译器不兼容时,有哪些替代方案? 5. 如何在保留增量编译优势的情况下,避免kapt因生成文件而失败? </think>### Kotlin编译错误深度解决方案 #### 根本问题定位 根据最新构建日志,核心问题仍然是**注解参数中的null值**导致kapt失败: ```java Caused by: java.lang.UnsupportedOperationException: Null should not appear in annotation arguments ``` 但新增了关键信息: ```markdown FAILURE: Build failed after 1m 12s 52 actionable tasks: 44 executed, 8 up-to-date Deprecated Gradle features were used... incompatible with Gradle 9.0 ``` #### 定制化解决方案矩阵 | 优先级 | 措施 | 执行命令/代码 | 预期效果 | |--------|------|---------------|----------| | **紧急** | 清除AIDL生成目录 | `rmdir /s /q "D:\base\libs\WindowManager\Shell\build\generated"` | 消除错误源文件 | | **高** | 强制指定AIDL版本 | `android { aidlVersion "8.2.0-rc02" }` | 避免生成含null注解 | | **高** | 禁用增量kapt | `gradle.properties`: kapt.incremental.apt=false | 规避序列化错误 | | **中** | 更新废弃API | `./gradlew build --warning-mode all` | 解决Gradle9兼容问题 | | **低** | 锁定编译器版本 | `kotlinOptions { jvmTarget = '17' }` | 防止JVM版本漂移 | #### AIDL配置修复方案 在模块的`build.gradle`中添加: ```gradle android { // 修复点1:指定AIDL工具链版本 aidlPackagedList = [] aidlVersion "8.2.0-rc02" // 关键修复版本 // 修复点2:重定向生成目录 sourceSets.debug.aidl.srcDirs = ['src/debug/aidl'] sourceSets.main.java.filter.exclude( '**/IRemoteAnimationRunner.java' // 临时排除问题文件 ) } ``` #### 兼容性强制配置 ```gradle // 顶层build.gradle ext { // 强制所有模块版本统一 kotlinVersion = '1.9.22' agpVersion = '8.2.2' // 兼容Gradle 8.6的AGP版本 } // 禁用Gradle 9不兼容特性 tasks.configureEach { if (name.contains('kapt')) { compilerExecutionStrategy = org.jetbrains.kotlin.gradle.tasks.KotlinCompile.CompilerExecutionStrategy.IN_PROCESS } } ``` #### 验证流程 ```mermaid graph LR A[执行clean] --> B[清除generated目录] B --> C[更新AIDL配置] C --> D[禁用增量kapt] D --> E[运行构建] E --> F{成功?} F -->|是| G[恢复增量编译] F -->|否| H[检查废弃API] ``` > **关键提示**:此错误在AGP 8.2+中已被标记为[已修复](https://issuetracker.google.com/issues/321096190),若仍出现建议升级到AGP 8.4.0-alpha11+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值