当前位置: 首页 > news >正文

BroadcastReceiver的应用

BroadcastReceiver的应用

本文是Android四大组件系列的第四篇,主要介绍BroadcastReceiver的基本概念、使用方式以及实际应用场景。

一、BroadcastReceiver基础概念

BroadcastReceiver(广播接收器)是Android四大组件之一,主要用于接收和响应系统或应用发出的广播消息。它允许应用程序接收来自Android系统或其他应用程序的通知,从而实现组件间的松耦合通信。

1.1 BroadcastReceiver的特点

  • 松耦合通信:发送方和接收方无需直接关联,通过Intent进行通信
  • 全局事件处理:可以接收系统级别的广播事件,如开机启动、网络变化等
  • 跨应用通信:可以接收来自其他应用的广播消息
  • 生命周期短:广播接收器只在处理广播消息时激活,处理完成后即销毁

1.2 广播的分类

按发送方式分类
  • 标准广播(Normal Broadcast):完全异步,所有接收器几乎同时接收到广播
  • 有序广播(Ordered Broadcast):按照优先级顺序传递,前面的接收器可以截断广播
按注册方式分类
  • 静态注册:在AndroidManifest.xml中注册,应用未启动时也能接收广播
  • 动态注册:在代码中注册,随组件生命周期变化,需要手动注销
按作用范围分类
  • 系统广播:由Android系统发出的广播,如开机、电量变化等
  • 应用内广播:在应用内部发送和接收的广播
  • 跨应用广播:可以被其他应用接收的广播

二、BroadcastReceiver的使用方式

2.1 创建BroadcastReceiver

创建BroadcastReceiver需要继承BroadcastReceiver类并实现onReceive()方法:

public class MyReceiver extends BroadcastReceiver {
    private static final String TAG = "MyReceiver";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.d(TAG, "Received broadcast: " + action);
        
        // 获取广播中的数据
        String data = intent.getStringExtra("data");
        
        // 处理广播消息
        if ("com.example.MY_ACTION".equals(action)) {
            // 处理自定义广播
            Toast.makeText(context, "Received: " + data, Toast.LENGTH_SHORT).show();
        } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
            // 处理系统广播
            Log.d(TAG, "System boot completed");
        }
    }
}

2.2 静态注册BroadcastReceiver

在AndroidManifest.xml中注册广播接收器:

<receiver
    android:name=".MyReceiver"
    android:exported="true">
    <intent-filter>
        <!-- 自定义广播 -->
        <action android:name="com.example.MY_ACTION" />
        <!-- 系统广播 -->
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

注意:从Android 8.0(API级别26)开始,大多数隐式广播(没有明确指定接收器的广播)无法通过静态注册的方式接收,需要使用动态注册。

2.3 动态注册BroadcastReceiver

在代码中动态注册和注销广播接收器:

public class MainActivity extends AppCompatActivity {
    private MyReceiver myReceiver;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 创建广播接收器实例
        myReceiver = new MyReceiver();
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        
        // 注册广播接收器
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.MY_ACTION");
        filter.addAction(Intent.ACTION_BATTERY_LOW);
        registerReceiver(myReceiver, filter);
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        
        // 注销广播接收器
        unregisterReceiver(myReceiver);
    }
}

2.4 发送广播

发送标准广播
// 创建Intent对象
Intent intent = new Intent("com.example.MY_ACTION");

// 添加数据
intent.putExtra("data", "Hello Broadcast");

// 发送广播
sendBroadcast(intent);
发送有序广播
// 创建Intent对象
Intent intent = new Intent("com.example.MY_ORDERED_ACTION");

// 添加数据
intent.putExtra("data", "Hello Ordered Broadcast");

// 发送有序广播
sendOrderedBroadcast(intent, null); // 第二个参数是权限

2.5 本地广播(LocalBroadcastManager)

本地广播只在应用内部传递,更加安全高效:

// 创建LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);

// 注册本地广播接收器
IntentFilter filter = new IntentFilter("com.example.LOCAL_ACTION");
localBroadcastManager.registerReceiver(myReceiver, filter);

// 发送本地广播
Intent intent = new Intent("com.example.LOCAL_ACTION");
intent.putExtra("data", "Hello Local Broadcast");
localBroadcastManager.sendBroadcast(intent);

// 注销本地广播接收器
localBroadcastManager.unregisterReceiver(myReceiver);

注意:LocalBroadcastManager已在AndroidX中被废弃,推荐使用LiveData、Flow或其他替代方案。

三、实战案例:网络状态监听

3.1 创建网络状态广播接收器

public class NetworkReceiver extends BroadcastReceiver {
    private NetworkCallback networkCallback;
    
    public interface NetworkCallback {
        void onNetworkChanged(boolean isConnected, String networkType);
    }
    
    public NetworkReceiver(NetworkCallback callback) {
        this.networkCallback = callback;
    }
    
    @Override
    public void onReceive(Context context, Intent intent) {
        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
            // 检查网络连接状态
            boolean isConnected = false;
            String networkType = "unknown";
            
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
            
            if (activeNetwork != null && activeNetwork.isConnected()) {
                isConnected = true;
                
                if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                    networkType = "WiFi";
                } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                    networkType = "Mobile";
                }
            }
            
            // 通知回调
            if (networkCallback != null) {
                networkCallback.onNetworkChanged(isConnected, networkType);
            }
        }
    }
}

3.2 在Activity中使用网络状态监听

public class MainActivity extends AppCompatActivity implements NetworkReceiver.NetworkCallback {
    private NetworkReceiver networkReceiver;
    private TextView networkStatusText;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        networkStatusText = findViewById(R.id.network_status);
        
        // 创建网络状态接收器
        networkReceiver = new NetworkReceiver(this);
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        
        // 注册网络状态接收器
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(networkReceiver, filter);
        
        // 立即检查当前网络状态
        checkNetworkStatus();
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        
        // 注销网络状态接收器
        unregisterReceiver(networkReceiver);
    }
    
    @Override
    public void onNetworkChanged(boolean isConnected, String networkType) {
        // 更新UI显示网络状态
        if (isConnected) {
            networkStatusText.setText("Connected to " + networkType);
            networkStatusText.setTextColor(Color.GREEN);
        } else {
            networkStatusText.setText("No network connection");
            networkStatusText.setTextColor(Color.RED);
        }
    }
    
    private void checkNetworkStatus() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        boolean isConnected = activeNetwork != null && activeNetwork.isConnected();
        String networkType = "unknown";
        
        if (isConnected) {
            if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                networkType = "WiFi";
            } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                networkType = "Mobile";
            }
        }
        
        onNetworkChanged(isConnected, networkType);
    }
}

3.3 适配Android 7.0及以上版本

Android 7.0(API级别24)及以上版本对广播接收器有一些限制,特别是对于系统广播。对于网络状态监听,可以使用NetworkCallback替代广播接收器:

public class NetworkMonitor {
    private ConnectivityManager connectivityManager;
    private NetworkCallback networkCallback;
    
    public interface NetworkStatusCallback {
        void onNetworkAvailable();
        void onNetworkLost();
    }
    
    public NetworkMonitor(Context context, final NetworkStatusCallback callback) {
        connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 使用NetworkCallback(Android 7.0及以上)
            networkCallback = new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    if (callback != null) {
                        callback.onNetworkAvailable();
                    }
                }
                
                @Override
                public void onLost(Network network) {
                    if (callback != null) {
                        callback.onNetworkLost();
                    }
                }
            };
        }
    }
    
    public void register() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 注册NetworkCallback
            NetworkRequest request = new NetworkRequest.Builder().build();
            connectivityManager.registerNetworkCallback(request, networkCallback);
        }
    }
    
    public void unregister() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && networkCallback != null) {
            // 注销NetworkCallback
            connectivityManager.unregisterNetworkCallback(networkCallback);
        }
    }
}

四、常见系统广播及应用场景

4.1 系统启动相关广播

  • ACTION_BOOT_COMPLETED:系统启动完成
    • 应用场景:启动后台服务、初始化应用数据
    • 权限要求:需要RECEIVE_BOOT_COMPLETED权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  • ACTION_SHUTDOWN:系统关机
    • 应用场景:保存重要数据、释放资源

4.2 电源管理相关广播

  • ACTION_BATTERY_LOW:电池电量低

    • 应用场景:提醒用户、降低应用耗电
  • ACTION_BATTERY_OKAY:电池电量恢复正常

    • 应用场景:恢复正常功能
  • ACTION_POWER_CONNECTED:连接电源

    • 应用场景:开始执行耗电任务
  • ACTION_POWER_DISCONNECTED:断开电源

    • 应用场景:停止耗电任务、进入省电模式

4.3 网络相关广播

  • CONNECTIVITY_ACTION:网络连接状态变化

    • 应用场景:监控网络状态、调整应用行为
    • 注意:Android 7.0后需要动态注册
  • WIFI_STATE_CHANGED_ACTION:WiFi状态变化

    • 应用场景:监控WiFi开关状态

4.4 应用安装相关广播

  • ACTION_PACKAGE_ADDED:应用安装

    • 应用场景:监控新应用安装
  • ACTION_PACKAGE_REMOVED:应用卸载

    • 应用场景:清理相关数据
  • ACTION_PACKAGE_REPLACED:应用更新

    • 应用场景:检测应用更新,执行迁移操作

五、实战案例:应用内消息推送系统

5.1 创建消息广播发送器

public class MessagePushManager {
    private static final String ACTION_NEW_MESSAGE = "com.example.ACTION_NEW_MESSAGE";
    private Context context;
    
    public MessagePushManager(Context context) {
        this.context = context.getApplicationContext();
    }
    
    public void sendMessage(String title, String content, String targetScreen) {
        Intent intent = new Intent(ACTION_NEW_MESSAGE);
        intent.putExtra("title", title);
        intent.putExtra("content", content);
        intent.putExtra("target_screen", targetScreen);
        intent.putExtra("timestamp", System.currentTimeMillis());
        
        // 使用LocalBroadcastManager发送应用内广播
        LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
    }
}

5.2 创建消息广播接收器

public class MessageReceiver extends BroadcastReceiver {
    private static final String ACTION_NEW_MESSAGE = "com.example.ACTION_NEW_MESSAGE";
    private MessageListener listener;
    
    public interface MessageListener {
        void onNewMessage(String title, String content, String targetScreen, long timestamp);
    }
    
    public MessageReceiver(MessageListener listener) {
        this.listener = listener;
    }
    
    public static IntentFilter getIntentFilter() {
        return new IntentFilter(ACTION_NEW_MESSAGE);
    }
    
    @Override
    public void onReceive(Context context, Intent intent) {
        if (ACTION_NEW_MESSAGE.equals(intent.getAction()) && listener != null) {
            String title = intent.getStringExtra("title");
            String content = intent.getStringExtra("content");
            String targetScreen = intent.getStringExtra("target_screen");
            long timestamp = intent.getLongExtra("timestamp", 0);
            
            listener.onNewMessage(title, content, targetScreen, timestamp);
        }
    }
}

5.3 在Activity中使用消息系统

public class MainActivity extends AppCompatActivity implements MessageReceiver.MessageListener {
    private MessageReceiver messageReceiver;
    private MessagePushManager pushManager;
    private TextView messageView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        messageView = findViewById(R.id.message_view);
        pushManager = new MessagePushManager(this);
        messageReceiver = new MessageReceiver(this);
        
        // 发送测试消息按钮
        findViewById(R.id.btn_send_message).setOnClickListener(v -> {
            pushManager.sendMessage(
                    "测试标题", 
                    "这是一条测试消息内容", 
                    "MainActivity");
        });
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        // 注册消息接收器
        LocalBroadcastManager.getInstance(this)
                .registerReceiver(messageReceiver, MessageReceiver.getIntentFilter());
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        // 注销消息接收器
        LocalBroadcastManager.getInstance(this)
                .unregisterReceiver(messageReceiver);
    }
    
    @Override
    public void onNewMessage(String title, String content, String targetScreen, long timestamp) {
        // 处理接收到的消息
        if ("MainActivity".equals(targetScreen) || targetScreen == null) {
            String formattedTime = new SimpleDateFormat("HH:mm:ss", Locale.getDefault())
                    .format(new Date(timestamp));
            
            String message = String.format("[%s] %s\n%s", formattedTime, title, content);
            messageView.append(message + "\n\n");
            
            // 自动滚动到底部
            final ScrollView scrollView = (ScrollView) messageView.getParent();
            scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
        }
    }
}

六、常见面试题解析

6.1 BroadcastReceiver相关面试题

问题1:BroadcastReceiver的两种注册方式有什么区别?

答案

  1. 静态注册

    • 在AndroidManifest.xml中声明
    • 应用未启动时也能接收广播
    • 应用被杀死后仍能接收广播
    • Android 8.0后对隐式广播有限制
    • 生命周期与应用生命周期无关
  2. 动态注册

    • 在代码中通过registerReceiver()方法注册
    • 只有在应用运行时才能接收广播
    • 必须在组件销毁前调用unregisterReceiver()注销
    • 生命周期与注册的组件生命周期相关
    • 可以接收所有类型的广播

问题2:标准广播和有序广播有什么区别?

答案

  1. 标准广播(Normal Broadcast)

    • 完全异步执行
    • 所有接收器几乎同时接收到广播
    • 效率高
    • 接收器之间互不干扰
    • 无法被拦截
    • 通过sendBroadcast()方法发送
  2. 有序广播(Ordered Broadcast)

    • 同步执行
    • 按照接收器优先级顺序传递
    • 优先级通过intent-filter的android:priority属性设置
    • 前面的接收器可以修改广播内容
    • 可以被拦截(abortBroadcast())
    • 通过sendOrderedBroadcast()方法发送

问题3:如何在BroadcastReceiver中执行耗时操作?

答案
BroadcastReceiver的onReceive()方法在主线程中执行,不应该执行耗时操作。处理耗时任务的方法有:

  1. 启动Service:在onReceive()中启动Service处理耗时任务

    Intent serviceIntent = new Intent(context, MyService.class);
    context.startService(serviceIntent);
    
  2. 使用WorkManager:调度后台任务

    OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class).build();
    WorkManager.getInstance(context).enqueue(workRequest);
    
  3. 使用goAsync():获取PendingResult延长生命周期

    @Override
    public void onReceive(Context context, Intent intent) {
        final PendingResult pendingResult = goAsync();
        new Thread(() -> {
            // 执行耗时操作
            // ...
            pendingResult.finish();
        }).start();
    }
    

问题4:Android 8.0对广播接收器有哪些限制?如何适配?

答案
Android 8.0(API 26)对广播接收器的主要限制:

  1. 隐式广播限制:大多数隐式广播(没有明确指定接收器的广播)无法通过静态注册的方式接收

  2. 例外情况

    • 面向特定应用的显式广播
    • 特定系统广播(如BOOT_COMPLETED、ACTION_LOCALE_CHANGED等)
  3. 适配方法

    • 使用动态注册替代静态注册
    • 使用JobScheduler或WorkManager替代部分广播场景
    • 对于自定义广播,使用显式Intent指定接收器
    • 使用Context.sendBroadcast(Intent, String)方法发送带权限的广播

6.2 实际开发中的广播问题

问题1:如何防止应用外的广播干扰应用内的广播?

答案

  1. 使用LocalBroadcastManager(已废弃但仍可用)
  2. 使用自定义权限
    <!-- 在AndroidManifest.xml中定义权限 -->
    <permission android:name="com.example.MY_PERMISSION" android:protectionLevel="signature" />
    
    <!-- 在接收器中使用权限 -->
    <receiver android:name=".MyReceiver" android:permission="com.example.MY_PERMISSION" />
    
  3. 使用显式Intent:明确指定接收器组件
  4. 使用其他通信机制:如LiveData、EventBus等

问题2:如何在应用被杀死后仍能接收广播?

答案

  1. 使用静态注册:在AndroidManifest.xml中注册接收器
  2. 处理系统限制
    • 针对Android 8.0+,只能接收特定系统广播
    • 可能需要申请自启动权限(不同厂商有不同策略)
  3. 替代方案
    • 使用FCM(Firebase Cloud Messaging)
    • 使用WorkManager的周期性任务
    • 使用前台服务保持应用活跃

七、开源项目实战分析

7.1 分析EventBus中的广播机制

EventBus是一个流行的事件总线库,它的实现原理与BroadcastReceiver类似,但更加轻量和高效:

// EventBus简化实现
public class SimpleEventBus {
    private Map<Class<?>, List<Subscription>> subscriptionsByEventType = new HashMap<>();
    
    // 注册订阅者
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        // 通过反射找到所有带@Subscribe注解的方法
        List<SubscriberMethod> methods = findSubscriberMethods(subscriberClass);
        
        for (SubscriberMethod method : methods) {
            subscribe(subscriber, method);
        }
    }
    
    // 添加订阅
    private void subscribe(Object subscriber, SubscriberMethod method) {
        Class<?> eventType = method.eventType;
        Subscription subscription = new Subscription(subscriber, method);
        
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new ArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        }
        subscriptions.add(subscription);
    }
    
    // 发布事件(类似于发送广播)
    public void post(Object event) {
        Class<?> eventClass = event.getClass();
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventClass);
        
        if (subscriptions != null) {
            for (Subscription subscription : subscriptions) {
                postToSubscriber(subscription, event);
            }
        }
    }
    
    // 向订阅者发送事件
    private void postToSubscriber(Subscription subscription, Object event) {
        try {
            subscription.method.method.invoke(subscription.subscriber, event);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 注销订阅者
    public void unregister(Object subscriber) {
        // 从所有事件类型的订阅列表中移除该订阅者
        for (List<Subscription> subscriptions : subscriptionsByEventType.values()) {
            Iterator<Subscription> iterator = subscriptions.iterator();
            while (iterator.hasNext()) {
                if (iterator.next().subscriber == subscriber) {
                    iterator.remove();
                }
            }
        }
    }
    
    // 订阅信息类
    private static class Subscription {
        final Object subscriber;
        final SubscriberMethod method;
        
        Subscription(Object subscriber, SubscriberMethod method) {
            this.subscriber = subscriber;
            this.method = method;
        }
    }
    
    // 订阅方法类
    private static class SubscriberMethod {
        final Method method;
        final Class<?> eventType;
        
        SubscriberMethod(Method method, Class<?> eventType) {
            this.method = method;
            this.eventType = eventType;
        }
    }
}

与BroadcastReceiver相比,EventBus有以下特点:

  1. 完全在应用内部使用,不涉及系统广播
  2. 基于反射实现,不需要显式注册IntentFilter
  3. 支持更细粒度的事件类型(基于类型而非Action字符串)
  4. 性能更高,没有系统开销

7.2 分析RxJava中的事件处理机制

RxJava也可以用于替代BroadcastReceiver实现应用内通信:

// 创建事件总线
public class RxBus {
    private static final RxBus INSTANCE = new RxBus();
    private final Subject<Object> bus = PublishSubject.create().toSerialized();
    
    private RxBus() {
        // 私有构造函数
    }
    
    public static RxBus getInstance() {
        return INSTANCE;
    }
    
    // 发送事件
    public void post(Object event) {
        bus.onNext(event);
    }
    
    // 订阅事件
    public <T> Observable<T> toObservable(Class<T> eventType) {
        return bus.ofType(eventType);
    }
}

// 使用示例
public class MainActivity extends AppCompatActivity {
    private Disposable subscription;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 订阅事件
        subscription = RxBus.getInstance().toObservable(MessageEvent.class)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(event -> {
                    // 处理接收到的事件
                    Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
                });
        
        // 发送事件按钮
        findViewById(R.id.btn_send).setOnClickListener(v -> {
            RxBus.getInstance().post(new MessageEvent("Hello RxBus!"));
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消订阅
        if (subscription != null && !subscription.isDisposed()) {
            subscription.dispose();
        }
    }
    
    // 事件类
    public static class MessageEvent {
        private String message;
        
        public MessageEvent(String message) {
            this.message = message;
        }
        
        public String getMessage() {
            return message;
        }
    }
}

八、总结

BroadcastReceiver是Android中用于接收广播消息的重要组件,它提供了一种松耦合的通信机制,使应用能够响应系统事件或其他应用发出的广播。

在实际开发中,我们需要根据不同场景选择合适的广播接收方式:

  1. 系统广播:使用动态注册方式,在应用运行期间接收系统事件
  2. 应用内通信:考虑使用LiveData、EventBus等替代方案,性能更好
  3. 跨应用通信:使用显式广播并添加权限控制,确保安全性

随着Android版本的更新,广播接收器的使用受到了越来越多的限制,特别是在Android 8.0及以上版本。开发者需要了解这些限制并采取适当的适配措施,如使用动态注册替代静态注册,或使用其他组件(如JobScheduler、WorkManager)替代部分广播场景。

总的来说,BroadcastReceiver是Android系统中不可或缺的组件,合理使用它可以帮助我们构建响应性更强、交互更灵活的应用程序。

相关文章:

  • 算法 之 堆
  • Rat工具:XiebroC2 一款国产C2工具简单使用介绍
  • 第四章:反射-Reflecting Your World《Unity Shaders and Effets Cookbook》
  • 2025.3.3-2025.3.9学习周报
  • 零基础上手Python数据分析 (1):Windows环境配置与开发工具,开启数据科学之旅!
  • Spring Cloud Gateway 笔记
  • 微软程序的打包格式MSIX
  • [项目]基于FreeRTOS的STM32四轴飞行器: 五.Motor驱动
  • Llama factory微调后的模型怎么通过ollama发布
  • Android 调用c++报错 exception of type std::bad_alloc: std::bad_alloc
  • 防火墙IPSec (无固定IP地址---一对多)
  • C++之vector类(超详解)
  • 【UCB CS 61B SP24】Lecture 28 - Tries 学习笔记
  • 使用 Elastic-Agent 或 Beats 将 Journald 中的 syslog 和 auth 日志导入 Elastic Stack
  • Go语言实战,HTTP和gRPC多服务启动与关闭的最佳实践
  • aws(学习笔记第三十二课) 深入使用cdk(API Gateway + event bridge)
  • 数据结构常见面试题
  • Java后端高频面经——计算机网络
  • Java线程池深度解析,从源码到面试热点
  • sudo systemctl restart docker 重启docker失败
  • 网站哪家做的好/外链生成器
  • mac page转wordpress/seo规则
  • 淘宝客必须做网站/2023年8月份新冠症状
  • 网站建设费用选网络专业/谷歌搜索引擎营销
  • 建设网站模板免费/今日全国最新疫情通报
  • wordpress时间轴scopic主题/seo托管服务