startActivityForResult被标记为废弃?Activity Result API闪亮登场!

news2024/11/18 10:48:27

本文已同步发表于我的微信公众号,搜索 代码说 即可关注,欢迎与我沟通交流。

文章目录

      • startActivityForResult()被标记为过时
      • registerForActivityResult替代方案
        • 使用示例
        • ActivityResultContract 场景
        • 自定义ActivityResultContract
      • 源码浅析
        • registerForActivityResult()
        • ActivityResultLauncher#launch()
      • 总结
      • 资料

startActivityForResult()被标记为过时

一般我们声明的Activity都会继承自 AppCompatActivity (需要引入androidx.appcompat:appcompat库),而AppCompatActivity -> FragmentActivity -> ComponentActivity (->表示继承)

当我们需要跳转到另一个Activity并需要拿到返回结果时,可以使用startActivityForResult()来实现,然而随着相应库的提高,突然有一天,你会发现startActivityForResult()被标记成过时方法了,如下:

startActivityForResult废弃
点进去看是 ComponentActivity#startActivityForResult()onActivityResult() 被标记为过时:

    /**
     * {@inheritDoc}
     *
     * @deprecated use
     * {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
     * passing in a {@link StartActivityForResult} object for the {@link ActivityResultContract}.
     */
    @Override
    @Deprecated //被标记为过时
    public void startActivityForResult(@SuppressLint("UnknownNullness") Intent intent,
            int requestCode) {
        super.startActivityForResult(intent, requestCode);
    }

   /**
     * {@inheritDoc}
     *
     * @deprecated use
     * {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
     * with the appropriate {@link ActivityResultContract} and handling the result in the
     * {@link ActivityResultCallback#onActivityResult(Object) callback}.
     */
    @CallSuper
    @Override
    @Deprecated //被标记为过时
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

ComponentActivity是通过androidx.activity:activity引入的,通过查看该库的更新记录发现是在 androidx.activity:activity:1.2.0-alpha04 中将其废弃的,所以在这个版本之后,如果我们想使用startActivityForResult(),都会看到过时提醒并推荐使用registerForActivityResult()替代。
1.2.0-alpha04

registerForActivityResult替代方案

使用示例

class ResultApiActivity : AppCompatActivity() {

  //1、注册回调函数
  private var resultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        //处理返回的结果
        val code = result.resultCode //返回码 如:Activity.RESULT_OK、Activity.RESULT_CANCELED
        val data = result.data
        log("resultCode:$code,data:${intent?.getStringExtra(ResultApi2Activity.KEY_TRANSFER)}")
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_result_api)
    mTvResultApi.setOnClickListener {
        //2、启动Intent跳转
        resultLauncher.launch(Intent(this, ResultApi2Activity::class.java))
      }
  }
}
//执行结果: resultCode:-1,data:i'm value from ResultApi2Activity

首先1处在 ComponentActivityFragment 中调用registerForActivityResult()注册一个回调函数来处理其他页面返回的结果,registerForActivityResult() 接受 ActivityResultContractActivityResultCallback 作为参数:

  • ActivityResultContract<I,O>:定义生成结果所需的输入类型(I)输出类型(O),可为Activity传值拍照请求权限等基本 intent 操作提供默认协定,还可以创建自定义协定。
  • ActivityResultCallback< O>:带有onActivityResult(O result)方法的interface类,参数类型是ActivityResultContract<I,O>中定义的输出类型O对象。

registerForActivityResult()方法执行后,返回ActivityResultLauncher类型,用来启动另一个activity,但是此时还没有启动,需要调用ActivityResultLauncher#launch()进行启动

ActivityResultContract 场景

上一节示例中,registerForActivityResult()的第一个参数我们传入的是ActivityResultContracts.StartActivityForResult(),其是ActivityResultContract接口的具体实现类,我们点进ActivityResultContracts看看其他实现类:

ActivityResultContracts.xxxlaunch()入参结果回调说明
StartActivityForResultIntentActivityResult(resultCode, intent)启动另一个Activity并接收其返回结果
RequestPermissionString,如:Manifest.permission.CAMERABoolean请求一个权限并接收其授权结果
RequestMultiplePermissionsString[],如arrayOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE)Map<String, Boolean>,如:for ((permission, isGranted) in permissions) {if (isGranted) {// 权限已授权} else {// 权限未授权}}请求多个权限并接收其授权结果
CaptureVideoUri
@RequiresApi(19) CreateDocumentString,如:launcher.launch("document.txt")Uri创建一个新的文档并返回其URI
GetContentString,如:launcher.launch("image/*")Uri打开文件选择器并获取所选文件的URI
@RequiresApi(18) GetMultipleContentsStringList< Uri>打开文件选择器并获取所选多个文件的URI
@RequiresApi(19) OpenDocumentString[]Uri打开现有文档并返回其URI
@RequiresApi(21) OpenDocumentTreeUriUri打开文档树并返回所选目录的URI
@RequiresApi(19) OpenMultipleDocumentsString[]List< Uri>打开多个现有文档并返回它们的URI
PickContactVoidUri打开联系人应用程序并返回所选联系人的URI
StartIntentSenderForResultIntentSenderRequestActivityResult启动一个IntentSender并接收其返回结果
TakePictureUriBoolean启动相机应用程序并拍摄照片
TakePicturePreviewVoidBitmap启动相机应用程序并拍摄预览照片

自定义ActivityResultContract

通常来说上一节介绍的ActivityResultContract已经够我们平时开发使用了,不过系统还是提供了自定义ActivityResultContract的能力。

自定义ActivityResultContract可以让我们根据自己的需求创建一个新的ActivityIntent操作,并使用registerForActivityResult()方法将其与ActivityResultLauncher对象绑定。以下是一个自定义ActivityResultContract的示例:

/**
 * 1、自定义ActivityResultContract
 */
class CustomContract : ActivityResultContract<Void, String>() {
    companion object {
        const val DEFAULT_VALUE = "default_value"
    }

    /**
     * 创建Intent
     * @param context Context
     * @param input 当前类的第一个泛型参数
     * @return
     */
    override fun createIntent(context: Context, input: Void?): Intent {
        return Intent(context, ResultApi2Activity::class.java)
    }

    /**
     * 解析结果,类似于Activity#onActivityResult
     * @param resultCode 返回码 [Activity.setResult] 的 resultCode
     * @param intent [Activity.setResult] 的 intent
     * @return
     */
    override fun parseResult(resultCode: Int, intent: Intent?): String {
        if (resultCode != Activity.RESULT_OK || intent == null) return DEFAULT_VALUE
        return intent.getStringExtra(ResultApi2Activity.KEY_TRANSFER) ?: DEFAULT_VALUE
    }
}

 //2、Activity中使用自定义Contract
 private val mLauncher =
        registerForActivityResult(CustomContract()) { result -> log("return result:$result") }
 //点击事件中触发launch
 mTvCustomContract.setOnClickListener { mLauncher.launch(null)}

//3、目标Activity
class ResultApi2Activity : AppCompatActivity() {
    companion object{
        const val KEY_TRANSFER = "key_transfer"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setResult(RESULT_OK, intent.putExtra(KEY_TRANSFER, "i'm value from ResultApi2Activity"))
    }
}

首先自定义ActivityResultContract,重写了其中的createIntent()parseResult()方法。createIntent()方法用于创建一个新的Intent对象,因为输入参数为Void,所以不需要传数据给目标ActivityparseResult()方法用于解析目标Activity返回的结果,并将其转换为输出类型String

接着,使用registerForActivityResult()方法将CustomContractActivityResultLauncher对象绑定,并在需要启动该Activity时使用该对象的launch()启动它。

源码浅析

上述示例中主要涉及两个方法,分别为registerForActivityResult()ActivityResultLauncher#launch()方法,下面重点看下这两个方法。

registerForActivityResult()

    //ComponentActivity.java
    @NonNull
    @Override
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return registerForActivityResult(contract, mActivityResultRegistry, callback);
    }

    @NonNull
    @Override
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultRegistry registry,
            @NonNull final ActivityResultCallback<O> callback) {
        return registry.register(
                "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
    }

可以看到最终调用了ActivityResultRegistry#register()方法:

    public final <I, O> ActivityResultLauncher<I> register(
            @NonNull final String key,
            @NonNull final LifecycleOwner lifecycleOwner,
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultCallback<O> callback) {

        Lifecycle lifecycle = lifecycleOwner.getLifecycle();

        //1、判断Lifecycle的状态必须小于Lifecycle.State.STARTED,否则直接抛异常
        if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
            throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
                    + "attempting to register while current state is "
                    + lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
                    + "they are STARTED.");
        }

        //2、生成唯一的 requestCode
        final int requestCode = registerKey(key);
        //3、获取或初始化生命周期容器LifecycleContainer
        LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
        if (lifecycleContainer == null) {
            lifecycleContainer = new LifecycleContainer(lifecycle);
        }
        //4、注册观察者并将其添加到LifecycleContainer中
        LifecycleEventObserver observer = new LifecycleEventObserver() {
            @Override
            public void onStateChanged(
                    @NonNull LifecycleOwner lifecycleOwner,
                    @NonNull Lifecycle.Event event) {
                if (Lifecycle.Event.ON_START.equals(event)) {
                    mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
                    if (mParsedPendingResults.containsKey(key)) {
                        final O parsedPendingResult = (O) mParsedPendingResults.get(key);
                        mParsedPendingResults.remove(key);
                        callback.onActivityResult(parsedPendingResult);
                    }
                    final ActivityResult pendingResult = mPendingResults.getParcelable(key);
                    if (pendingResult != null) {
                        mPendingResults.remove(key);
                        callback.onActivityResult(contract.parseResult(
                                pendingResult.getResultCode(),
                                pendingResult.getData()));
                    }
                } else if (Lifecycle.Event.ON_STOP.equals(event)) {
                    mKeyToCallback.remove(key);
                } else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
                    unregister(key);
                }
            }
        };
        lifecycleContainer.addObserver(observer);
        mKeyToLifecycleContainers.put(key, lifecycleContainer);
    
        //5、初始化ActivityResultLauncher对象并返回
        return new ActivityResultLauncher<I>() {
            @Override
            public void launch(I input, @Nullable ActivityOptionsCompat options) {
                mLaunchedKeys.add(key);
                onLaunch(requestCode, contract, input, options);
            }

            @Override
            public void unregister() {
                ActivityResultRegistry.this.unregister(key);
            }

            @NonNull
            @Override
            public ActivityResultContract<I, ?> getContract() {
                return contract;
            }
        };
    }
  • 1处判断当前Lifecycle的状态必须小于Lifecycle.State.STARTED,否则直接抛异常。也就是说registerForActivityResult()必须在onStart()之前调用。
  • 2处注册键值,该方法会调用registerKey()方法生成一个唯一的requestCode,并将keyrequestCode绑定起来。
  • 3处从mKeyToLifecycleContainers集合中获取与key对应的LifecycleContainer对象。如果不存在,则会创建一个新的LifecycleContainer对象,并将其与lifecycle绑定起来。
  • 4处创建一个LifecycleEventObserver对象,并将其添加到3处的LifecycleContainer对象中。该观察者会在LifecycleOwner对象的生命周期发生变化时执行相应的操作,例如在STARTED状态时将CallbackAndContract对象添加到mKeyToCallback集合中,在STOPPED状态时将其从集合中移除,在DESTROYED状态时调用unregister()方法注销ActivityResultLauncher对象等。
  • 5处返回一个ActivityResultLauncher对象,该对象包含了launch()unregister()getContract()三个方法。其中,launch()方法用于启动ActivityIntent操作,unregister()方法用于注销ActivityResultLauncher对象,getContract()方法用于获取与之绑定的ActivityResultContract对象。

ActivityResultLauncher#launch()

上一节中registerForActivityResult()返回了ActivityResultLauncher对象,当启动跳转时,只需要调用launch()方法即可:

public void launch(I input) {
   launch(input, null);
}

public abstract void launch( I input, ActivityOptionsCompat options);

可以看到最终调用的 launch( I input, ActivityOptionsCompat options) 方法是一个abstract 抽象方法,其具体实现自然是上一节5处初始化ActivityResultLauncher的地方,可以看到其内部又调用了ActivityResultRegistry#onLaunch(requestCode, contract, input, options) 方法,继续找ActivityResultRegistry初始化的地方,可以找到在ComponentActivity中初始化了ActivityResultRegistry

 this.mActivityResultRegistry = new ActivityResultRegistry() {
       public <I, O> void onLaunch(final int requestCode, @NonNull ActivityResultContract<I, O> contract, I input, @Nullable ActivityOptionsCompat options) {
                ComponentActivity activity = ComponentActivity.this;
                final ActivityResultContract.SynchronousResult<O> synchronousResult = contract.getSynchronousResult(activity, input);
                //1、同步检查结果,如果不为空,直接返回contract.getSynchronousResult中的数据
                if (synchronousResult != null) {
                    (new Handler(Looper.getMainLooper())).post(new Runnable() {
                        public void run() {
                            dispatchResult(requestCode, synchronousResult.getValue());
                        }
                    });
                } else {
                //2、创建Intent对象
                    Intent intent = contract.createIntent(activity, input);
                    Bundle optionsBundle = null;
                    if (intent.getExtras() != null && intent.getExtras().getClassLoader() == null) {
                        intent.setExtrasClassLoader(activity.getClassLoader());
                    }

                    if (intent.hasExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE")) {
                        optionsBundle = intent.getBundleExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
                        intent.removeExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
                    } else if (options != null) {
                        optionsBundle = options.toBundle();
                    }

                    if ("androidx.activity.result.contract.action.REQUEST_PERMISSIONS".equals(intent.getAction())) {
                        String[] permissions = intent.getStringArrayExtra("androidx.activity.result.contract.extra.PERMISSIONS");
                        if (permissions == null) {
                            permissions = new String[0];
                        }

                        ActivityCompat.requestPermissions(activity, permissions, requestCode);
                    } else if ("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST".equals(intent.getAction())) {
                        IntentSenderRequest request = (IntentSenderRequest)intent.getParcelableExtra("androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST");

                        try {
                            ActivityCompat.startIntentSenderForResult(activity, request.getIntentSender(), requestCode, request.getFillInIntent(), request.getFlagsMask(), request.getFlagsValues(), 0, optionsBundle);
                        } catch (final IntentSender.SendIntentException var11) {
                            (new Handler(Looper.getMainLooper())).post(new Runnable() {
                                public void run() {
                                    dispatchResult(requestCode, 0, (new Intent()).setAction("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST").putExtra("androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION", var11));
                                }
                            });
                        }
                    } else {
                        ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
                    }

                }
            }
        };
  • 1处用ActivityResultContractgetSynchronousResult()方法获取同步结果。如果存在同步结果,则会将其返回值通过dispatchResult()方法分发出去。
  • 如果1处不存在同步结果,那么就会走2处逻辑,调用ActivityResultContractcreateIntent()方法创建一个新的Intent对象,并根据需要设置ActivityIntent操作的选项。
  • 检查Intent对象的Action属性是否为"androidx.activity.result.contract.action.REQUEST_PERMISSIONS""androidx.activity.result.contract.action.INTENT_SENDER_REQUEST"。如果是,则会分别调用ActivityCompat.requestPermissions()方法请求权限或ActivityCompat.startIntentSenderForResult()方法启动相应的操作;如果都不是,则默认调用ActivityCompat.startActivityForResult()方法启动ActivityIntent操作。
    //ComponentActivity.java
    //1、startActivityForResult()返回结果
    @Deprecated
    @CallSuper
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (!this.mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    //2、权限申请的结果
    @Deprecated
    @CallSuper
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (!this.mActivityResultRegistry.dispatchResult(requestCode, -1, (new Intent()).putExtra("androidx.activity.result.contract.extra.PERMISSIONS", permissions).putExtra("androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS", grantResults)) && VERSION.SDK_INT >= 23) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
  
     //分发ActivityResultLauncher对象的返回结果
    @MainThread
    public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
        String key = mRcToKey.get(requestCode);
        if (key == null) {
            return false;
        }
        mLaunchedKeys.remove(key);

        doDispatch(key, resultCode, data, mKeyToCallback.get(key));
        return true;
    }

    //3、处理返回结果
    private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
            @Nullable CallbackAndContract<O> callbackAndContract) {
        if (callbackAndContract != null && callbackAndContract.mCallback != null) {
            ActivityResultCallback<O> callback = callbackAndContract.mCallback;
            ActivityResultContract<?, O> contract = callbackAndContract.mContract;
            callback.onActivityResult(contract.parseResult(resultCode, data));
        } else {
            // Remove any parsed pending result
            mParsedPendingResults.remove(key);
            // And add these pending results in their place
            mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
        }
    }

主要来看3处的doDispatch()方法:

  • 检查callbackAndContract对象是否为空,如果不为空,则会调用ActivityResultCallback对象的onActivityResult()方法处理返回结果。到这里执行结果就会在registerForActivityResult()中的第2个参数中收到结果
  • 如果上述不成立,会将返回结果存储到mPendingResults集合中,以便稍后使用。

总结

  • 个人认为startActivityForResult()/onActivityResult()、onRequestPermissionsResult()被标记成过时并不是因为有什么性能问题或者bug,而是通过registerForActivityResult()统一进行收口,为什么这么说呢,因为registerForActivityResult()内部最终还是调用了ComponentActivity中的startActivityForResult()、onActivityResult()、onRequestPermissionsResult()方法,只不过是对其进行了统一的封装而已,简化了使用。
  • 使用 registerForActivityResult(),可以通过一个 lambda 表达式来处理返回结果,避免了繁琐的 requestCodeonActivityResult 的处理逻辑。此外,registerForActivityResult 还支持多个请求码和多个返回结果,使得代码更加清晰和易于维护。

资料

【1】android官网:Activity Result API

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

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

相关文章

(4.2)STM32中断系统

目录 1.中断基本概念 2.中断的意义 3.中断处理过程 4. 中断体系结构 5.NVIC 6.EXTI 1.中断基本概念 在处理器中&#xff0c;中断相当于对于突发事件的处理过程。 当遇到内部/外部的紧急事件需要处理时&#xff0c;暂时中止当前程序&#xff0c;转而去处理紧急事件&#xff0c; …

使用 ESP32 UWB DW3000进行测距和定位

什么是超宽带及其工作原理? UWB 是一种类似于蓝牙或 Wi-Fi 的短距离无线通信协议。它还使用无线电波进行通信并以非常高的频率运行。顾名思义,它还使用几 GHz 的宽频谱。可以将其想象成一种雷达,可以连续扫描整个房间并像激光束一样精确锁定物体以发现其位置并传输数据。 超…

08-用户权限控制

1、用户权限控制 权限控制是什么:控制用户对系统资源(URI)的操作。 前端的权限控制:对页面或页面元素的权限控制。 > 页面访问权限:哪些页面可以访问、哪些页面元素可见等等。 > 操作权限:如页面按钮是否可点击、是否可以增删改查等等 后端的权限控制:对接口及…

网络协议之HTTP详细解释

文章目录 前言一.Web发展的阶段二.http协议初识2.1 概念2.2 http的发展史2.3 http协议的作用2.4http协议的格式 三.抓包工具的介绍什么是抓包抓包工具的原理抓包工具的使用和下载- 具体下载过程:- 使用过程 四.HTTP请求请求行请求报头 五.HTTP响应状态行响应报头响应正文 前言 …

Java+Redis实现撤销重做功能

文章目录 1.背景2.需求分析3.实现逻辑分析4.统一过期时间设置5.初始图表栈6.记录图表变化7.撤销操作8.重做操作9.删除图表处理 1.背景 ​ 在一个编辑页面中&#xff0c;存在多个图表&#xff0c;对图表的配置操作允许撤销和重做&#xff1b;撤销和重做只是针对页面中图…

马斯克宣布将卸任推特 CEO:已找到接班人,自己要去当 CTO

作者 | 李冬梅 来源 | AI前线 ID | ai-front 当地时间 5 月 11 日&#xff0c;马斯克在特推上发文宣布&#xff0c;他将在 6 个星期后正式卸任推特 CEO 一职&#xff0c;并且已经找到了一位女性接班人&#xff0c;自己将转到技术岗位。 马斯克在推文中写道&#xff1a;“很…

Yolov5轻量级:EfficientViT, better speed and accuracy

EfficientViT: Memory Efficient Vision Transformer with Cascaded Group Attention 论文:https://arxiv.org/abs/2305.07027 代码:Cream/EfficientViT at main microsoft/Cream GitHub 🏆🏆🏆🏆🏆🏆Yolo轻量化模型🏆🏆🏆🏆🏆🏆 近些年对视觉Tra…

Java【网络原理3】TCP 协议的确认应答、超时重传机制

文章目录 前言一、确认应答1, 什么是确认应答2, 序列号和确认应答号 二、超时重传1, 什么是超时重传 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系…

【redis】redis为什么这么快?高性能设计之epoll和I/O多路复用深度解析

系列文章目录 文章目录 系列文章目录前言一、before 学习I/O多路复用之前多路复用 需要解决的问题 一对一性能差结论 需要让一个进程同时处理多个连接 二、I/O多路复用模型1、是什么&#xff1f;一句话 2、redis单线程如何处理那么多并发客户端连接&#xff0c;为什么单线程&am…

Edge插件之WeTab,画面优美,可以免费使用chatgpt,很难不爱

目录 一、普通的edge新标签页 二、安装WeTab插件 1.WeTab插件的安装非常简单&#xff0c;只需在百度搜索wetab&#xff0c;进入官网&#xff1a; 2.进入官网&#xff0c;点击edge图标&#xff0c;进入插件下载页面&#xff1a; 3.这里由于我是已经安装成功&#xff0c;显示…

无法上网问题解决过程

下班&#xff0c;收到一同事在群里说&#xff0c;环境里有冒充网关的mac的&#xff0c;现在无法上网&#xff0c;让arp -s ip mac地址&#xff0c;先绑定正确的网关mac地址&#xff0c;先临时使用&#xff0c;等第二天上班再查找原因。 不能上网原因&#xff1a; 1、环境…

Cloud Studio 内核升级之触手可及

前言 Cloud Studio是基于浏览器的集成式开发环境&#xff08;IDE&#xff09;&#xff0c;为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装&#xff0c;随时随地打开浏览器就能使用。云端开发体验与本地几乎一样&#xff0c;上手门槛更低&#x…

IMU和GPS融合定位(ESKF)

说明 1.本文理论部分参考文章https://zhuanlan.zhihu.com/p/152662055和https://blog.csdn.net/brightming/article/details/118057262 ROS下的实践参考https://blog.csdn.net/qinqinxiansheng/article/details/107108475和https://zhuanlan.zhihu.com/p/163038275 理论 坐标…

三年测试,月薪才12k,想跳槽又不太敢.....

在我们的身边&#xff0c;存在一个普遍现象&#xff1a;很多人从事软件测试岗&#xff0c;不计其数&#xff0c;经历的心酸难与外人道也。可是技术确难以提升、止步不前&#xff0c;薪资也只能看着别人水涨船高&#xff0c;自己却没有什么起色。 虽然在公司里属于不可缺少的一…

java学习笔记

java学习笔记 直接写出来的人可以理解的数据&#xff0c;在java中叫做字面量。 字面量分类&#xff1a; 数据类型分类&#xff1a; 不同的数据类型分配了不同的内存空间&#xff0c;不同的内存空间&#xff0c;所存储的数据大小是不一样的。 数据类型内存占用和取值范围…

JavaSE入门必读篇——详解数组

文章目录 数组的概念1.什么是数组呢&#xff1f;2.如何创建数组3.遍历数组4.扩展&#xff1a;快速批量初始化 数组原理内存图1. 内存概述2.Java虚拟机的内存划分3.其存储方式图4.认识null 二维数组二维数组初始化遍历二维数组 数组常见异常1. 数组越界异常2. 数组空指针异常 Ja…

Windows下编译安装gRPC

gRPC是Google基于HTTP/2协议开发的一套开源、跨平台的高性能RPC框架&#xff0c;可用于连接微服务架构内的各种服务&#xff0c;亦可以连接客户端与后端服务。 Ref. from gRPC gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can…

代码随想录算法训练营第二十四天|理论基础 77. 组合

文章目录 理论基础77.组合思路代码总结 理论基础 回溯算法&#xff1a;一种暴力搜索方式 回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 回溯法&#xff0c;一般可以解决如下几种问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合切割问题…

数据安全技术工作部成员动态 | 鸿翼联合天空卫士打造“基于内容的敏感信息处理”解决方案

据2022年统计数据表明&#xff0c;因IT故障、人为错误、供应链攻击、破坏性攻击和勒索软件攻击等原因导致的数据泄露事件频繁发生&#xff0c;信息安全问题比以往任何一个时代都更为突出。信息泄漏造成的危害体现在多方面&#xff0c;不法分子通过各种途径收集的公司的某些重要…

大数据法律监督模型优势特色及应用场景

大数据法律监督平台是基于监督数据整合管理平台、监督模型构建平台、内置模型库以及法律监督线索管理平台打造的一套服务于检察机关法律监督工作的专业化系统。通过数据采集、融合、挖掘、建模、展现等一系列能力&#xff0c;辅助检察官从纷繁复杂的数据中&#xff0c;开展多维…