当前位置:

首页 > 编程开发 > Android应用内HTTP服务器:AlertDialog交互处理

Android应用内HTTP服务器:AlertDialog交互处理

本文档介绍了如何在Android应用内部署HTTP服务器,并在接收到请求时通过AlertDialog与用户交互。通过监听AlertDialog的按钮点击事件,服务器能够根据用户的选择向客户端发送响应。同时,提供了一种简单的阻塞方法,确保用户交互完成后再发送响应,并讨论了潜在的性能影响。

Android应用内HTTP服务器:AlertDialog交互与响应处理

本文档介绍了如何在Android应用内部署HTTP服务器,并在接收到请求时通过AlertDialog与用户交互。通过监听AlertDialog的按钮点击事件,服务器能够根据用户的选择向客户端发送响应。同时,提供了一种简单的阻塞方法,确保用户交互完成后再发送响应,并讨论了潜在的性能影响。

在Android应用中集成HTTP服务器,并根据用户在AlertDialog中的选择来响应客户端请求,这涉及到多线程、UI交互和网络编程。以下将详细介绍实现步骤和注意事项。

创建HTTP服务器

首先,需要创建一个HTTP服务器。可以使用Java自带的HttpServer类来实现。

import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import android.os.Handler;
import android.os.Looper;
import android.app.AlertDialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatButton;
import android.widget.LinearLayout;
import android.util.Log;

public class HttpServerManager {

    private static final String TAG = "HttpServerManager";
    private HttpServer httpServer;
    private AlertDialog adminRegisterDialog;
    private View adminRegisterView;

    private static HttpServerManager instance;

    public static HttpServerManager getInstance() {
        if (instance == null) {
            instance = new HttpServerManager();
        }
        return instance;
    }

    private HttpServerManager() {
        try {
            InetSocketAddress address = new InetSocketAddress(8080);
            httpServer = HttpServer.create(address, 0);

            httpServer.createContext("/getDeviceRegister", new EchoGetHandlerForDeviceRegister());

            httpServer.setExecutor(null); // Use the default executor
            httpServer.start();

            Log.i(TAG, "HttpServer Start");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    static void parseQuery(String query, Map parameters) {
        if (query != null) {
            String pairs[] = query.split("[&]");
            for (String pair : pairs) {
                String param[] = pair.split("[=]");
                if (param.length > 1) {
                    parameters.put(param[0], param[1]);
                }
            }
        }
    }

    class EchoGetHandlerForDeviceRegister implements HttpHandler {

        @Override
        public void handle(HttpExchange he) throws IOException {
            // parse request
            Map parameters = new HashMap<>();
            URI requestedUri = he.getRequestURI();
            String query = requestedUri.getRawQuery();

            parseQuery(query, parameters);

            // Flag to control the while loop
            final boolean[] isClick = {false};
            final AppCompatButton[] allowButtonHolder = {null};


            new Handler(Looper.getMainLooper()).post(() -> {
                //SHOW DIALOG HERE
                AdminRegisterDialogResult result = showAdminRegisterDialog(he.getRemoteAddress());
                allowButtonHolder[0] = result.getAllowButton();

                //ALERT DILOG CLICK LISTENER
                result.getAllowButton().setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Log.i(TAG,"allowButton");
                        isClick[0] = true;
                        adminRegisterDialog.dismiss();
                    }
                });

                result.getNotAllowButton().setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Log.i(TAG,"not allowButton");
                        isClick[0] = true;
                        adminRegisterDialog.dismiss();
                    }
                });

            });

            // This is a busy-wait loop.  Consider alternatives.
            while (!isClick[0]) {
                Log.i(TAG, "in while loop");
                try {
                    Thread.sleep(100); // Avoid busy-waiting, but be careful with sleep in a handler
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            Log.i(TAG, "out while loop");

            // send response
            String response = "

Alert Dialog Clicked

"; for (String key : parameters.keySet()) response += key + " = " + parameters.get(key) + "\n"; he.sendResponseHeaders(200, response.length()); OutputStream os = he.getResponseBody(); os.write(response.toString().getBytes()); os.close(); } } public class AdminRegisterDialogResult { private AppCompatButton allowButton; private AppCompatButton notAllowButton; public AdminRegisterDialogResult(AppCompatButton allowButton, AppCompatButton notAllowButton) { this.allowButton = allowButton; this.notAllowButton = notAllowButton; } public AppCompatButton getAllowButton() { return allowButton; } public AppCompatButton getNotAllowButton() { return notAllowButton; } } public AdminRegisterDialogResult showAdminRegisterDialog(InetSocketAddress clientAdress){ Log.i(TAG, "showAdminRegisterDialog()"); if (adminRegisterDialog != null) adminRegisterDialog.cancel(); Context context = MainActivity.instance(); AlertDialog.Builder builder = new AlertDialog.Builder(context); LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); adminRegisterView = li.inflate(R.layout.register_dialog, null); builder.setView(adminRegisterView); builder.setCancelable(false); TextView deviceNameText = adminRegisterView.findViewById(R.id.deviceNameText); TextView infoText = adminRegisterView.findViewById(R.id.infoText); deviceNameText.setText(clientAdress.toString()); infoText.setText(R.string.register_admin_allow_text); AppCompatButton allowButton = adminRegisterView.findViewById(R.id.allowButton); AppCompatButton notAllowButton = adminRegisterView.findViewById(R.id.notAllowButton); adminRegisterDialog = builder.create(); adminRegisterDialog.show(); adminRegisterDialog.getWindow().setLayout(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); return new AdminRegisterDialogResult(allowButton, notAllowButton); } public void stop() { if (httpServer != null) { httpServer.stop(0); Log.i(TAG, "HttpServer Stop"); } } }

代码解释:

  • HttpServerManager 类负责创建和管理 HTTP 服务器实例。
  • EchoGetHandlerForDeviceRegister 类实现了 HttpHandler 接口,用于处理 /getDeviceRegister 路径的请求。
  • parseQuery 方法用于解析 URL 中的查询参数。
  • showAdminRegisterDialog 方法用于显示 AlertDialog,等待用户交互。
  • isClick 标志位用于控制 while 循环,确保用户点击按钮后才发送响应。

显示AlertDialog并等待用户交互

在 EchoGetHandlerForDeviceRegister 的 handle 方法中,使用 Handler 将显示 AlertDialog 的操作切换到主线程。这非常重要,因为UI操作必须在主线程中进行。

new Handler(Looper.getMainLooper()).post(() -> {
    //SHOW DIALOG HERE
    AdminRegisterDialogResult result = showAdminRegisterDialog(he.getRemoteAddress());

    //ALERT DILOG CLICK LISTENER
    result.getAllowButton().setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.i(TAG,"allowButton");
            isClick[0] = true;
            adminRegisterDialog.dismiss();
        }
    });

    result.getNotAllowButton().setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.i(TAG,"not allowButton");
            isClick[0] = true;
            adminRegisterDialog.dismiss();
        }
    });

});

使用while循环阻塞直到用户交互

为了确保在用户点击AlertDialog上的按钮后再发送响应,可以使用一个 while 循环进行阻塞。

final boolean[] isClick = {false};

while (!isClick[0]) {
    Log.i(TAG, "in while loop");
    try {
        Thread.sleep(100); // Avoid busy-waiting, but be careful with sleep in a handler
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Log.i(TAG, "out while loop");

注意: 这种方法使用了忙等待(busy-waiting),会消耗CPU资源。虽然添加了Thread.sleep(100)来降低CPU占用,但仍然不是最佳实践。在实际应用中,应该考虑使用更高效的线程同步机制,如 CountDownLatch 或 Semaphore。

发送响应

在 while 循环结束后,表示用户已经进行了交互,此时可以构建并发送响应。

// send response
String response = "

Alert Dialog Clicked

"; for (String key : parameters.keySet()) response += key + " = " + parameters.get(key) + "\n"; he.sendResponseHeaders(200, response.length()); OutputStream os = he.getResponseBody(); os.write(response.toString().getBytes()); os.close();

替代方案:使用 CountDownLatch

CountDownLatch 是一种更优雅的线程同步方式,可以避免忙等待。

import java.util.concurrent.CountDownLatch;

class EchoGetHandlerForDeviceRegister implements HttpHandler {

    @Override
    public void handle(HttpExchange he) throws IOException {
        // parse request
        Map parameters = new HashMap<>();
        URI requestedUri = he.getRequestURI();
        String query = requestedUri.getRawQuery();

        parseQuery(query, parameters);

        final CountDownLatch latch = new CountDownLatch(1);

        new Handler(Looper.getMainLooper()).post(() -> {
            //SHOW DIALOG HERE
            AdminRegisterDialogResult result = showAdminRegisterDialog(he.getRemoteAddress());

            //ALERT DILOG CLICK LISTENER
            result.getAllowButton().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i(TAG,"allowButton");
                    latch.countDown(); // Signal that the button has been clicked
                    adminRegisterDialog.dismiss();
                }
            });

            result.getNotAllowButton().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i(TAG,"not allowButton");
                    latch.countDown(); // Signal that the button has been clicked
                    adminRegisterDialog.dismiss();
                }
            });
        });

        try {
            latch.await(); // Wait for the latch to be counted down
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // send response
        String response = "

Alert Dialog Clicked

"; for (String key : parameters.keySet()) response += key + " = " + parameters.get(key) + "\n"; he.sendResponseHeaders(200, response.length()); OutputStream os = he.getResponseBody(); os.write(response.toString().getBytes()); os.close(); } }

代码解释:

  • CountDownLatch latch = new CountDownLatch(1); 创建一个计数器,初始值为1。
  • 在按钮的 OnClickListener 中,调用 latch.countDown(); 将计数器减1,当计数器为0时,latch.await(); 会立即返回。
  • latch.await(); 会阻塞当前线程,直到计数器为0。

总结与注意事项

  • 在Android应用中使用HTTP服务器需要处理多线程和UI交互的问题。
  • 确保UI操作在主线程中进行。
  • 避免使用忙等待,考虑使用 CountDownLatch 或 Semaphore 等线程同步机制。
  • 合理处理异常,例如 InterruptedException。
  • 仔细评估性能影响,避免阻塞主线程。
  • 在应用退出时,停止HTTP服务器以释放资源。

本教程提供了一种在Android应用内实现HTTP服务器并与AlertDialog交互的方法。通过选择合适的线程同步机制,可以有效地管理并发,并提供良好的用户体验。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发
相关文章 更多
ServBay安装配置详细教程与操作指南
ServBay安装配置详细教程与操作指南

新手入门 ServBay 本地开发环境,详解安装包下载、Dashboard 状态监控、Packages 组件安装、Services 服务控制及 Websites 项目配置。掌握 .servbay.config 版本管理与日志排查技巧,快速搭建稳定的 PHP、Node.js 等多语言开发环境。

codekit环境配置指南从安装到环境搭建完整教程
codekit环境配置指南从安装到环境搭建完整教程

详解 CodeKit 在 macOS 下的安装步骤、项目导入方法、Sass与JavaScript编译设置及浏览器自动刷新功能,助您快速搭建高效的前端开发环境。

codex安装windows 命令行完整操作教程
codex安装windows 命令行完整操作教程

详解Windows环境下安装OpenAI Codex CLI的步骤,包括WSL环境检查、Node.js/npm配置、npm全局安装命令及首次启动验证,适合开发者快速上手。

NativeRest环境配置要求与完整操作教程
NativeRest环境配置要求与完整操作教程

学习如何配置 NativeRest REST API 客户端。涵盖 Windows/macOS/Linux 安装后的工作区创建、环境变量管理、请求编辑及响应查看步骤,帮助开发者快速完成基础环境搭建与连通性测试。

CSS设置透明度的注意事项有哪些?opacity属性详解
CSS设置透明度的注意事项有哪些?opacity属性详解

深入解析CSS中设置透明度的核心属性opacity,剖析子元素继承、事件穿透、层叠上下文等关键注意事项,并提供与rgba、hsla的实用选型对比。

flutter页面传值到后台的方法及示例代码
flutter页面传值到后台的方法及示例代码

flutter页面传值到后台的完整实现方法及示例代码,帮助读者快速掌握相关技术要点。

Java 8至21新特性代码写法对比:Lambda、Record与Switch
Java 8至21新特性代码写法对比:Lambda、Record与Switch

本文通过具体的旧版与新版代码对比,详细剖析Java 8引入的Lambda表达式、Java 14/16引入的Record类,以及Java 12至21逐步演进完善的Switch表达式与模式匹配,展示代码简化路径与避坑要点。

AI智能体开发培训课程学什么及实战内容介绍
AI智能体开发培训课程学什么及实战内容介绍

系统梳理AI智能体开发培训的核心知识模块、技术栈选型与典型实战项目,解析低代码平台与纯代码框架的差异,提供从零构建可落地智能体的完整学习与实施路径。

Java子类未实现抽象方法编译错误修复指南
Java子类未实现抽象方法编译错误修复指南

针对Java开发中常见的“子类未实现抽象方法”编译错误,深入分析报错原因,提供重写实现、声明抽象子类两种标准修复路径,并总结参数签名、访问修饰符等典型避坑要点。

解决PHP递归报错:max_nesting_level限制与内存溢出处理
解决PHP递归报错:max_nesting_level限制与内存溢出处理

遇到PHP递归报错时,不要盲目调大max_nesting_level。本文教你区分Xdebug限制、内存耗尽和正则递归错误,提供代码级的终止条件优化与迭代替代方案,彻底解决栈溢出问题。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

PDF教程
PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

Mac软件 更多
Shapr3D macOS版
Shapr3D macOS版
Mac

Shapr3D是一款面向工业设计、机械工程、建筑概念和三维打印工作流的CAD软件。Mac版采用Parasolid建模内核,支持草图约束、实体建模、工程图、可视化渲染及常见CAD格式交换,并可通过账户在多台设备之间同步项目。

REAPER macOS版
REAPER macOS版
Mac

REAPER是Cockos开发的数字音频工作站,提供多轨音频与MIDI录制、剪辑、处理、混音和母带制作工具。Mac版兼容Intel与Apple芯片,支持AU、VST、VST3、CLAP等插件格式,并提供高度可定制的工作流程。

Ableton Live macOS版
Ableton Live macOS版
Mac

Ableton Live 是面向音乐制作人与现场表演者的数字音频工作站,提供编曲视图、独具特色的现场视图、音频录制、MIDI创作、实时变速、乐器及效果器。Mac版原生支持Apple芯片,并可连接音频接口、MIDI控制器和第三方插件。

WINDOWS 更多
3dmax(3ds max)
3dmax(3ds max)
Windows

Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。