Android 面试题 应用程序结构 十

news2024/11/17 22:20:04

🔥 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.coloradmin.cn/o/814176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【雕爷学编程】MicroPython动手做(21)——掌控板之磁场传感器

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

hooks复杂前端业务解题之道

hooks 大势所趋 2019年年初&#xff0c;react 在 16.8.x 版本正式具备了 hooks 能力&#xff0c;同年6月&#xff1b;尤雨溪在 vue/github-issues 里提出了关于 vue3 Component API 的提案&#xff08;vue hooks的基础&#xff09;。在Vue3的组合式API出现后&#xff0c;githu…

session反序列化+SoapClientSSRF+CRLF

文章目录 session反序列化SoapClientSSRFCRLF前言bestphps revengecall_user_func()方法的特性SSRFCRLF组合拳session反序列化 解题步骤总结 session反序列化SoapClientSSRFCRLF 前言 从一道题分析通过session反序列化出发SoapClientSSRF利用CRLF解题 bestphp’s revenge 首…

计算机毕设 深度学习人体语义分割在弹幕防遮挡上的实现 - python

文章目录 1 课题背景2 技术原理和方法2.1基本原理2.2 技术选型和方法 3 实例分割4 实现效果5 最后 # 1 前言 &#x1f6a9; 深度学习人体语义分割在弹幕防遮挡上的应用 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分…

ChatGPT在教育领域的应用:改变学习方式的前沿技术

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Flutter Add to App 问题记录

前一阵应用中接入了Flutter&#xff0c;使用的是官方的Multiple FlutterEngine管理方案&#xff0c;目前线上运行良好&#xff0c;这里整理一下遇到的问题。 将 Flutter 集成到现有应用整体来说没有什么问题&#xff0c;按照文档的说明结合demo操作就行。接入后多语言&#xf…

【应用层】Http协议总结

文章目录 一、续->Http协议的学习 1.http请求中的get方法和post方法 2.http的状态码 3.http的报头 4.长链接 5.cookie&#xff08;会话保持&#xff09;总结 继续上一篇的内容&#xff1a; 上一篇的最后我们讲到了web根目录&#xff0c;知道…

Git的.gitignore文件、标签管理以及给命令起别名

文章目录 1. 前言2. .gitignore文件3. 标签管理4. 给命令起别名 1. 前言 本文主要讲解Git中容易被忽略但比较重要一些知识:.gitignore文件、标签管理以及给命令起别名. 2. .gitignore文件 在新建仓库时,有一个添加.gitignore 模板: .gitignore 是一个用于指定 Git 忽略特定文…

Mysql第四,五连弹

第四弹 一、&#x1f49b; 主键约束&#xff08;Primary key): 通过这个约束来指定某一个列作为主键&#xff08;1.非空&#xff0c;2.不能重复&#xff09; &#xff0c;主键&#xff1a;一条数据&#xff0c;身份标识&#xff08;类似于内存地址&#xff09; &#x1f604;&a…

Python爬虫Scrapy(二)_入门案例

入门案例 学习目标 创建一个Scrapy项目定义提取的结构化数据(Item)编写爬取网站的Spider并提取出结构化数据(Item)编写Item Pipelines来存储提取到的Item(即结构化数据) 一、新建项目(scrapy startproject) 在开始爬取之前&#xff0c;必须创建一个新的Scrapy项目。进入自定…

计算机毕设 深度学习人体跌倒检测 -yolo 机器视觉 opencv python

文章目录 0 前言1.前言2.实现效果3.相关技术原理3.1卷积神经网络3.1YOLOV5简介3.2 YOLOv5s 模型算法流程和原理4.数据集处理3.1 数据标注简介3.2 数据保存 5.模型训练 6 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题…

[Round#14 Illuminate with your bril]

周末NSS的PWN专题&#xff0c;只作了3个&#xff0c;结束后跟NSS的师傅聊&#xff0c;对方确认了第4题的作法&#xff0c;重作成功。第5题看师傅的WP复现成功。 love 主函数给了个printf&#xff0c;这里可以得到所有地址&#xff0c;并且要求把v4,v5改相等 int __cdecl mai…

Linux 版本的 Abyss Locker 勒索软件针对 VMware ESXi 服务器

Abyss Locker 是最新开发的 Linux 加密器&#xff0c;旨在针对 VMware 的 ESXi 虚拟机平台对企业进行攻击。 随着企业从单个服务器转向虚拟机以实现更好的资源管理、性能和灾难恢复&#xff0c;勒索软件团伙创建了专注于针对该平台的加密器。 随着 VMware ESXi 成为最流行的虚…

UE5 摄像机与NPC重叠阻挡导致视角闪现的解决方法

文章目录 前言问题背景问题剖析摄像机碰撞分析解决方法总结前言 本文基于虚幻5.2.1版本,对摄像机与NPC重叠阻挡导致视角闪现提供一个解决方案,并深入讲解摄像机碰撞原理,提升大家的思维与解决问题的能力。 问题背景 当我们被NPC攻击或者NPC介于摄像机与玩家之间导致摄像机…

小研究 - JVM GC 对 IMS HSS 延迟分析(二)

用户归属服务器&#xff08;IMS HSS&#xff09;是下一代通信网&#xff08;NGN&#xff09;核心网络 IP 多媒体子系统&#xff08;IMS&#xff09;中的主要用户数据库。IMS HSS 中存储用户的配置文件&#xff0c;可执行用户的身份验证和授权&#xff0c;并提供对呼叫控制服务器…

微服务入门---Docker

微服务入门---Docker 1.初识Docker1.1.什么是Docker1.1.1.应用部署的环境问题1.1.2.Docker解决依赖兼容问题1.1.3.Docker解决操作系统环境差异1.1.4.小结 1.2.Docker和虚拟机的区别1.3.Docker架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker架构1.3.4.小结 1.4.安装Docker 2.…

Flutter环境搭建踩坑集锦

Flutter 背景准备工作先检查一下自己的电脑&#xff0c;看一下是不是满足配置要求下载安装配置环境下载安装JDK下载安装Android studio下载Flutterflutter doctor故障Android license status unknownNetwork resources 故障 后记 背景 发现一个不错的框架Flutter&#xff0c;听…

web题型

0X01 命令执行 漏洞原理 没有对用户输入的内容进行一定过滤直接传给shell_exec、system一类函数执行 看一个具体例子 cmd1|cmd2:无论cmd1是否执行成功&#xff0c;cmd2将被执行 cmd1;cmd2:无论cmd1是否执行成功&#xff0c;cmd2将被执行 cmd1&cmd2:无论cmd1是否执行成…

【C++继承】

目录 一、继承的概念及定义1.1继承的概念1.2继承的定义1.2.1定义格式1.2.2继承方式与访问限定符的组合 二、基类和派生类对象赋值转换三、继承中的作用域四、派生类的默认成员函数五、继承与友元六、继承与静态成员七、复杂的菱形继承及菱形虚拟继承八、虚拟继承的原理 一、继承…

(AcWing)满足条件的01序列

给定 n 个 0 和 n 个 1&#xff0c;它们将按照某种顺序排成长度为 2n 的序列&#xff0c;求它们能排列成的所有序列中&#xff0c;能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个。 输出的答案对 10^97 取模。 输入格式 共一行&#xff0c;包含整数 n。 …