Android 面试题 应用程序结构 十

🔥 Intent 传递数据 🔥

ActivityServiceBroadcastReceiver之间的通信载体 Intent 来传递数据。而ContentProvider则是共享文件

 Intent可传递的数据类型:

  • a. 8种基本数据类型(boolean byte char short int long float double)、String
  • b. Intent、Bundle
  • c. Serializable和Parcelable序列化对象(将对象转为字节流)及其对应数组、CharSequence 类型
  • d. ArrayList,泛型参数类型为:<Integer>、<? Extends Parcelable>、<Charsequence>、<String>

 序列化

1.Serializable 方式来进行序列化
//构建一个实现了Serializable接口的类
public class XrData implements Serializable {
    public String name;
    public String phone;
}

//序列化

    Bundle bundle = new Bundle(); // Bundle主要用于传递数据,它保存的数据,是以key-value(键值对)的形式存在的
    bundle.putSerializable("DATA", entity); //1
    fragment.setArguments(bundle);

//反序列化
    mContact = (XrData) getArguments().getSerializable("DATA");
    mContact.name
    
2.Parcelable
这是安卓专用的序列化方式。Parcelable接口比Serializable接口效率更高。
因为其实现原理是把对象分解为Intent支持的数据类型,并通过Intent进行传输;
其数据可以保存在内存中,相对于Serializable将数据保存在磁盘,效率自然更高。

public class CutInfo implements Parcelable {

    private int offsetX;
    private int offsetY;

    public CutInfo() {}

    // 序列化
    @Override
    public void writeToParcel(Parcel dest, int flags) { //3
        dest.writeInt(this.offsetX);
        dest.writeInt(this.offsetY);
    }
    
    @Override
    public int describeContents() { //4
        return 0;
    }

    // 反序列化
    protected CutInfo(Parcel in) {
        this.offsetX = in.readInt();
        this.offsetY = in.readInt();
    }

    // 反序列化
    public static final Creator<CutInfo> CREATOR = new Creator<CutInfo>() { //5
        @Override
        public CutInfo createFromParcel(Parcel source) {
            return new CutInfo(source);
        }

        @Override
        public CutInfo[] newArray(int size) {
            return new CutInfo[size];
        }
    };
}

// ActivityA.java 实现序列化
button.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
          Intent intent = new Intent(this, ActivityB.class);
          intent.putExtra("cut", new CutInfo(8, 8));
          startActivity(intent);
      }
  });
  
// ActivityB.java 获取并反序列化
 Intent intent = getIntent();
 CutInfo cut = intent.getParcelableExtra("cut");
 Log.d("ActivityB", cut.toString()); //会输出: “8, 8”

🔥 ContentProvider 进程间传递数据 🔥 

进程间传递数据(底层用的还是binder)

原理:Binder简单的用法是进程A可以通过Binder获得进程B的本地代理,通过本地代理,就可以在进程A里面的调用进程B的方法。在ContentProvider的实现原理中,通过ContentResolver可以查找对应给定Uri的ContentProvider,返回对应的本地代理 BinderProxy,通过这个BinderProxy就可以调用insert、delete接口。

  • Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。(例如通信录信息)
  • ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。ContentProvider是以Uri的形式对外提供数据,ContentResolver是根据Uri来访问数据。

🔥 三者关系 🔥 

ContentProvider、ContentResolver、ContentObserver 三者关系

  • ContentProvider:内容提供者,主要作用就是管理数据,比如最常见的增删改查操作,同时为这些数据的访问提供了统一的接口,实现进程间的数据传递和共享;
  • ContentResolver:内容解析者,ContentResolver可以为不同URI操作不同的ContentProvider中的数据,外部进程可以通过ContentResolver与ContentProvider进行交互。
  • ContentObserver:内容观察者,观察ContentProvider中的数据变化,有变化的时候执行特定操作。本人用的最多的是监听Settings数据库的变化。由于ContentObserver的生命周期不同步于Activity和Service等,因此,在不需要时,需要手动的调用unregisterContentObserver()去取消注册。

🔥 在AndroidManifest注册 🔥 

 在Android文件AndroidManifest.xml注册
authorities:访问表的主机地址;
exported:是否允许对外应用程序访问,true允许,false不允许,默认false;

 🔥 继承ContentProvider 🔥

Android系统为我们提供ContentProvider类,我们继承这个类实现onCreate()、getType()、insert()、delete()、update()、query();
onCreate():内容提供者初始化的时候调用,一般这里执行数据库创建、升级操作。
getType():根据给定的Uri返回一个MIME类型的数据。
insert():Uri表示需要操作哪张表,ContentValues需要插入的值,插入成功返回一个Uri;
delete():Uri表示需要操作哪张表,selection和selectionArgs表示筛选的条件,返回受影响的行。
update():Uri表示需要操作哪张表,ContentValues需要插入的值,selection和selectionArgs表示筛选的条件,返回受影响的行。
query():Uri表示需要操作哪张表,projection需要查询表中那些字段,selection和selectionArgs表示筛选的条件,sortOrder对查询查询结果进行排序,将查询结果放到Cursor返回。

public class PersonProvider extends ContentProvider {
    private static String TAG = PersonProvider.class.getSimpleName();

    //这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities
    public static final String AUTHORITY = "com.dream.contentprovider";

    //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码,也就是说如果找不到匹配的类型,返回-1
    private static final UriMatcher URI_MATCHER;

    //匹配不同的表的code
    private static final int ALL_PERSON = 1;
    private static final int PERSON = 2;

    //数据库的名字
    public static final String DB_NAME = "test.db";
    //数据库版本
    public static final int DB_VERSION = 1;
    //数据库表的名称
    private static final String TABLE_NAME = "person";

    private MyDBHelper dbHelper;
    private SQLiteDatabase db;

    static {
        //创建一个路径识别器
        URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
        //1.指定一个路径的匹配规则
        //如果路径满足content://com.dream.contentprovider.provider.PersonProvider/person,返回值就是(ALL_PERSON)=1
        URI_MATCHER.addURI(AUTHORITY, "/person", ALL_PERSON);
        //2.#号为通配符
        //如果路径满足content://com.dream.contentprovider.provider.PersonProvider/person/3,返回值就是(PERSON)=2
        URI_MATCHER.addURI(AUTHORITY, "/person/#", PERSON);
    }


    /**
     * 一般是对象第一次被创建时调用的方法
     *
     * @return
     */
    @Override
    public boolean onCreate() {
        dbHelper = new MyDBHelper(this.getContext(), DB_NAME, null, DB_VERSION);
        db = dbHelper.getReadableDatabase();
        return true;
    }


    /**
     * 让别人去调用返回结果
     *
     * @param uri           匹配条件
     * @param projection    选择的列
     * @param selection     查询条件
     * @param selectionArgs 查询条件的value
     * @param sortOrder     排序
     * @return
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        int type = URI_MATCHER.match(uri);
        switch (type) {
            case ALL_PERSON:
                return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
            case PERSON:
                //获取具体某一行的数据
                String id = uri.getPathSegments().get(1);
                return db.query(TABLE_NAME, projection, "_id = ?", new String[]{id}, null, null, sortOrder);
            default:
                break;
        }
        return null;
    }

    //如果是单条记录应该返回以vnd.android.cursor.item/ 为首的字符串
    //如果是多条记录,应该返回vnd.android.cursor.dir/ 为首的字符串
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        int type = URI_MATCHER.match(uri);
        switch (type) {
            case ALL_PERSON:
                return "vnd.android.cursor.dir/vnd.com.dream.contentprovider.provider.person";
            case PERSON:
                return "vnd.android.cursor.item/vnd.com.dream.contentprovider.provider.person";
            default:
                return null;
        }
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        int type = URI_MATCHER.match(uri);
        switch (type) {
            case ALL_PERSON:
                long row = db.insert(TABLE_NAME, null, values);
                return ContentUris.withAppendedId(uri, row);
            case PERSON:
                long row2 = db.insert(TABLE_NAME, null, values);
                return ContentUris.withAppendedId(uri, row2);
            default:
                return null;
        }
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int type = URI_MATCHER.match(uri);
        int updateRow = 0;
        switch (type) {
            case ALL_PERSON:
                updateRow = db.delete(TABLE_NAME, selection, selectionArgs);
                break;
            case PERSON:
                //获取具体某一行的数据
                String id = uri.getPathSegments().get(1);
                updateRow = db.delete(TABLE_NAME, "_id = ?", new String[]{id});
                break;
            default:
                break;
        }
        return updateRow;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int type = URI_MATCHER.match(uri);
        int updateRow = 0;
        switch (type) {
            case ALL_PERSON:
                updateRow = db.update(TABLE_NAME, values, selection, selectionArgs);
                break;
            case PERSON:
                //获取具体某一行的数据
                String id = uri.getPathSegments().get(1);
                updateRow = db.update(TABLE_NAME, values, "_id = ?", new String[]{id});
                break;
            default:
                break;
        }
        return updateRow;
    }
}

 当APP启动的时候,onCreate()方法被执行,此时已经创建了数据库。

 🔥 跨进程调用 🔥

private static final Uri STUDENT_ALL_URI = Uri.parse("content://" + PersonProvider.AUTHORITY + "/person");

拼接完整的地址为:"content://com.dream.contentprovider/person""content://com.dream.contentprovider/person/id",其中这个id表示表中数据的行数,id=0、1、 2 ...。当和UriMatcher 进行匹配返回匹配类型。不管是进程内还是跨进程之间通信关键是否URL正确,否则会出现如下错误:

private static final Uri STUDENT_ALL_URI = Uri.parse("content://com.dream.contentprovider/person");

 插入数据insert()

 mUserProvider = getContentResolver();
 ContentValues values = new ContentValues();
 values.put("name", "张三");
 values.put("age", "18");
 values.put("sex", "男");
 mUserProvider.insert(STUDENT_ALL_URI, values);

 删除数据delete()

 mUserProvider = getContentResolver();
 mUserProvider.delete(STUDENT_ALL_URI, "name = ?", new String[]{"张三"});

查询数据query() 

 mUserProvider = getContentResolver();
 String result = "";
 Cursor cursor = mUserProvider.query(STUDENT_ALL_URI, null, null, null);
 if (cursor != null) {
     try {
          while (cursor.moveToNext()) {
              String id = cursor.getString(cursor.getColumnIndex("_id"));
              String name = cursor.getString(cursor.getColumnIndex("NAME"));
              String age = cursor.getString(cursor.getColumnIndex("AGE"));
              String sex = cursor.getString(cursor.getColumnIndex("SEX"));
              result = result + "\n" + "_id=" + id + " name=" + name + " sex=" + sex + "  age=" + age;
          }
     } finally {
         cursor.close();
         cursor = null;
         mTextView.setText(result);
     }
 }

记得关闭游标,防止内存泄漏

🔥 BroadcastReceiver 🔥

BroadcastReceiver(广播接收器),属于 Android 四大组件之一,在 Android 开发中,BroadcastReceiver 的应用场景非常多。今天,我将详细讲解关于BroadcastReceiver的所有广播类型,主要分为5类:

普通广播(Normal Broadcast)
系统广播(System Broadcast)
有序广播(Ordered Broadcast)
粘性广播(Sticky Broadcast)
App应用内广播(Local Broadcast)

🔥 普通广播(Normal Broadcast)🔥 

即开发者自身定义intent的广播(最常用)。发送广播使用如下:

Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);

若被注册了的广播接收者中注册时 intentFilter action与上述匹配,则会接收此广播(即进行回调 onReceive() )。如下mBroadcastReceiver则会接收上述广播

<receiver 
    //此广播接收者类是mBroadcastReceiver
    android:name=".mBroadcastReceiver" >
    //用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="BROADCAST_ACTION" />
    </intent-filter>
</receiver>

若发送广播有相应权限,那么广播接收者也需要相应权限

🔥 系统广播(System Broadcast)🔥 

 Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播

 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:

系统操作action
监听网络变化android.net.conn.CONNECTIVITY_CHANGE
关闭或打开飞行模式Intent.ACTION_AIRPLANE_MODE_CHANGED
充电时或电量发生变化Intent.ACTION_BATTERY_CHANGED
电池电量低Intent.ACTION_BATTERY_LOW
电池电量充足(即从电量低变化到饱满时会发出广播Intent.ACTION_BATTERY_OKAY
系统启动完成后(仅广播一次)Intent.ACTION_BOOT_COMPLETED
按下照相时的拍照按键(硬件按键)时Intent.ACTION_CAMERA_BUTTON
屏幕锁屏Intent.ACTION_CLOSE_SYSTEM_DIALOGS
设备当前设置被改变时(界面语言、设备方向等)Intent.ACTION_CONFIGURATION_CHANGED
插入耳机时Intent.ACTION_HEADSET_PLUG

未正确移除SD卡但已取出来时(正确移除方法:设置–SD

卡和设备内存–卸载SD卡)

Intent.ACTION_MEDIA_BAD_REMOVAL
插入外部储存装置(如SD卡)Intent.ACTION_MEDIA_CHECKING
成功安装APKIntent.ACTION_PACKAGE_ADDED
成功删除APKIntent.ACTION_PACKAGE_REMOVED
重启设备Intent.ACTION_REBOOT
屏幕被关闭Intent.ACTION_SCREEN_OFF
屏幕被打开Intent.ACTION_SCREEN_ON
关闭系统时Intent.ACTION_SHUTDOWN
重启设备Intent.ACTION_REBOOT

注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播

🔥 有序广播(Ordered Broadcast)🔥 

定义
发送出去的广播被广播接收者按照先后顺序接收

广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者)
按照Priority属性值从大-小排序;
Priority属性相同者,动态注册的广播优先;

 特点
接收广播按顺序接收
先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播

 具体使用
有序广播的使用过程与普通广播非常类似,差异仅在于广播的发送方式:

sendOrderedBroadcast(intent);

 🔥 App应用内广播(Local Broadcast)🔥

背景
Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)

 冲突
可能出现的问题:
其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息;
即会出现安全性 & 效率性的问题。

 解决方案
使用App应用内广播(Local Broadcast)

1、App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
2、相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高

具体使用1 - 将全局广播设置成局部广播
注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
在广播发送和接收时,增设相应权限permission,用于权限验证;
发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
通过**intent.setPackage(packageName)**指定报名

具体使用2 - 使用封装好的LocalBroadcastManager类
使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例
注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册

//注册应用内广播接收器
//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver 
mBroadcastReceiver = new mBroadcastReceiver(); 
IntentFilter intentFilter = new IntentFilter(); 

//步骤2:实例化LocalBroadcastManager的实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);

//步骤3:设置接收广播的类型 
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);

//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);

//发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);

🔥 粘性广播(Sticky Broadcast)🔥 

由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。 

🔥 OnReceive 回调函数中Context 含义 🔥

对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext
对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context
对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context
对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context

 

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

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

相关文章

防御第三天

1.总结当堂NAT与双机热备原理&#xff0c;形成思维导图 2.完成课堂NAT与双机热备实验 fw1: <USG6000V1>sy [USG6000V1]int g0/0/0 [USG6000V1-GigabitEthernet0/0/0]ip add 192.168.18.2 24 [USG6000V1-GigabitEthernet0/0/0]service-manage all permit (地址无所谓&…

SSIS对SQL Server向Mysql数据转发表数据 (二)

1、在SQL Server数据库创建一个数据库表&#xff1a;users USE [Test1] GO/****** Object: Table [dbo].[users] Script Date: 2023/7/27 16:25:11 ******/ SET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOCREATE TABLE [dbo].[users]([id] [int] IDENTITY(1,1) NOT NUL…

C语言实现定时器,定时触发函数

最近想到使用C语言实现一个简单的定时器。使用操作系统windows.h提供的多线程API就能实现 首先定义一个定时器结构体&#xff0c;包含定时时间和触发的函数指针 typedef struct Stimer{int valid;//定时器有效long timingMS;//定时时间TriggerFunc tf;//触发函数 }Stimer;创建…

Sestra 实用教程(三)输入与输出

目 录 一、前言二、分析流程三、输入文件3.1 模型与荷载3.2 分析控制数据 四、输出文件五、参考文献 一、前言 SESAM &#xff08;Super Element Structure Analysis Module&#xff09;是由挪威船级社&#xff08;DNV-GL&#xff09;开发的一款有限元分析&#xff08;FEA&…

解读Spring-context的property-placeholder

在spring中&#xff0c;如果要给程序定义一些参数&#xff0c;可以放在application.properties中&#xff0c;通过<context:property-placeholder>加载这个属性文件&#xff0c;然后就可以通过value给我们的变量自动赋值&#xff0c;如果你们的程序可能运行在多个环境中&…

Qt 5. QSerialPort串口收发

1. 代码 //ex2.cpp #include "ex2.h" #include "ui_ex2.h" #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo>int static cnt 0;Ex2::Ex2(QWidget *parent): QDialog(parent), ui(new Ui::Ex2) {ui->setupUi…

【Golang 接口自动化03】 解析接口返回XML

目录 解析接口返回数据 定义结构体 解析函数&#xff1a; 测试 优化 资料获取方法 上一篇我们学习了怎么发送各种数据类型的http请求&#xff0c;这一篇我们来介绍怎么来解析接口返回的XML的数据。 解析接口返回数据 定义结构体 假设我们现在有一个接口返回的数据resp如…

flutter 打包iOS安装包

flutter iOS Xcode打包并导出ipa文件安装包 1、 Xcode配置 1、 启动打包 1、 等待打包 1、 打包完成、准备导出ipa 1、 选择模式 1、 选择配置文件 1、 导出 1、 选择导出位置 1、 得到ipa

后台管理系统中常见的三栏布局总结:使用element ui构建

vue2 使用 el-menu构建的列表布局&#xff1a; 列表可以折叠展开 <template><div class"home"><header><el-button type"primary" click"handleClick">切换</el-button></header><div class"conte…

小研究 - 主动式微服务细粒度弹性缩放算法研究(三)

微服务架构已成为云数据中心的基本服务架构。但目前关于微服务系统弹性缩放的研究大多是基于服务或实例级别的水平缩放&#xff0c;忽略了能够充分利用单台服务器资源的细粒度垂直缩放&#xff0c;从而导致资源浪费。为此&#xff0c;本文设计了主动式微服务细粒度弹性缩放算法…

webpack联邦模块介绍及在dumi中使用问题整理

文章目录 前言一、ModuleFederationPlugin参数含义&#xff1f;二、如何在dumi中使用及问题整理1. 如何在dumi中使用(这个配置是好使的)2.相关问题整理2.1 问题12.2 问题2 总结 前言 联邦模块&#xff08;Module Federation&#xff09;是指一种用于构建微前端架构的技术&…

Vue3 导出word

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;导出word 目录 1.首先&#xff0c;你需要安装docxtemplater库。可以使用npm或yarn来安装&#xff1a; 2.在Vue组件中&#xff0c;你可以使用docxtemplater来生成Word文档并提供一个导出按钮供用户下载…

[论文笔记] CLRerNet: Improving Confidence of Lane Detection with LaneIoU

Honda, Hiroto, and Yusuke Uchida. “CLRerNet: Improving Confidence of Lane Detection with LaneIoU.” arXiv preprint arXiv:2305.08366 (2023). 2023.05 出的一篇车道线检测的文章, 效果在CULane, CurveLanes SOTA 文章目录 简介LaneIoULineIoU存在问题为什么使用LaneIo…

Elasticsearch:通过动态修剪实现更快的基数聚合

作者&#xff1a;Adrien Grand Elasticsearch 8.9 通过支持动态修剪&#xff08;dynamic pruning&#xff09;引入了基数聚合加速。 这种优化需要满足特定的条件才能生效&#xff0c;但一旦实现&#xff0c;通常会产生惊人的结果。 我们观察到&#xff0c;通过此更改&#xff0…

微软对Visual Studio 17.7 Preview 4进行版本更新,新插件管理器亮相

近期微软发布了Visual Studio 17.7 Preview 4版本&#xff0c;而在这个版本当中&#xff0c;全新设计的扩展插件管理器将亮相&#xff0c;并且可以让用户可更简单地安装和管理扩展插件。 据了解&#xff0c;目前用户可以从 Visual Studio Marketplace 下载各式各样的 VS 扩展插…

【分布鲁棒、状态估计】分布式鲁棒优化电力系统状态估计研究[几种算法进行比较](Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringBoot 8种异步实现方式

前言&#xff1a;异步执行对于开发者来说并不陌生&#xff0c;在实际的开发过程中&#xff0c;很多场景多会使用到异步&#xff0c;相比同步执行&#xff0c;异步可以大大缩短请求链路耗时时间&#xff0c;比如&#xff1a;「发送短信、邮件、异步更新等」&#xff0c;这些都是…

驱动开发 day3 (模块化驱动启动led,蜂鸣器,风扇,震动马达)

模块化驱动启动led,蜂鸣器,风扇,震动马达并加上Makefile 封装模块化驱动&#xff0c;可自由安装卸载驱动&#xff0c;便于驱动更新(附图) 1.安装模块驱动同时初始化各个设备并使能 2.该驱动会自动创建驱动节点. 3.通过c函数程序输入控制各个设备 4.卸载模块驱动 //编译驱动…

Python 日志记录:6大日志记录库的比较

Python 日志记录&#xff1a;6大日志记录库的比较 文章目录 Python 日志记录&#xff1a;6大日志记录库的比较前言一些日志框架建议1. logging - 内置的标准日志模块默认日志记录器自定义日志记录器生成结构化日志 2. Loguru - 最流行的Python第三方日志框架默认日志记录器自定…

Redis(五)—— Redis进阶部分

一、Redis配置文件详解 注意这是Redis服务本身的配置文件&#xff0c;相当于maven的settings.xml&#xff0c;而不是我们在springboot去配置Redis的那个application.yml。 核心部分include 引入其他redis配置文件&#xff0c;相当于spring的<import>bind 设置IP&#xf…