Android 四大组件——BroadcastReceiver(广播)

目录

一、广播接收器概述

[1. 是什么?](#1. 是什么?)

[2. 核心设计思想:发布-订阅模式](#2. 核心设计思想:发布-订阅模式)

[3. 主要用途](#3. 主要用途)

二、广播的工作原理

三、广播的类型

[1. 标准广播](#1. 标准广播)

[2. 有序广播](#2. 有序广播)

[2.1 在 AndroidManifest.xml 中注册,并设置优先级:](#2.1 在 AndroidManifest.xml 中注册,并设置优先级:)

[2.2 定义三个广播接收器](#2.2 定义三个广播接收器)

[2.3 在 Activity 或 Service 中发送有序广播:](#2.3 在 Activity 或 Service 中发送有序广播:)

[2.4 结果](#2.4 结果)

[四、 广播接收器的注册方式](#四、 广播接收器的注册方式)

[1. 静态注册](#1. 静态注册)

[2. 动态注册](#2. 动态注册)

[五、 本地广播(应用内广播)](#五、 本地广播(应用内广播))

[六、 安全考量](#六、 安全考量)

[七、 最佳实践与注意事项](#七、 最佳实践与注意事项)

八、常用的系统广播


我们来对 Android 四大组件之一的 BroadcastReceiver(广播接收器) 进行一次全面而深入的解析。本文将涵盖其定义、工作原理、类型、注册方式、使用方法、安全考量、最佳实践以及进化历程。

一、广播接收器概述

1. 是什么?

BroadcastReceiver(广播接收器)是一个用于响应系统范围或应用内广播消息 的组件。它充当应用程序与系统事件或其他应用程序事件之间的"事件监听器"。

可以把它理解为一种全局的监听器,它不包含用户界面,但在后台工作,当它感兴趣的特定事件(广播)发生时,就会被系统激活并执行相应的代码。

2. 核心设计思想:发布-订阅模式

广播机制完美体现了发布-订阅模式:

  • 发布者: 发送一个广播Intent

  • 订阅者: 注册一个 BroadcastReceiver来监听特定类型的广播。

  • 系统(作为中介): 当有广播发出时,系统会寻找所有订阅了该广播的接收器,并调用它们的**onReceive()** 方法。

3. 主要用途

  1. 监听系统状态变化: 如开机完成、电量低、网络状态改变、屏幕开关、时区变化等。

  2. 应用内组件通信: 在不同 Activity、Service 等组件之间传递消息,实现解耦。

  3. 应用间通信: 一个应用可以发送广播,让其他应用(如果它们注册了相应的接收器)收到并处理。

二、广播的工作原理

  1. 发送广播: 某个组件(如 Activity、Service 或其他 Receiver)使用 Intent 对象,通过 Context.sendBroadcast() 等方法发送广播。

  2. 系统匹配: Android 系统接收到广播 Intent 后,会根据 Intent 中的信息(如 Action、Category 等)去查找所有已注册 并且与该 Intent 匹配的 BroadcastReceiver

  3. 实例化与执行: 系统会实例化匹配的 Receiver(如果它还没运行),并调用其 onReceive(Context context, Intent intent) 方法。

  4. 生命周期: Receiver 对象的生命周期非常短暂,仅在 onReceive() 方法执行期间存活。方法执行完毕后,该实例就会被销毁

重要限制:onReceive()不能执行异步操作,也不能开启一个长时间运行的后台线程。因为一旦 onReceive() 返回,进程可能随时被系统杀死。如果需要进行耗时操作,必须使用 goAsync() 或启动一个 JobIntentService / JobScheduler

三、广播的类型

1. 标准广播

  • 特点: 完全异步。发送后,所有匹配的接收器几乎同时收到广播,它们之间没有顺序,也无法中止广播。

  • 发送方法: sendBroadcast(Intent)

2. 有序广播

  • 特点: 同步执行。发送后,同一时间只有一个接收器能收到广播。该接收器执行完后,可以将结果传递给下一个接收器 ,也可以完全中止广播,使其不再继续传播。

  • 发送方法: sendOrderedBroadcast(Intent, String)

  • 优先级: 通过在注册时设置 android:priority 属性来决定接收顺序,数值越大优先级越高(范围:-1000 到 1000)。

  • 结果传播: 可以使用 **setResultExtras(Bundle)**存放数据,下一个接收器可以用 getResultExtras(true) 获取并修改。

  • 中止广播: abortBroadcast()

接下来通过示例来演示有序广播的流程:这里用一个链式处理的场景,如:权限验证 → 数据验证 → 业务处理 → 结果上报等流程。

2.1 在 AndroidManifest.xml 中注册,并设置优先级:

XML 复制代码
<receiver
    android:name=".PermissionCheckReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter android:priority="1000"> <!-- 最高优先级 -->
        <action android:name="com.example.ORDERED_BROADCAST_ACTION" />
    </intent-filter>
</receiver>

<receiver
    android:name=".ContentFilterReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter android:priority="500"> <!-- 中等优先级 -->
        <action android:name="com.example.ORDERED_BROADCAST_ACTION" />
    </intent-filter>
</receiver>

<receiver
    android:name=".FinalProcessorReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter android:priority="100"> <!-- 最低优先级 -->
        <action android:name="com.example.ORDERED_BROADCAST_ACTION" />
    </intent-filter>
</receiver>

2.2 定义三个广播接收器

java 复制代码
//------------------------PermissionCheckReceiver---------------------------------

public class PermissionCheckReceiver extends BroadcastReceiver {
    private static final String TAG = "PermissionCheck";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "【权限检查】开始执行...");
        
        // 模拟权限检查
        boolean hasPermission = checkUserPermission();
        
        if (!hasPermission) {
            Log.w(TAG, "【权限检查】权限不足,中止广播!");
            abortBroadcast(); // 中止广播,后续接收器不会执行
            return;
        }
        
        // 添加处理结果
        Bundle resultBundle = getResultExtras(true);
        resultBundle.putString("permission_status", "PASS");
        setResultExtras(resultBundle);
        
        Log.d(TAG, "【权限检查】权限验证通过,继续传递");
    }
    
    private boolean checkUserPermission() {
        // 模拟权限检查逻辑,这里简单返回true
        return true;
    }
}



//--------------------------ContentFilterReceiver -----------------------------------

public class ContentFilterReceiver extends BroadcastReceiver {
    private static final String TAG = "ContentFilter";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "【内容过滤】开始执行...");
        
        // 获取上一个接收器的处理结果
        Bundle previousResult = getResultExtras(false);
        String permissionStatus = previousResult != null ? 
            previousResult.getString("permission_status") : "UNKNOWN";
        
        Log.d(TAG, "【内容过滤】接收到权限状态: " + permissionStatus);
        
        // 模拟内容过滤
        String originalMessage = intent.getStringExtra("message");
        String filteredMessage = filterContent(originalMessage);
        
        // 更新结果数据
        Bundle resultBundle = getResultExtras(true);
        resultBundle.putString("filtered_message", filteredMessage);
        resultBundle.putString("content_filter_status", "PROCESSED");
        setResultExtras(resultBundle);
        
        Log.d(TAG, "【内容过滤】内容过滤完成,继续传递");
    }
    
    private String filterContent(String message) {
        // 简单的敏感词过滤
        if (message == null) return "";
        return message.replace("敏感词", "***");
    }
}


//------------------------FinalProcessorReceiver ---------------------------------

public class FinalProcessorReceiver extends BroadcastReceiver {
    private static final String TAG = "FinalProcessor";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "【最终处理】开始执行...");
        
        // 获取前面所有接收器的处理结果
        Bundle finalResult = getResultExtras(false);
        
        String originalMessage = intent.getStringExtra("message");
        String filteredMessage = finalResult != null ? 
            finalResult.getString("filtered_message") : originalMessage;
        String permissionStatus = finalResult != null ? 
            finalResult.getString("permission_status") : "UNKNOWN";
        
        Log.d(TAG, "======= 最终处理结果 =======");
        Log.d(TAG, "原始消息: " + originalMessage);
        Log.d(TAG, "过滤后消息: " + filteredMessage);
        Log.d(TAG, "权限状态: " + permissionStatus);
        Log.d(TAG, "【最终处理】消息处理流程完成");
        
        // 可以在这里更新UI或进行其他操作
        Toast.makeText(context, "消息处理完成: " + filteredMessage, Toast.LENGTH_SHORT).show();
    }
}

2.3 在 Activity 或 Service 中发送有序广播:

java 复制代码
public class MainActivity extends AppCompatActivity {
    
    private void sendOrderedBroadcastExample() {
        Log.d("BroadcastSender", "准备发送有序广播...");
        
        // 创建广播Intent
        Intent orderedIntent = new Intent("com.example.ORDERED_BROADCAST_ACTION");
        orderedIntent.putExtra("message", "这是一条包含敏感词的重要消息");
        
        // 发送有序广播
        // 参数说明:
        // 1. Intent
        // 2. 权限字符串(可为null)
        // 3. 最终接收器(可选)
        // 4. Handler(可为null)
        // 5. 初始结果码
        // 6. 初始数据(可为null)
        // 7. 初始Bundle
        sendOrderedBroadcast(
            orderedIntent,
            null, // 权限
            new FinalProcessorReceiver(), // 可选的最终接收器
            null, // Handler
            Activity.RESULT_OK,
            null, // initialData
            null  // initialExtras
        );
        
        Log.d("BroadcastSender", "有序广播已发送");
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Button sendButton = findViewById(R.id.send_button);
        sendButton.setOnClickListener(v -> sendOrderedBroadcastExample());
    }
}

详细解释下sendOrderedBroadcast:

java 复制代码
public abstract void sendOrderedBroadcast(
    //要发送的广播Intent,包含action、data、category等信息。
    Intent intent, 

    //接收器必须拥有的权限。只有声明了该权限的接收器才能接收此广播。
    //如:String permission = "com.example.MY_PERMISSION";
    //String systemPermission = android.Manifest.permission.INTERNET;
    String receiverPermission, 

    //最终接收器。无论广播是否被中止,这个接收器都会最后执行。
    //即使前面的接收器调用了 abortBroadcast(),最终接收器仍会执行
    BroadcastReceiver resultReceiver, 

    //指定处理广播的Handler。控制 onReceive() 方法在哪个线程执行。
    //如果为 null:在主线程执行
    Handler scheduler, 

    //初始结果码。接收器可以通过 setResultCode(int code) 修改这个值。
    int initialCode, 

    //初始结果数据。接收器可以通过 setResultData(String data) 修改这个值。
    String initialData, 

    //初始附加数据。接收器可以通过 getResultExtras() 和 setResultExtras() 访问和修改。
    Bundle initialExtras
)

2.4 结果

java 复制代码
BroadcastSender: 准备发送有序广播...
BroadcastSender: 有序广播已发送
PermissionCheck: 【权限检查】开始执行...
PermissionCheck: 【权限检查】权限验证通过,继续传递
ContentFilter: 【内容过滤】开始执行...
ContentFilter: 【内容过滤】接收到权限状态: PASS
ContentFilter: 【内容过滤】内容过滤完成,继续传递
FinalProcessor: 【最终处理】开始执行...
FinalProcessor: ======= 最终处理结果 =======
FinalProcessor: 原始消息: 这是一条包含敏感词的重要消息
FinalProcessor: 过滤后消息: 这是一条包含***的重要消息
FinalProcessor: 权限状态: PASS
FinalProcessor: 【最终处理】消息处理流程完成

四、 广播接收器的注册方式

有两种方式将 BroadcastReceiver 注册到系统中,使其开始监听。

1. 静态注册

AndroidManifest.xml 文件中进行声明。

XML 复制代码
<receiver
    android:name=".MyBootCompletedReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <!-- 监听系统启动完成广播 -->
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
  • 特点:

    • 应用安装后即注册,即使应用未启动,Receiver 也能被激活(系统会先启动你的应用进程)。

    • 常用于监听系统事件(如开机启动)。

    • 从 Android 8.0 (API 26) 开始,大部分隐式广播不能再使用静态注册 ,以防止应用过度消耗资源。BOOT_COMPLETED 是少数例外之一。

2. 动态注册

在代码中(如 Activity 或 Service 中)通过调用 **registerReceiver()**方法进行注册。

java 复制代码
public class MainActivity extends AppCompatActivity {
    private MyNetworkReceiver networkReceiver;

    @Override
    protected void onResume() {
        super.onResume();
        // 1. 创建Receiver实例
        networkReceiver = new MyNetworkReceiver();
        // 2. 创建IntentFilter
        IntentFilter filter = new IntentFilter();
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        // 3. 注册Receiver
        registerReceiver(networkReceiver, filter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 4. 必须反注册!防止内存泄漏和不必要的系统开销
        if (networkReceiver != null) {
            unregisterReceiver(networkReceiver);
            networkReceiver = null;
        }
    }
}

特点:

  • 注册与组件(如 Activity)生命周期绑定,灵活性强。

  • 必须在合适的时机(如 onPause()onDestroy())调用 unregisterReceiver() 进行反注册

  • 不受 Android 8.0 隐式广播限制的影响,可以监听任何广播

五、 本地广播(应用内广播)

为了安全和效率,如果广播只在应用内部使用,强烈推荐使用 LocalBroadcastManager (现已并入 AndroidX)或类似的机制。本地广播无法通过静态注册方式来接受,相比起系统全局广播更加高效

  • 优点:

    1. 安全: 数据不会泄露给其他应用,其他应用也无法向你发送广播

    2. 高效: 不需要系统介入,进程内通信,开销小

    3. 便捷: 不需要担心 Android 8.0 的静态注册限制。

  • 使用方法:

java 复制代码
// 1. 获取LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);

// 2. 注册接收器(动态注册方式)
localBroadcastManager.registerReceiver(myLocalReceiver, new IntentFilter("LOCAL_ACTION"));

// 3. 发送广播
Intent localIntent = new Intent("LOCAL_ACTION");
localIntent.putExtra("data", "Local message");
localBroadcastManager.sendBroadcast(localIntent);

// 4. 反注册
localBroadcastManager.unregisterReceiver(myLocalReceiver);

六、 安全考量

广播是跨进程通信的,因此安全性至关重要。

  1. 权限:

    • 在发送时指定权限: sendBroadcast(Intent, String)。只有拥有该权限的接收器才能收到广播。

    • 在注册时指定权限: 在静态注册的 <receiver> 标签或动态注册的 registerReceiver() 方法中指定权限。只有拥有该权限的发送者发出的广播才能被接收。

  2. android:exported 属性:

    • 在静态注册中,此属性决定该 Receiver 是否能接收来自其他应用的广播。

    • false:仅接收同一应用内或同一用户ID下的组件发出的广播。

    • true可以接收来自任何应用的广播

    • 最佳实践: 如果 Receiver 不需要接收外部广播,显式设置为 false

  3. 避免敏感信息: 不要在隐式广播的 Intent 中携带敏感数据,因为任何注册了该广播的应用都能收到。

七、 最佳实践与注意事项

  • onReceive() 中不要做耗时操作: 重申这一点。如果需要,请使用 goAsync() 获取一个 PendingResult 对象,然后在后台线程中完成工作并调用 finish()
java 复制代码
@Override
public void onReceive(Context context, Intent intent) {
    final PendingResult pendingResult = goAsync();
    AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
        @Override
        protected String doInBackground(String... params) {
            // 在这里执行耗时操作
            return "Done";
        }
        @Override
        protected void onPostExecute(String s) {
            // 必须调用finish()
            pendingResult.finish();
        }
    };
    asyncTask.execute();
}
  • 精确的 IntentFilter: 尽量使用更具体的 Action,避免使用过于宽泛的 Filter,以减少不必要的激活和资源浪费。

  • 生命周期管理: 动态注册一定要配对反注册。

  • 优先使用 LocalBroadcastManager: 对于应用内通信,它是首选。

推荐替代方案:

  • JobScheduler / WorkManager 用于调度后台任务,是执行延迟、异步任务的首选。

  • Foreground Service + 通知: 用于需要用户感知的长时间运行任务。

  • LocalBroadcastManager 用于应用内通信。

  • 第三方事件总线: 如 EventBus、RxJava,用于组件间解耦通信,但它们只在进程内有效。

八、常用的系统广播

最后给大家提供下我们平常可能会用到的一些系统广播:

java 复制代码
intent.action.AIRPLANE_MODE;
//关闭或打开飞行模式时的广播

Intent.ACTION_BATTERY_CHANGED;
//充电状态,或者电池的电量发生变化
//电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册

Intent.ACTION_BATTERY_LOW;
//表示电池电量低

Intent.ACTION_BATTERY_OKAY;
//表示电池电量充足,即从电池电量低变化到饱满时会发出广播

Intent.ACTION_BOOT_COMPLETED;
//在系统启动完成后,这个动作被广播一次(只有一次)。

Intent.ACTION_CAMERA_BUTTON;
//按下照相时的拍照按键(硬件按键)时发出的广播

Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
//当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,android系统都会广播此Action消息

Intent.ACTION_CONFIGURATION_CHANGED;
//设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.java)

Intent.ACTION_DATE_CHANGED;
//设备日期发生改变时会发出此广播

Intent.ACTION_DEVICE_STORAGE_LOW;
//设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用?

Intent.ACTION_DEVICE_STORAGE_OK;
//设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用?

Intent.ACTION_DOCK_EVENT;
//
//发出此广播的地方frameworks\base\services\java\com\android\server\DockObserver.java

Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE;
////移动APP完成之后,发出的广播(移动是指:APP2SD)

Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
//正在移动APP时,发出的广播(移动是指:APP2SD)

Intent.ACTION_GTALK_SERVICE_CONNECTED;
//Gtalk已建立连接时发出的广播

Intent.ACTION_GTALK_SERVICE_DISCONNECTED;
//Gtalk已断开连接时发出的广播

Intent.ACTION_HEADSET_PLUG;
//在耳机口上插入耳机时发出的广播

Intent.ACTION_INPUT_METHOD_CHANGED;
//改变输入法时发出的广播

Intent.ACTION_LOCALE_CHANGED;
//设备当前区域设置已更改时发出的广播

Intent.ACTION_MANAGE_PACKAGE_STORAGE;
//

Intent.ACTION_MEDIA_BAD_REMOVAL;
//未正确移除SD卡(正确移除SD卡的方法:设置--SD卡和设备内存--卸载SD卡),但已把SD卡取出来时发出的广播
//广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)

Intent.ACTION_MEDIA_BUTTON;
//按下"Media Button" 按键时发出的广播,假如有"Media Button" 按键的话(硬件按键)

Intent.ACTION_MEDIA_CHECKING;
//插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播?
Intent.ACTION_MEDIA_EJECT;
//已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播?
//广播:用户想要移除扩展介质(拔掉扩展卡)。
Intent.ACTION_MEDIA_MOUNTED;
//插入SD卡并且已正确安装(识别)时发出的广播
//广播:扩展介质被插入,而且已经被挂载。
Intent.ACTION_MEDIA_NOFS;
//
Intent.ACTION_MEDIA_REMOVED;
//外部储存设备已被移除,不管有没正确卸载,都会发出此广播?
// 广播:扩展介质被移除。
Intent.ACTION_MEDIA_SCANNER_FINISHED;
//广播:已经扫描完介质的一个目录
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE;
//
Intent.ACTION_MEDIA_SCANNER_STARTED;
//广播:开始扫描介质的一个目录

Intent.ACTION_MEDIA_SHARED;
// 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。
 Intent.ACTION_MEDIA_UNMOUNTABLE;
//
Intent.ACTION_MEDIA_UNMOUNTED
// 广播:扩展介质存在,但是还没有被挂载 (mount)。
Intent.ACTION_NEW_OUTGOING_CALL;

Intent.ACTION_PACKAGE_ADDED;
//成功的安装APK之后
//广播:设备上新安装了一个应用程序包。
//一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)
 Intent.ACTION_PACKAGE_CHANGED;
//一个已存在的应用程序包已经改变,包括包名
Intent.ACTION_PACKAGE_DATA_CLEARED;
//清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时?)
//用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播)

Intent.ACTION_PACKAGE_INSTALL;
//触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用?
//
Intent.ACTION_PACKAGE_REMOVED;
//成功的删除某个APK之后发出的广播
//一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)

Intent.ACTION_PACKAGE_REPLACED;
//替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播?)
Intent.ACTION_PACKAGE_RESTARTED;
//用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
Intent.ACTION_POWER_CONNECTED;
//插上外部电源时发出的广播
Intent.ACTION_POWER_DISCONNECTED;
//已断开外部电源连接时发出的广播
Intent.ACTION_PROVIDER_CHANGED;
//

Intent.ACTION_REBOOT;
//重启设备时的广播

Intent.ACTION_SCREEN_OFF;
//屏幕被关闭之后的广播

Intent.ACTION_SCREEN_ON;
//屏幕被打开之后的广播

Intent.ACTION_SHUTDOWN;
//关闭系统时发出的广播

Intent.ACTION_TIMEZONE_CHANGED;
//时区发生改变时发出的广播

Intent.ACTION_TIME_CHANGED;
//时间被设置时发出的广播

Intent.ACTION_TIME_TICK;
//广播:当前时间已经变化(正常的时间流逝)。
//当前时间改变,每分钟都发送,不能通过组件声明来接收,只有通过Context.registerReceiver()方法来注册

Intent.ACTION_UID_REMOVED;
//一个用户ID已经从系统中移除发出的广播
//

Intent.ACTION_UMS_CONNECTED;
//设备已进入USB大容量储存状态时发出的广播?

Intent.ACTION_UMS_DISCONNECTED;
//设备已从USB大容量储存状态转为正常状态时发出的广播?

Intent.ACTION_USER_PRESENT;
//

Intent.ACTION_WALLPAPER_CHANGED;
//设备墙纸已改变时发出的广播
相关推荐
努力学习的小廉2 小时前
初识MYSQL —— 复合查询
android·数据库·mysql
canonical_entropy2 小时前
Nop平台到底有什么独特之处,它能用在什么场景?
java·后端·领域驱动设计
chilavert3182 小时前
技术演进中的开发沉思-174 java-EJB:分布式通信
java·分布式
ii_best2 小时前
安卓/IOS工具开发基础教程:按键精灵一个简单的文字识别游戏验证
android·开发语言·游戏·ios·编辑器
不是株2 小时前
JavaWeb(后端进阶)
java·开发语言·后端
编程火箭车3 小时前
【Java SE 基础学习打卡】02 计算机硬件与软件
java·电脑选购·计算机基础·编程入门·计算机硬件·软件系统·编程学习路线
Felix_XXXXL4 小时前
IDEA + Spring Boot 的三种热加载方案
java·后端
我命由我123454 小时前
IDEA - IDEA 快速回到页面首尾、页面快速滑动、快速定位到指定行
java·运维·ide·后端·java-ee·intellij-idea·intellij idea