You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发中如何从ExecutorService.execute()获取返回值

解决方案:从后台线程获取认证Token

你的核心问题是同步方法阻塞主线程+多请求下结果无法安全返回,以下是三种适配Android场景的实现方案:


方案1:回调接口(Android传统异步模式)

通过定义回调接口,将异步任务的结果通过回调方法传递到主线程,天然支持多请求独立处理。

步骤1:在通信类中定义回调接口

public class LedGo {
    private ExecutorService executor = Executors.newSingleThreadExecutor();
    private Handler handler = new Handler(Looper.getMainLooper());
    private boolean connected = false;
    private String authToken;

    // 定义认证结果回调接口
    public interface AuthTokenCallback {
        void onSuccess(String authToken);
        void onFailure(String errorCode);
        void onNotConnected();
    }

    // 原connect方法保持不变
    public boolean connect(String ip, int port, SSLContext cf, Runnable onSuccess, Runnable onFailure) {
        // 你的原有连接逻辑
        return connected;
    }

步骤2:修改genAuthToken为异步方法

public void genAuthToken(String userName, String passwd, AuthTokenCallback callback) {
        if (!connected) {
            // 主线程回调未连接状态
            handler.post(callback::onNotConnected);
            return;
        }

        executor.execute(() -> {
            try {
                // 构造请求JSON
                JSONObject json = new JSONObject();
                json.put("func", "genAuthToken");
                json.put("userName", userName);
                json.put("passwd", passwd);

                // 发送请求并接收响应
                pw.println(json);
                String reply = scan.nextLine();

                // 解析响应
                JSONObject responseJson = new JSONObject(reply);
                int code = responseJson.getInt("code");
                if (code == 200) {
                    authToken = responseJson.getString("authToken");
                    // 切换到主线程返回成功结果
                    handler.post(() -> callback.onSuccess(authToken));
                } else {
                    handler.post(() -> callback.onFailure(String.valueOf(code)));
                }
            } catch (Exception e) {
                e.printStackTrace();
                handler.post(() -> callback.onFailure("请求异常:" + e.getMessage()));
            }
        });
    }
}

步骤3:在MainActivity中调用

LedGo client = new LedGo();
boolean connected = client.connect("192.168.1.4",1304,cf,()->{},()->{
    Snackbar.make(view,"Couldn't connect to server",Snackbar.LENGTH_SHORT)
            .setAnchorView(R.id.fab)
            .show();
});

// 调用异步认证方法,通过回调获取结果
client.genAuthToken("userName","passwd", new LedGo.AuthTokenCallback() {
    @Override
    public void onSuccess(String authToken) {
        // 主线程处理成功结果:存储Token、更新UI等
        Log.d("Auth", "获取Token成功:" + authToken);
    }

    @Override
    public void onFailure(String errorCode) {
        Snackbar.make(view,"认证失败:"+errorCode,Snackbar.LENGTH_SHORT)
                .setAnchorView(R.id.fab)
                .show();
    }

    @Override
    public void onNotConnected() {
        Snackbar.make(view,"未连接到服务器",Snackbar.LENGTH_SHORT)
                .setAnchorView(R.id.fab)
                .show();
    }
});

方案2:Future(适合后台任务间依赖场景)

如果需要在后台线程中等待结果(而非直接更新UI),可以用ExecutorService.submit()返回Future对象,通过它获取异步任务的结果。

修改通信类的genAuthToken方法

public Future<String> genAuthToken(String userName, String passwd) {
    if (!connected) {
        // 返回空结果的Future
        return CompletableFuture.completedFuture(null);
    }

    // 提交任务并返回Future
    return executor.submit(() -> {
        try {
            JSONObject json = new JSONObject();
            json.put("func", "genAuthToken");
            json.put("userName", userName);
            json.put("passwd", passwd);

            pw.println(json);
            String reply = scan.nextLine();

            JSONObject responseJson = new JSONObject(reply);
            int code = responseJson.getInt("code");
            if (code == 200) {
                authToken = responseJson.getString("authToken");
                return authToken;
            } else {
                return String.valueOf(code);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "请求异常:" + e.getMessage();
        }
    });
}

在MainActivity中使用(必须在后台线程调用future.get())

LedGo client = new LedGo();
boolean connected = client.connect("192.168.1.4",1304,cf,()->{},()->{
    Snackbar.make(view,"Couldn't connect to server",Snackbar.LENGTH_SHORT)
            .setAnchorView(R.id.fab)
            .show();
});

// 必须在后台线程处理Future,避免阻塞主线程
client.executor.execute(() -> {
    Future<String> future = client.genAuthToken("userName","passwd");
    try {
        String result = future.get(); // 阻塞当前后台线程,等待结果
        if (result == null) {
            handler.post(() -> Snackbar.make(view,"未连接到服务器",Snackbar.LENGTH_SHORT).show());
        } else if (result.equals(authToken)) {
            handler.post(() -> Log.d("Auth", "Token获取成功:" + result));
        } else {
            handler.post(() -> Snackbar.make(view,"认证失败:"+result,Snackbar.LENGTH_SHORT).show());
        }
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
        handler.post(() -> Snackbar.make(view,"请求出错:"+e.getMessage(),Snackbar.LENGTH_SHORT).show());
    }
});

方案3:Kotlin协程(现代Android首选)

如果项目支持Kotlin,协程可以更简洁地实现异步任务+主线程切换,代码可读性更高。

通信类(Kotlin实现)

class LedGo {
    private val executor = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
    private var connected = false
    private var authToken: String? = null

    // 原connect方法保持不变
    fun connect(ip: String, port: Int, cf: SSLContext, onSuccess: ()->Unit, onFailure: ()->Unit): Boolean {
        // 原有连接逻辑
        return connected
    }

    suspend fun genAuthToken(userName: String, passwd: String): String? {
        return withContext(executor) {
            if (!connected) return@withContext null
            try {
                val json = JSONObject().apply {
                    put("func", "genAuthToken")
                    put("userName", userName)
                    put("passwd", passwd)
                }
                pw.println(json)
                val reply = scan.nextLine()
                val responseJson = JSONObject(reply)
                val code = responseJson.getInt("code")
                if (code == 200) {
                    authToken = responseJson.getString("authToken")
                    authToken
                } else {
                    code.toString()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                "请求异常:${e.message}"
            }
        }
    }
}

MainActivity中调用(使用lifecycleScope自动绑定生命周期)

lifecycleScope.launch {
    val client = LedGo()
    val connected = client.connect("192.168.1.4",1304,cf, {}, {
        Snackbar.make(view,"Couldn't connect to server",Snackbar.LENGTH_SHORT)
                .setAnchorView(R.id.fab)
                .show()
    })
    if (connected) {
        val authToken = client.genAuthToken("userName","passwd")
        when {
            authToken == null -> Snackbar.make(view,"未连接到服务器",Snackbar.LENGTH_SHORT).show()
            authToken == client.authToken -> Log.d("Auth", "Token获取成功:$authToken")
            else -> Snackbar.make(view,"认证失败:$authToken",Snackbar.LENGTH_SHORT).show()
        }
    }
}

关键注意事项

  1. 禁止主线程执行网络操作:所有Socket读写必须放到ExecutorService或协程的后台线程中,否则会触发NetworkOnMainThreadException。
  2. 主线程更新UI:所有UI操作必须通过Handler、lifecycleScope等切换到主线程执行,避免线程安全问题。
  3. 多请求安全:以上三种方案均为每个请求独立处理结果,不会出现公共变量被多请求覆盖的问题。

内容的提问来源于stack exchange,提问作者Balpreet Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:02:10