ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity与Android双向通信实战:基于AndroidJavaProxy的aar插件集成与回调处理

Unity与Android双向通信实战:基于AndroidJavaProxy的aar插件集成与回调处理 1. 项目概述与核心价值最近在做一个Unity项目需要深度集成一个Android端的硬件SDK对方只提供了一个aar包。核心需求是Unity不仅要能调用Android里的方法还要能稳定、高效地接收来自Android原生层主动发起的各种事件通知比如设备连接状态变化、实时数据流等。这可不是简单的AndroidJavaClass.CallStatic就能搞定的它涉及到双向通信尤其是从Android到Unity的回调机制。如果你也在头疼Unity和Android插件交互时怎么让Android那边的事件能顺利“通知”到Unity的C#脚本里那么这次基于AndroidJavaProxy的实战经验就是为你准备的。AndroidJavaProxy是Unity提供的一个关键桥梁它允许你在C#侧创建一个Java接口的代理实现。当Android原生代码调用这个接口的方法时实际上会触发你C#里对应的方法。这完美解决了“Android主动回调Unity”的难题是实现深度交互的基石。整个方案的核心就是围绕如何正确地使用AndroidJavaProxy并处理好aar包的集成、接口定义、生命周期管理以及那些容易踩坑的线程和内存问题。我会从原理拆解到一行行代码实现把集成过程中的所有细节和避坑指南都摊开来讲清楚。无论你是Unity开发者需要对接Android SDK还是Android开发者想为Unity提供插件这篇文章都能给你一套可复现的可靠方案。2. 交互原理与AndroidJavaProxy深度解析2.1 Unity与Android通信的两种基本模式在深入AndroidJavaProxy之前我们必须先理清Unity与Android特指Android应用或库交互的两种基本方向这决定了我们技术方案的选型。第一种是Unity调用AndroidJava/Kotlin。这是最常用、最直接的方式。Unity通过AndroidJavaClass和AndroidJavaObject这两个核心类利用JNIJava Native Interface桥接可以直接实例化Java对象、调用静态或实例方法、访问字段。这就像是Unity作为“外部调用者”向Android虚拟机VM发送指令。例如调用一个Android SDK的初始化方法或者获取系统信息都属于这个范畴。这种方式是单向的、同步或异步的调用主动权在Unity。第二种则是Android主动回调UnityC#。这是本次要解决的核心痛点。当Android原生层发生某些事件如传感器数据更新、网络状态变化、第三方SDK的异步回调时它需要一种机制来“通知”处于上层的Unity逻辑。由于Unity的C#脚本运行在自身的Mono或IL2CPP环境中Android的Java线程无法直接调用C#函数。这就需要建立一个反向的通道让Android能以一种它“认识”的方式即Java接口调用来触发Unity中的逻辑。AndroidJavaProxy就是Unity官方为这种场景设计的解决方案。2.2 AndroidJavaProxy的工作原理与本质AndroidJavaProxy类的本质是在C#侧动态实现一个指定的Java接口。听起来有点绕我们来拆解一下定义Java接口首先在Android原生代码Java/Kotlin中你需要定义一个接口。这个接口声明了回调方法例如void onDataReceived(String data)或void onConnectionStateChanged(int state)。这个接口是双方通信的“协议”或“契约”。C#实现代理在Unity的C#脚本中你创建一个类继承自AndroidJavaProxy并在构造函数中传入上述Java接口的完整类名如“com.example.sdk.CallbackListener”。然后你在这个C#类里用相同的方法名和签名参数类型、返回类型去实现那些接口方法。JNI魔法当你将这个C#代理类的实例传递给Android时比如作为参数传给某个Android方法Unity底层会通过JNI生成一个实现了对应Java接口的“代理对象”。这个代理对象在Java虚拟机里看起来就是一个普通的Java对象但它内部的方法调用会被JNI转发回你C#类中实现的方法。完成回调当Android原生代码在某个时刻可能在任意线程调用这个“代理对象”的接口方法如onDataReceived时调用会通过JNI层穿越边界最终执行你在C#里写的onDataReceived方法。这样事件就从Android侧传递到了Unity侧。重要提示AndroidJavaProxy实现的方法是在非Unity主线程通常是Android的UI线程或其它工作线程上被调用的。这意味着你不能在这些回调方法里直接访问Unity的GameObject、修改Transform或调用UnityEngine的API少数线程安全的除外如Debug.Log。必须通过UnityEngine.Dispatcher如UnityMainThreadDispatcher插件或UnityEngine.WSA.Application.InvokeOnAppThread等机制将任务派发回主线程执行。这是新手最容易导致崩溃或诡异Bug的地方。2.3 为何选择AndroidJavaProxy而非其他方案你可能会问除了AndroidJavaProxy有没有别的办法比如用UnityPlayer.UnitySendMessage或者AndroidJNI直接搞我们来对比一下UnitySendMessage这是Unity早期提供的一个简单方法允许Android通过UnityPlayer类向指定的GameObject发送消息。它最大的问题是性能低下、类型支持极其有限只能传一个字符串参数、且严重依赖GameObject的名字和场景状态。在复杂的、高频的、需要传递结构化数据的回调场景中它完全不够用也不够优雅。直接使用AndroidJNI这是最底层、最灵活的方式但复杂度极高。你需要手动处理JNI的环境JNIEnv、方法ID、签名、局部引用和全局引用等代码冗长且极易出错特别是内存泄漏问题难以排查。除非有极端性能要求或非常特殊的集成需求否则不推荐。AndroidJavaProxy它封装了复杂的JNI操作提供了面向对象的、类型相对安全的交互方式。你就像在实现一个普通的C#接口一样简单同时它能处理基本数据类型、字符串、数组甚至AndroidJavaObject的传递。在可维护性、开发效率和可靠性上它是Unity官方推荐的、用于实现Java到C#回调的首选方案。因此对于需要集成aar包并处理其异步事件的场景基于AndroidJavaProxy的方案在复杂性、性能和可维护性上取得了最佳平衡。3. 实战准备aar包集成与Android环境配置3.1 理解aar包与集成方式aarAndroid Archive是Android库的二进制分发格式它包含了编译后的代码classes.jar、资源文件res、清单文件AndroidManifest.xml以及可能的本地库.so文件。相比jar包aar能更好地封装Android库的所有内容。在Unity中集成aar包通常有两种主流方式Plugins/Android目录放置这是最直接的方式。将你的aar文件例如sdk-release.aar直接放入Unity项目的Assets/Plugins/Android目录下。如果该目录不存在就依次创建。Unity在构建Android APK时会自动将这个aar包合并到最终的Android工程中。这种方式简单粗暴适合纯二进制依赖无需修改Android工程配置。通过Gradle依赖管理推荐对于更复杂的集成特别是aar包本身还依赖其他第三方库如OkHttp, Gson等时使用Gradle是更专业的选择。你需要创建一个mainTemplate.gradle文件位于Assets/Plugins/Android来定制Unity的构建流程。在这个文件中你可以像在标准Android Studio项目中一样在dependencies块里添加对aar文件的依赖。优点能处理传递依赖方便管理版本冲突可以利用Gradle的丰富功能。缺点配置稍复杂需要了解Gradle基础。如何选择如果SDK提供方给的aar是独立的、无其他依赖的用方式一就够了。如果SDK文档明确说明需要额外依赖或者你遇到了类找不到ClassNotFoundException的错误就应该采用方式二。本次实战我们先从简单的目录放置开始后续遇到问题再引入Gradle。3.2 创建Android Studio模块与定义回调接口即使你最终拿到的是aar理解其内部结构也很有帮助。很多时候SDK提供方会给你一个示例Android工程。你需要关注其中定义的回调接口。假设SDK提供了一个核心类com.example.hardware.SdkManager它有一个初始化方法要求传入一个监听器// 这是Java代码通常在SDK提供的文档或示例中 package com.example.hardware; public class SdkManager { public interface DataCallback { void onDataUpdate(String jsonData); // 数据更新回调 void onError(int errorCode, String message); // 错误回调 } public void initialize(Context context, DataCallback callback) { // ... 初始化逻辑并持有callback引用 } }你的任务就是要在Unity的C#侧创建一个能“冒充”这个DataCallback接口的实例。所以准确拿到这个接口的完整类名com.example.hardware.SdkManager$DataCallback注意内部类的$符号和方法签名是第一步也是最重要的一步。记错一个字母回调都无法建立。3.3 Unity项目基础设置在Unity Editor中进行如下设置确保项目能正确构建Android应用并支持与Java代码交互切换平台打开File - Build Settings在平台列表中选择Android然后点击Switch Platform。Player SettingsOther Settings区域Package Name设置一个符合Android规范的包名如com.yourcompany.yourgame。Minimum API Level根据你的SDK要求设置通常至少API Level 21 (Android 5.0)。Target API Level建议设置为最新的稳定版或与测试设备匹配的版本。Publishing Settings区域如果需要确保Minify选项如ProGuard或R8的配置不会混淆或移除你的回调接口类。通常需要在自定义ProGuard规则文件中添加-keep规则。例如-keep class com.example.hardware.SdkManager$DataCallback { *; }。如果SDK提供方有专门的混淆规则文件一并放入Assets/Plugins/Android目录下Unity会将其合并。4. 核心实现C#侧的AndroidJavaProxy与事件派发4.1 创建AndroidJavaProxy代理类在Unity项目中创建一个C#脚本例如AndroidCallbackProxy.cs。这个类将继承AndroidJavaProxy并实现我们在3.2节中确定的Java回调接口。using UnityEngine; public class AndroidCallbackProxy : AndroidJavaProxy { // 构造函数中传入Java回调接口的完整类名 public AndroidCallbackProxy() : base(com.example.hardware.SdkManager$DataCallback) { // 基类构造函数会处理JNI代理的创建 } // 实现接口中的 onDataUpdate 方法 // 方法名、参数类型必须与Java接口完全一致 public void onDataUpdate(string jsonData) { Debug.Log($[AndroidCallbackProxy] 收到原始数据: {jsonData}); // 注意此方法在Android线程调用不能直接操作Unity对象。 // 将数据和事件类型派发到主线程处理器 MainThreadDispatcher.Instance?.Enqueue(() { // 现在在主线程了可以安全处理 EventManager.Instance?.DispatchDataUpdate(jsonData); }); } // 实现接口中的 onError 方法 public void onError(int errorCode, string message) { Debug.LogError($[AndroidCallbackProxy] SDK错误代码: {errorCode}, 信息: {message}); MainThreadDispatcher.Instance?.Enqueue(() { EventManager.Instance?.DispatchError(errorCode, message); }); } }关键点解析基类构造函数base(“完整接口类名”)是固定写法它告诉Unity底层要代理哪个Java接口。方法签名一致C#方法的名称、参数数量、参数类型必须与Java接口方法一一对应。string对应java.lang.Stringint对应intbool对应boolean等。如果Java方法返回voidC#方法也返回void。线程安全我在每个回调方法里做的第一件事就是通过一个假设存在的MainThreadDispatcher.Instance.Enqueue方法将实际的处理逻辑封装成一个Action丢到主线程队列去执行。这是强制要求否则在回调里直接操作Unity对象十有八九会崩溃。4.2 实现主线程调度器MainThreadDispatcher上面用到了主线程调度器这是一个非常重要的基础设施。因为Unity的API不是线程安全的。这里给出一个简单但可靠的实现using System; using System.Collections.Generic; using UnityEngine; public class MainThreadDispatcher : MonoBehaviour { private static MainThreadDispatcher _instance; private readonly QueueAction _executionQueue new QueueAction(); public static MainThreadDispatcher Instance { get { if (_instance null) { // 尝试查找场景中已存在的实例 _instance FindObjectOfTypeMainThreadDispatcher(); if (_instance null) { // 创建一个新的GameObject来挂载它 GameObject go new GameObject(MainThreadDispatcher); _instance go.AddComponentMainThreadDispatcher(); DontDestroyOnLoad(go); // 跨场景不销毁 } } return _instance; } } void Awake() { if (_instance ! null _instance ! this) { Destroy(this.gameObject); return; } _instance this; DontDestroyOnLoad(this.gameObject); } // 由其他线程如Android回调线程调用将任务加入队列 public void Enqueue(Action action) { lock (_executionQueue) // 加锁保证线程安全 { _executionQueue.Enqueue(action); } } // 在Unity主线程的Update中执行队列里的所有任务 void Update() { lock (_executionQueue) { while (_executionQueue.Count 0) { Action action _executionQueue.Dequeue(); try { action?.Invoke(); } catch (Exception e) { Debug.LogError($在主线程执行任务时发生异常: {e}); } } } } }将这个脚本挂载到一个在初始场景就存在且永不销毁的GameObject上或者通过上述代码的Instance属性让它自动创建。这样任何后台线程的回调都能通过Enqueue方法安全地将逻辑“邮寄”到主线程执行。4.3 初始化SDK并传递Proxy实例现在我们需要在Unity启动时初始化Android SDK并将我们创建的AndroidCallbackProxy实例传递过去。创建另一个C#脚本例如SdkBridge.cs。using UnityEngine; public class SdkBridge : MonoBehaviour { private AndroidJavaObject _sdkManagerInstance; private AndroidCallbackProxy _callbackProxy; void Start() { InitializeAndroidSdk(); } void InitializeAndroidSdk() { try { // 1. 创建回调代理实例 _callbackProxy new AndroidCallbackProxy(); // 2. 获取当前Android应用的Activity上下文 using (AndroidJavaClass unityPlayer new AndroidJavaClass(com.unity3d.player.UnityPlayer)) using (AndroidJavaObject currentActivity unityPlayer.GetStaticAndroidJavaObject(currentActivity)) { // 3. 找到SDK的Java类并创建实例或调用静态方法 // 假设SdkManager是一个单例有一个getInstance()方法 using (AndroidJavaClass sdkManagerClass new AndroidJavaClass(com.example.hardware.SdkManager)) { // 方式A调用静态方法获取实例 _sdkManagerInstance sdkManagerClass.CallStaticAndroidJavaObject(getInstance); // 方式B直接通过构造函数创建实例如果允许 // _sdkManagerInstance sdkManagerClass.CallAndroidJavaObject(new); if (_sdkManagerInstance ! null) { // 4. 调用初始化方法传入Activity和回调代理 _sdkManagerInstance.Call(initialize, currentActivity, _callbackProxy); Debug.Log(Android SDK 初始化成功回调代理已注册。); } else { Debug.LogError(无法获取SdkManager实例。); } } } } catch (System.Exception e) { Debug.LogError($初始化Android SDK时发生异常: {e.Message}\n{e.StackTrace}); } } // 示例从Unity主动调用SDK的方法 public void StartDataCollection() { if (_sdkManagerInstance ! null) { _sdkManagerInstance.Call(startCollecting); } } void OnDestroy() { // 清理资源 if (_sdkManagerInstance ! null) { _sdkManagerInstance.Call(release); _sdkManagerInstance.Dispose(); // 重要释放JNI引用 _sdkManagerInstance null; } // _callbackProxy 由GC管理通常无需手动Dispose除非它持有大量资源 } }操作意图与细节说明using语句用于包裹AndroidJavaObject和AndroidJavaClass。这确保了即使发生异常这些JNI对象的Dispose()方法也会被调用避免JNI局部引用泄漏这是保持应用稳定的关键技巧。获取Activity几乎所有Android SDK的初始化都需要一个Context通常是Activity。com.unity3d.player.UnityPlayer.currentActivity是Unity提供的当前运行的Activity。传递Proxy直接将C#的_callbackProxy对象作为参数传递给Java方法。Unity的JNI层会自动处理类型转换让Java端将其识别为一个实现了指定接口的对象。资源释放在OnDestroy中我们显式调用了SDK的释放方法如果有并对AndroidJavaObject调用了Dispose()。长期持有这些对象而不释放可能会导致内存问题。5. 高级话题、调试与性能优化5.1 处理复杂数据类型与自定义对象上面的例子回调参数是简单的string和int。如果Java接口方法需要传递或返回一个自定义的Java对象呢例如void onDeviceConnected(DeviceInfo deviceInfo)。策略一在C#中定义对应的类如果只需要读数据如果DeviceInfo只是用来传递数据你可以在C#中定义一个结构相似的类然后通过AndroidJavaObject的Get方法来获取字段值。public void onDeviceConnected(AndroidJavaObject deviceInfoJavaObj) { if (deviceInfoJavaObj ! null) { string deviceName deviceInfoJavaObj.Getstring(name); int deviceId deviceInfoJavaObj.Getint(id); // ... 获取其他字段 Debug.Log($设备连接: {deviceName}, ID: {deviceId}); // 同样派发到主线程处理 } }策略二在Android侧封装推荐更健壮的方式是在Android端如果你能修改aar或编写一个“胶水层”jar/aar将复杂对象“扁平化”。例如将DeviceInfo对象转换成JSON字符串再通过回调接口传递。这样C#侧只需要解析JSON即可避免了直接操作不透明的AndroidJavaObject也简化了跨语言的数据模型同步。5.2 调试技巧与常见问题排查与Android原生代码交互的调试比较棘手因为错误可能发生在Java层、JNI桥接层或C#层。充分利用Logcat在Unity构建时选择Development Build并勾选Script Debugging。通过Android Studio的Logcat或adb logcat命令查看日志。Unity的Debug.Log会输出到LogcatAndroid SDK自身的日志Log.d,Log.e也会在这里。过滤标签Unity和你的SDK标签是定位问题的首要手段。处理“JNI: Initd AndroidJavaObject with null ptr!”错误这通常意味着你尝试使用了一个已经被销毁Disposed或未正确初始化的AndroidJavaObject。确保你的对象在生命周期内有效并且用using语句或Dispose妥善管理。回调不触发检查以下几点接口类名是否正确大小写、包名、内部类符号$一个都不能错。方法签名是否匹配参数类型、数量、顺序必须完全一致。int和Integer在JNI中是不同的。Proxy实例是否被GC回收确保你的AndroidCallbackProxy实例被一个长期存在的C#对象如SdkBridge持有引用。如果它被垃圾回收了Java端的回调将失去目标。Android端是否正确持有引用确认你的SDK在初始化后确实保存了你传入的callback引用而不是在方法内部就丢弃了。使用try-catch包裹关键交互所有通过AndroidJavaObject/AndroidJavaClass进行的调用都应该用try-catch包裹并将异常信息详细打印出来这能快速定位是哪个方法调用出了问题。5.3 性能优化与内存管理要点减少跨语言调用每一次Call或Get都是一次JNI调用有一定开销。避免在频繁更新的循环如Update中进行大量的JNI操作。可以考虑在Android端聚合数据减少回调频率或者一次性传递批量数据。管理JNI对象生命周期局部引用在方法内部创建的AndroidJavaObject/AndroidJavaClass尽量使用using语句让其在离开作用域时自动释放。全局引用如果你需要长期持有某个Java对象比如SDK的单例可以将其赋值给一个成员变量。但要记得在不再需要时如OnDestroy调用Dispose()。警惕闭包捕获在回调方法或主线程派发的Action中如果捕获了AndroidJavaObject要确保这个对象的生命周期长于回调执行时间或者处理好可能的空引用。主线程派发器的优化MainThreadDispatcher的Update中执行队列任务。如果某一帧入队的任务过多可能导致主线程卡顿。可以考虑限制每帧执行的任务数量或者对任务进行优先级排序。对于高频但轻量的回调如传感器数据可以考虑在C#回调方法中先进行简单的数据缓冲或聚合再以较低的频率派发到主线程。6. 构建、部署与真机测试全流程6.1 构建APK前的最终检查在点击Build之前请对照此清单进行检查[ ]aar文件位置确认aar文件已放入Assets/Plugins/Android目录。如果使用Gradle确认mainTemplate.gradle配置正确。[ ]接口类名确认AndroidJavaProxy构造函数中的字符串与SDK文档完全一致。[ ]方法签名确认C#代理类中的方法名、参数类型与Java接口一一对应。[ ]主线程派发器确认MainThreadDispatcher脚本已挂载到场景中一个不会被销毁的GameObject上并且能正常获取Instance。[ ]初始化时机确认SdkBridge的初始化如Start在场景加载早期执行且早于任何需要用到SDK功能的逻辑。[ ]Player Settings确认Package Name、API Level、安装位置如自动等设置无误。[ ]ProGuard/混淆如果开启了Minify确认已添加必要的-keep规则以保留回调接口和SDK关键类。6.2 真机调试与问题复现将构建好的APK安装到真机上进行测试。连接设备与获取日志使用USB连接Android设备并开启USB调试。在命令行使用adb logcat -s Unity来过滤Unity的日志。为了同时看到Android SDK的日志可以使用adb logcat | grep -E “(Unity|你的SDK标签)”或者直接使用Android Studio的Logcat工具它功能更强大可以按进程、日志级别筛选。模拟事件触发根据SDK的功能在设备上执行相应操作如连接硬件、点击某个模拟按钮观察Unity控制台如果通过Wi-Fi调试或Logcat中是否有预期的回调日志输出。崩溃分析如果应用崩溃Logcat中通常会输出Java的异常堆栈信息FATAL EXCEPTION。仔细阅读这些信息它们能明确指出崩溃发生在Java层、JNI层还是Native层。常见的崩溃原因包括在主线程外调用Unity API、JNI引用错误、找不到类或方法。6.3 常见问题速查表问题现象可能原因排查步骤与解决方案初始化失败日志报ClassNotFoundException1. aar未正确集成。2. 类名拼写错误。3. ProGuard混淆移除了类。1. 检查aar文件位置或Gradle依赖。2. 仔细核对AndroidJavaClass构造函数中的类名字符串。3. 检查并添加ProGuard keep规则。回调方法从未被调用1. Proxy实例未被Java SDK持有。2. 回调接口方法签名不匹配。3. SDK本身的事件未触发。1. 确认将Proxy实例传给了SDK并确认SDK保存了它。2. 用javap -s查看Java接口方法签名与C#方法严格比对。3. 在Android端写一个测试程序验证SDK本身是否能正常触发回调。回调中操作Unity对象导致崩溃在非Unity主线程中调用了非线程安全的Unity API。确保所有对GameObject、Transform、UI等的操作都通过MainThreadDispatcher派发到主线程执行。应用运行一段时间后卡顿或崩溃JNI局部引用泄漏。检查代码确保所有AndroidJavaObject和AndroidJavaClass都在使用后及时Dispose()或包裹在using语句中。AndroidJavaProxy方法参数接收不到值C#方法参数类型与Java不匹配。Java的int对应C#的intjava.lang.String对应stringboolean对应bool。对于对象使用AndroidJavaObject接收。日志显示调用了回调但主线程没反应MainThreadDispatcher未正确初始化或Enqueue的逻辑有误。确认MainThreadDispatcher的GameObject在场景中处于激活状态且Instance能正确返回。在Enqueue前后加日志确认任务被加入队列并在Update中执行。7. 封装与复用创建易于管理的交互层对于一个需要多处使用SDK功能的项目直接将上述代码散落在各个脚本里会很难维护。一个好的实践是将所有的Android交互封装到一个统一的、易于管理的模块中。你可以创建一个AndroidNativeManager单例类它负责SDK的初始化和生命周期管理。持有AndroidCallbackProxy实例。提供一系列安全的静态方法供其他C#脚本调用这些方法内部处理线程派发。定义一系列C#事件event ActionT将来自Android的回转换装成C#标准事件。这样其他脚本只需要订阅这些事件完全不用关心AndroidJavaProxy和线程问题。例如public class AndroidNativeManager : MonoBehaviour { public static AndroidNativeManager Instance { get; private set; } public event Actionstring OnDataUpdated; public event Actionint, string OnErrorOccurred; private AndroidCallbackProxy _proxy; private AndroidJavaObject _sdk; void Awake() { /* 单例初始化 */ } void Start() { InitializeSdk(); } private void InitializeSdk() { /* 初始化逻辑同前 */ } // 内部类处理原始回调并触发C#事件 private class InternalCallbackProxy : AndroidJavaProxy { private AndroidNativeManager _manager; public InternalCallbackProxy(AndroidNativeManager manager) : base(...) { _manager manager; } public void onDataUpdate(string data) { MainThreadDispatcher.Instance.Enqueue(() { _manager.OnDataUpdated?.Invoke(data); // 在主线程触发事件 }); } // ... 其他回调方法 } // 供外部调用的安全方法 public void SafeCallSdkMethod() { if (MainThreadDispatcher.IsMainThread()) { _sdk?.Call(someMethod); } else { MainThreadDispatcher.Instance.Enqueue(() _sdk?.Call(someMethod)); } } }这样业务逻辑代码就会变得非常干净void OnEnable() { AndroidNativeManager.Instance.OnDataUpdated HandleData; } void OnDisable() { AndroidNativeManager.Instance.OnDataUpdated - HandleData; } void HandleData(string json) { /* 安全地在主线程处理数据 */ }这种架构分离了底层交互细节和上层业务逻辑大大提升了代码的可读性、可测试性和可维护性。当需要更换或升级SDK时你只需要修改AndroidNativeManager这一层而不会影响到整个项目。
返回列表