Android学习制作app(ESP8266-01S连接-简单制作)

一、理论

部分理论见arduino学习-CSDN博客和Android Studio安装配置_android studio gradle 配置-CSDN博客

以下直接上代码和效果视频,esp01S的收发硬件代码目前没有分享,但是可以通过另一个手机网络调试助手进行模拟。也可以直接根据我的代码进行改动自行使用,代码中已经对模块进行了详细注释。本人不是java开发专业人士,也是通过ai完成的。

使用以下文件需要完成AndroidStdio的安装和SDK,SDK插件、gradle的配置,详细可以见之前的文章。

1、主xml文件制作界面

通过linearlayout布局,制作简单的界面,app头部为标题,中间为按钮和text显示。

<?xml version="1.0" encoding="utf-8"?>
<!-- CYA开发,SmartOrderDishes内容,VX:18712214828 -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
<!--    头部-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:gravity="top"
        android:orientation="horizontal">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="SmartOrderDishes"
                android:background="#609E9245"
                android:gravity="center|left"
                android:paddingLeft="30dp"
                android:textSize="20sp"
                android:textStyle="bold"
                android:letterSpacing="0.2"
                android:drawableStart="@mipmap/ic_launcher"
                />
    </LinearLayout>
<!--    显示模块-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_weight="0.5"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <TextView
                android:layout_width="400dp"
                android:layout_height="match_parent"
                android:text="在连接ESP-01S WIFI后,等待LCD1602显示CanConnectServer。点击连接按钮,连接服务器"
                android:textSize="20dp"
                android:gravity="left"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </LinearLayout>
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">
            <Button
                android:onClick="Connect"
                android:layout_width="120dp"
                android:layout_height="60dp"
                android:layout_marginLeft="10dp"
                android:text="连接"
                />
            <Button
                android:onClick="OffConnect"
                android:layout_width="120dp"
                android:layout_height="60dp"
                android:layout_marginLeft="10dp"
                android:text="断开连接"
                />
        </LinearLayout>
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop="50dp">
            <TextView
                android:id="@+id/Show_Text"
                android:layout_width="wrap_content"
                android:layout_height="50dp"
                android:textSize="20sp"
                android:text="Wait Checking out!"
                android:gravity="center"/>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="horizontal"></LinearLayout>
</LinearLayout>

 2、主xml对应的java文件

此文件中,对socket连接和收发线程进行了使用,并且有两个按钮点击事件,和接收到服务器数据的弹窗和弹窗按钮点击事件。

package com.example.smartorderdishes;
/*
CYA开发,VX:18712214828
自动点餐系统安卓app:
1、主线程进行点击时间和线程侦听
2、手机连接ESP-01S的WIFI后点击连接即可连接ESP服务器。(通过8080端口和192.168.4.1默认服务器ip)
3、接收到数据后进行弹窗显示需要结算的桌面,和总金额。
4、弹窗中点击确定即可结算。ESP会受到数据包。*/
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";//主java文件TAG
    private SocketClient socketClient;//socket自定义库文件变量
    private TextView textView;//TextView标签变量
    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {//主java文件函数,只会运行一次
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        socketClient = new SocketClient(this);//变量对象初始化
        textView = findViewById(R.id.Show_Text);//获取标签id

        // 设置数据接收回调// 连接成功后启动持续监听
        socketClient.setDataReceivedCallback(new SocketClient.DataReceivedCallback() {
            @Override
            public void onDataReceived(String data) {
                runOnUiThread(() -> {
                    byte[]  DataPacket = socketClient.hexStringToByteArray(data);
                    int deskNum = ((DataPacket[1]&0xF0)/16)+1;
                    int priceCount = (DataPacket[1]*256+DataPacket[2])&0x0FFF;
                    showDialog(data);
                });
            }
        });

    }
    // 连接按钮点击事件
    public void Connect(View view) {
        // 连接到服务器(内部会自动启动接收循环)
        socketClient.connectToServer();
    }


    //断开连接按钮点击事件
    public void OffConnect(View view) {
        // 关闭连接
        socketClient.closeConnection();
    }

    // 显示弹窗
    private void showDialog(String data) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);//新建弹窗对象
        byte[]  DataPacket = socketClient.hexStringToByteArray(data);//传入的数据转化为字节数组
        int deskNum = ((DataPacket[1]&0xF0)/16)+1;//桌号获取
        int priceCount = (DataPacket[1]*256+DataPacket[2])&0x0FFF;//总金额获取
        builder.setTitle("桌号"+deskNum+",结算请求:");//弹窗标题
        builder.setMessage("共计总金额$" + priceCount+"是否结算!");//弹窗信息
        // 确定按钮
        builder.setPositiveButton("确认结算", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {//确认按钮点击事件
                Toast.makeText(MainActivity.this, "You clicked OK", Toast.LENGTH_SHORT).show();
                //发送十六进制数据
                String hexData = "EBAAFF90"; //发送结算成功数据包
                socketClient.sendHexData(hexData);

                textView.setText("桌号:" + deskNum+"结算,总金额$"+priceCount+"\n");//显示
                /*textView.setText("桌号:" + deskNum+"结算,总金额$"+priceCount+"\n"+
                            "Data:"+data);*/
            }
        });

        // 取消按钮
        builder.setNegativeButton("取消结算", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(MainActivity.this, "You clicked Cancel", Toast.LENGTH_SHORT).show();
            }
        });

        // 显示弹窗
        AlertDialog dialog = builder.create();
        dialog.show();
    }



}

 3、socket连接服务器、侦听数据包和发送数据包线程,Java文件

package com.example.smartorderdishes;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SocketClient {

    private static final String SERVER_IP = "192.168.4.1";//连接的指定IP
    private static final int SERVER_PORT = 8080;//连接服务器的指定端口
    private static final int CONNECTION_TIMEOUT = 5000;//连接超时时间 ms
    private static final int READ_TIMEOUT = 5000; // 新增读取超时时间 ms

    private Socket socket;//socket变量
    private BufferedOutputStream out;//输出缓冲区变量
    private BufferedInputStream in;//输入缓冲区变量
    private Context context;//
    private ExecutorService executorService;//单线程 用于连接服务器
    private ExecutorService receiverExecutor; // 独立线程池用于接收数据

    public SocketClient(Context context) {
        this.context = context;
        executorService = Executors.newSingleThreadExecutor();//单线程的执行器服务(Executor Service),用于管理和调度任务的执行
        receiverExecutor = Executors.newSingleThreadExecutor(); // 独立线程池 线程用于接收数据
    }

    // 连接服务器(修改后的代码)
    public void connectToServer() {
        executorService.execute(() -> {//线程提交不需要返回结果的任务
            try {//异常抛出
                socket = new Socket();//socket对象
                socket.connect(new InetSocketAddress(SERVER_IP, SERVER_PORT), CONNECTION_TIMEOUT);//socket连接,指定地址、端口和超时时间
                socket.setSoTimeout(READ_TIMEOUT); // 设置读取超时
                out = new BufferedOutputStream(socket.getOutputStream());//发送缓冲区对象
                in = new BufferedInputStream(socket.getInputStream());//接收缓冲区对象

                runOnUiThread(() -> {//runOnUiThread() 是 Activity 类中的一个方法 ,用于在主线程执行代码
                    Toast.makeText(context, "Connected to server", Toast.LENGTH_SHORT).show();
                    Log.d("SocketClient", "Connected to server");
                });

                // 连接成功后启动接收循环
                startReceivingData();

            } catch (IOException e) {
                runOnUiThread(() -> {
                    Toast.makeText(context, "Failed to connect: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                    Log.e("SocketClient", "Connection error: " + e.getMessage());
                });
            }
        });
    }

    // 发送十六进制数据
    public void sendHexData(String hexData) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                if (out != null && socket != null && !socket.isClosed()) {
                    try {
                        // 将十六进制字符串转换为字节数组
                        byte[] data = hexStringToByteArray(hexData);
                        out.write(data);//发送字节数组
                        out.flush();//发送完毕后,关闭发送
                        Log.d("SocketClient", "Sent (Hex): " + hexData);
                    } catch (IOException e) {
                        e.printStackTrace();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(context, "Failed to send data: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                } else {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(context, "Not connected to server", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        });
    }

    // 接收数据包(0xEB 0xXX 0xXX 0x90)
    // 启动接收循环
    private void startReceivingData() {
        receiverExecutor.execute(() -> {//通过单线程执行器,所有提交的任务都会按顺序在一个单独的线程中执行。
            Log.d("SocketClient", "Starting receive loop");
            try {
                while (!Thread.currentThread().isInterrupted()//用于检查当前线程是否已被中断的方法
                        && socket != null//检查 Socket 对象是否已经被初始化且不为 null。这个检查通常用于确保在尝试使用 Socket 进行网络通信之前,它已经被正确创建和配置。
                        && !socket.isClosed()//检查 Socket 对象是否被关闭
                        && in != null) {//确保输入流(InputStream)对象已经被正确初始化且不为 null,避免潜在的 NullPointerException
                    byte[] buffer = new byte[1024];//存储获取的数据
                    int bytesRead;//存储获取的数据长度
                    try {
                        bytesRead = in.read(buffer); // 阻塞读取(但设置了超时),返回数组长度
                        if (bytesRead == -1) {//未读取到数据
                            Log.d("SocketClient", "Connection closed by server");
                            break;
                        }
                        String hexResponse = byteArrayToHexString(buffer, bytesRead);//转字节数组换为字符串
                        Log.d("SocketClient", "Received (Hex): " + hexResponse);
                        if (isValidDataPacket(buffer, bytesRead)) {//判断是否符合数据包格式
                            Log.d("SocketClient", "Valid packet received");
                            // 触发回调
                            if (dataReceivedCallback != null) {//回调接口变量是否为空
                                dataReceivedCallback.onDataReceived(hexResponse);//回调不为空则运行回调函数,回调接收到的hex字符串
                            }
                        }
                    } catch (SocketTimeoutException e) {
                        Log.d("SocketClient", "Read timeout, retrying...");
                        continue;
                    } catch (IOException e) {
                        Log.e("SocketClient", "Read error: " + e.getMessage());
                        break;
                    }
                }
            } finally {
                Log.d("SocketClient", "Exiting receive loop");
            }
        });
    }

    // 检查数据包是否符合 0xEB 0xXX 0xXX 0x90 格式
    private boolean isValidDataPacket(byte[] data, int length) {
        if (length < 4) {
            Log.d("SocketClient", "Invalid packet: length < 4");
            return false;
        }
        boolean isValid = (data[0] == (byte) 0xEB) && (data[3] == (byte) 0x90);
        Log.d("SocketClient", "Data validity: " + isValid);
        return isValid;
    }

    // 关闭连接
    public void closeConnection() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    if (out != null) out.close();
                    if (in != null) in.close();
                    if (socket != null) socket.close();//关闭socket连接
                    Log.d("SocketClient", "Connection closed");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    // 回调接口,用于接收数据
    /*onDataReceived 是一个常见的回调方法名称,
    通常用于在数据接收到时通知监听器或处理数据。
    这个方法一般定义在一个接口中,
    并由实现该接口的类提供具体的数据处理逻辑。*/
    public interface DataReceivedCallback {
        void onDataReceived(String data);
    }

    // 设置回调接口
    private DataReceivedCallback dataReceivedCallback;//回调接口变量,回调接口为自定义,在上面已定义
    public void setDataReceivedCallback(DataReceivedCallback callback) {
        this.dataReceivedCallback = callback;
    }

    // 在主线程中运行代码
    private void runOnUiThread(Runnable action) {
        new android.os.Handler(context.getMainLooper()).post(action);
    }

    // 将十六进制字符串转换为字节数组
    public byte[] hexStringToByteArray(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    // 将字节数组转换为十六进制字符串
    private String byteArrayToHexString(byte[] bytes, int length) {
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < length; i++) {
            hex.append(String.format("%02X", bytes[i]));
        }
        return hex.toString();
    }


}

 

 4、app获取网络权限文件,以及启动文件配置文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!-- 配置网络权限 -->
    <!-- 互联网访问 -->
    <uses-permission android:name="android.permission.INTERNET" /> <!-- 访问网络状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 访问wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!-- 访问WiFi网络的信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 允许改变WiFi连接状态(如果需要的话) -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <!-- 从Android 6.0(API level 23)开始,获取WiFi信息也需要位置权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- 或者使用粗略的位置权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SmartOrderDishes"
        tools:targetApi="31">
        <!-- 配置Activity可启动输出权限 -->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

二、效果

说明:

1、esp8266-01S开启AP模式的多连接的Station模式。设定端口为8080,默认ip应该是192.168.4.1,网络SSID(名称)为ESP-01S。这些里面目前是固定的,配置即ESP8266作为热点和服务器,手机连接ESP8266的WIFI,然后作为客户端手机连接到ESP8266的服务器,进行通信

2、手机连接ESP8266的wifi后。等待配置完毕,然后进行服务器连接,连接完成手机会有信息提醒。

3、连接完成后,esp8266给手机发送0xEB 0xXX 0xXX 0x90的数据包DATA[4](下标从0开始),其中DATA[1]的高4bit作为桌位,低12bit为总金额。

4、接收到数据后手机app进行弹窗,点击确定后手机app界面text改变。并且向ESP8266发送EBAAFF90(HEX)数据作为结账完成的标志数据包。

效果视频

智能点餐系统开发视频

 

代码详解 

 

 

 

其他代码问题(个人理解):

首先执行主线程mainactivity.java内容,创建UI和监听按钮动作。在onCreate创建的生命周期

(只执行一次,设置了数据接收回调的动作内容)。在socketclient.java中定义了回调函数,数据发送函数,数据接收函数,数据处理函数,类对象线程池创建等。

当mainactivity.java点击连接按钮时,触发Connect方法,进行服务器连接,在socketclient.java中的连接方法connectToServer启动了receiverExecutor线程。

receiverExecutor线程有while,会在while内持续运行,当

这些情况,线程才会结束,即意外断开服务器连接或者手动断开连接,线程才会退出,如果 in.read(buffer) 没有数据可读,线程会阻塞(挂起),直到有数据到达或超时。在接收到正确的数据包时,会触发回调,会在receiverExecutor运行mainactivity内的程序(想在主线程运行内容需要使用runOnUiThread())。

----------------------------------------------------------------------------------------

看一下AI的回答:

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/963970.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用mybatisPlus插件生成代码步骤及注意事项

使用mybatisPlus插件可以很方便的生成与数据库对应的PO对象&#xff0c;以及对应的controller、service、ImplService、mapper代码&#xff0c;生成这种代码的方式有很多&#xff0c;包括mybatis-plus提供的代码生成器&#xff0c;以及idea提供的代码生成器&#xff0c;无论哪一…

FFmpeg(7.1版本)在Ubuntu18.04上的编译

一、从官网上下载FFmpeg源码 官网地址:Download FFmpeg 点击Download Source Code 下载源码到本地电脑上 二、解压包 tar -xvf ffmpeg-7.1.tar.xz 三、配置configure 1.准备工作 安装编译支持的软件 ① sudo apt-get install nasm //常用的汇编器,用于编译某些需要汇编…

CMake的QML项目中使用资源文件

Qt6.5的QML项目中&#xff0c;我发现QML引用资源文件并不像QtWidgets项目那样直接。 在QtWidgets的项目中&#xff0c;我们一般是创建.qrc​资源文件&#xff0c;然后创建前缀/new/prefix​&#xff0c;再往该前缀中添加一个图片文件&#xff0c;比如&#xff1a;test.png​。…

微信登录模块封装

文章目录 1.资质申请2.combinations-wx-login-starter1.目录结构2.pom.xml 引入okhttp依赖3.WxLoginProperties.java 属性配置4.WxLoginUtil.java 后端通过 code 获取 access_token的工具类5.WxLoginAutoConfiguration.java 自动配置类6.spring.factories 激活自动配置类 3.com…

KNIME:开源 AI 数据科学

KNIME&#xff08;Konstanz Information Miner&#xff09;是一款开源且功能强大的数据科学平台&#xff0c;由德国康斯坦茨大学的软件工程师团队开发&#xff0c;自2004年推出以来&#xff0c;广泛应用于数据分析、数据挖掘、机器学习和可视化等领域。以下是对KNIME的深度介绍…

如何让DeepSeek恢复联网功能?解决(由于技术原因,联网搜索暂不可用)

DeekSeek提示&#xff1a;&#xff08;由于技术原因&#xff0c;联网搜索暂不可用&#xff09; 众所周知&#xff0c;因为海外黑客的ddos攻击、僵尸网络攻击&#xff0c;deepseek的联网功能一直处于宕机阶段&#xff0c;但是很多问题不联网出来的结果都还是2023年的&#xff0c…

【优先算法】专题——前缀和

目录 一、【模版】前缀和 参考代码&#xff1a; 二、【模版】 二维前缀和 参考代码&#xff1a; 三、寻找数组的中心下标 参考代码&#xff1a; 四、除自身以外数组的乘积 参考代码&#xff1a; 五、和为K的子数组 参考代码&#xff1a; 六、和可被K整除的子数组 参…

刷题记录 动态规划-6: 62. 不同路径

题目&#xff1a;62. 不同路径 难度&#xff1a;中等 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#x…

梯度、梯度下降、最小二乘法

在求解机器学习算法的模型参数&#xff0c;即无约束优化问题时&#xff0c;梯度下降是最常采用的方法之一&#xff0c;另一种常用的方法是最小二乘法。 1. 梯度和梯度下降 在微积分里面&#xff0c;对多元函数的参数求∂偏导数&#xff0c;把求得的各个参数的偏导数以向量的形式…

基于STM32的智能安防监控系统

1. 引言 随着物联网技术的普及&#xff0c;智能安防系统在家庭与工业场景中的应用日益广泛。本文设计了一款基于STM32的智能安防监控系统&#xff0c;集成人体感应、环境异常检测、图像识别与云端联动功能&#xff0c;支持实时报警、远程监控与数据回溯。该系统采用边缘计算与…

优化代码性能:利用CPU缓存原理

在计算机的世界里&#xff0c;有一场如同龟兔赛跑般的速度较量&#xff0c;主角便是 CPU 和内存 。龟兔赛跑的故事大家都耳熟能详&#xff0c;兔子速度飞快&#xff0c;乌龟则慢吞吞的。在计算机中&#xff0c;CPU 就如同那敏捷的兔子&#xff0c;拥有超高的运算速度&#xff0…

Notepad++消除生成bak文件

设置(T) ⇒ 首选项... ⇒ 备份 ⇒ 勾选 "禁用" 勾选禁用 就不会再生成bak文件了 notepad怎么修改字符集编码格式为gbk 如图所示

如何创建折叠式Title

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容&#xff0c;本章回中将介绍SliverAppBar组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似&#xff0c;它们的…

K个不同子数组的数目--滑动窗口--字节--亚马逊

Stay hungry, stay foolish 题目描述 给定一个正整数数组 nums和一个整数 k&#xff0c;返回 nums 中 「好子数组」 的数目。 如果 nums 的某个子数组中不同整数的个数恰好为 k&#xff0c;则称 nums 的这个连续、不一定不同的子数组为 「好子数组 」。 例如&#xff0c;[1,2,…

Chromium132 编译指南 - Android 篇(一):编译前准备

1. 引言 欢迎来到《Chromium 132 编译指南 - Android 篇》系列的第一部分。本系列指南将引导您逐步完成在 Android 平台上编译 Chromium 132 版本的全过程。Chromium 作为一款由 Google 主导开发的开源浏览器引擎&#xff0c;为众多现代浏览器提供了核心驱动力。而 Android 作…

webpack传输性能优化

手动分包 基本原理 手动分包的总体思路是&#xff1a;先打包公共模块&#xff0c;然后再打包业务代码。 打包公共模块 公共模块会被打包成为动态链接库&#xff08;dll Dynamic Link Library&#xff09;&#xff0c;并生成资源清单。 打包业务代码 打包时&#xff0c;如果…

6 [新一代Github投毒针对网络安全人员钓鱼]

0x01 前言 在Github上APT组织“海莲花”发布存在后门的提权BOF&#xff0c;通过该项目针对网络安全从业人员进行钓鱼。不过其实早在几年前就已经有人对Visual Studio项目恶意利用进行过研究&#xff0c;所以投毒的手法也不算是新的技术。但这次国内有大量的安全从业者转发该钓…

加载数据,并切分

# Step 3 . WebBaseLoader 配置为专门从 Lilian Weng 的博客文章中抓取和加载内容。它仅针对网页的相关部分&#xff08;例如帖子内容、标题和标头&#xff09;进行处理。 加载信息 from langchain_community.document_loaders import WebBaseLoader loader WebBaseLoader(w…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.5 高级索引应用:图像处理中的区域提取

2.5 高级索引应用&#xff1a;图像处理中的区域提取 目录/提纲 #mermaid-svg-BI09xc20YqcpUam7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BI09xc20YqcpUam7 .error-icon{fill:#552222;}#mermaid-svg-BI09xc20…

房屋中介管理系统的设计与实现

房屋中介管理系统的设计与实现 摘要&#xff1a;随着房地产市场的快速发展&#xff0c;房屋中介行业的信息管理需求日益增长。传统的管理方式已无法满足中介公司对房源信息、客户信息以及业务流程的高效管理需求。为此&#xff0c;本文设计并实现了一套房屋中介管理系统&#x…