Unity中C#如何等待Android插件的异步I/O操作?
在Unity中让C#等待Android插件异步操作的可行方案
我之前也踩过这个坑,Unity的C#代码没法直接阻塞等待Android插件的异步操作——毕竟Java侧的异步逻辑是跑在自己的线程里的,硬卡主线程只会让Unity直接卡死。核心思路是利用Unity提供的跨端回调机制,配合C#的同步工具来实现“等待”效果,下面给你两种靠谱的实现方式:
方案一:用TaskCompletionSource实现异步等待(推荐现代C#风格)
这种方式贴合C#的异步编程范式,代码更简洁易维护,还能方便处理异常。
第一步:Android插件侧代码(Java)
首先要在Android插件里完成异步操作后,通过UnityPlayer.UnitySendMessage把结果回调给Unity的C#代码:
import android.os.AsyncTask; import com.unity3d.player.UnityPlayer; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class AndroidAsyncHandler { // 单例模式方便Unity调用 private static AndroidAsyncHandler instance; public static AndroidAsyncHandler getInstance() { if (instance == null) { instance = new AndroidAsyncHandler(); } return instance; } // 异步执行HTTP Get,完成后回调Unity public void performAsyncGet(String url, String unityGameObjName, String callbackMethod) { new AsyncTask<String, Void, String>() { @Override protected String doInBackground(String... params) { try { URL urlObj = new URL(params[0]); HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection(); conn.setRequestMethod("GET"); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } reader.close(); return sb.toString(); } else { return "Error: HTTP Response Code " + conn.getResponseCode(); } } catch (Exception e) { return "Error: " + e.getMessage(); } } @Override protected void onPostExecute(String result) { // 把结果回调给Unity指定的GameObject和方法 UnityPlayer.UnitySendMessage(unityGameObjName, callbackMethod, result); } }.execute(url); } }
第二步:Unity侧C#代码
用TaskCompletionSource把Android的回调转换成C#的Task,实现异步等待:
using UnityEngine; using System.Threading.Tasks; public class AndroidAsyncWaiter : MonoBehaviour { private TaskCompletionSource<string> _asyncTaskSource; private void Awake() { // 确保这个GameObject不被销毁,否则回调会丢失 DontDestroyOnLoad(gameObject); } // 对外暴露的异步方法,调用后会等待Android操作完成 public async Task<string> WaitForAndroidAsyncOperation(string targetUrl) { _asyncTaskSource = new TaskCompletionSource<string>(); // 获取Android插件实例并调用异步方法 AndroidJavaClass handlerClass = new AndroidJavaClass("com.yourpackage.AndroidAsyncHandler"); AndroidJavaObject handlerInstance = handlerClass.CallStatic<AndroidJavaObject>("getInstance"); handlerInstance.Call("performAsyncGet", targetUrl, gameObject.name, "OnAndroidOperationCompleted"); // 等待Task完成并返回结果 string result = await _asyncTaskSource.Task; _asyncTaskSource = null; return result; } // 这个方法是Android回调的目标,必须是public且参数匹配 public void OnAndroidOperationCompleted(string result) { if (_asyncTaskSource != null && !_asyncTaskSource.Task.IsCompleted) { if (result.StartsWith("Error:")) { _asyncTaskSource.SetException(new System.Exception(result)); } else { _asyncTaskSource.SetResult(result); } } } // 测试调用示例 public async void TestAsyncWait() { try { string response = await WaitForAndroidAsyncOperation("https://example.com"); Debug.Log("Android异步操作结果: " + response); } catch (System.Exception e) { Debug.LogError("异步操作失败: " + e.Message); } } }
方案二:用协程+标志位实现等待(适合传统Unity开发习惯)
如果你的项目更偏向Unity传统协程开发,也可以用标志位配合协程循环检查来实现等待:
using UnityEngine; using System.Collections; public class AndroidCoroutineWaiter : MonoBehaviour { private bool _isOperationDone; private string _operationResult; private bool _isErrorOccurred; private void Awake() { DontDestroyOnLoad(gameObject); } // 启动协程等待Android操作完成 public IEnumerator WaitForAndroidOperation(string targetUrl) { // 重置状态标志 _isOperationDone = false; _operationResult = null; _isErrorOccurred = false; // 调用Android插件异步方法 AndroidJavaClass handlerClass = new AndroidJavaClass("com.yourpackage.AndroidAsyncHandler"); AndroidJavaObject handlerInstance = handlerClass.CallStatic<AndroidJavaObject>("getInstance"); handlerInstance.Call("performAsyncGet", targetUrl, gameObject.name, "OnCoroutineOperationCompleted"); // 每帧检查操作是否完成,直到状态改变 while (!_isOperationDone) { yield return null; } // 处理结果 if (_isErrorOccurred) { Debug.LogError("Android操作出错: " + _operationResult); } else { Debug.Log("Android操作结果: " + _operationResult); } } // Android回调的目标方法 public void OnCoroutineOperationCompleted(string result) { _isOperationDone = true; if (result.StartsWith("Error:")) { _isErrorOccurred = true; } _operationResult = result; } // 测试调用示例 public void TestCoroutineWait() { StartCoroutine(WaitForAndroidOperation("https://example.com")); } }
关键注意事项
- Android侧必须用
UnityPlayer.UnitySendMessage回调Unity,这个方法是线程安全的,会把回调消息放到Unity主线程的消息队列里,避免跨线程问题。 - 回调目标的GameObject必须始终存在,所以一定要加
DontDestroyOnLoad,否则回调会丢失。 - 如果是WebSocket这种长连接场景,逻辑完全类似:Java侧的WebSocket回调(
onOpen、onMessage、onClose)都通过UnitySendMessage通知C#,C#侧用对应的同步机制处理(比如用TaskCompletionSource等待连接成功,用事件订阅接收消息)。 - 绝对不要在C#侧用
while(true)死循环等待,会直接卡死Unity主线程,必须用协程或者异步Task的方式。
内容的提问来源于stack exchange,提问作者Ariam1_catalist_number_666
相关产品推荐
相关产品推荐

