【Android】 四大组件详解之活动、服务

文章目录

    • 前言
    • 活动
      • 概述
      • 活动间的通信
      • 活动的生命周期
      • 活动的启动模式
    • 服务
      • 概述
      • 启动和停止服务
      • 活动和服务间的通信
      • 服务的生命周期
      • 使用前台服务
      • 使用IntentService

前言

众所周知,Android的四大组件包括:活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)、内容提供器(Content Provider)。本文先来介绍一下活动以及服务,广播接收器与内容提供器在下一篇文章介绍。

活动

概述

活动是用户进行操作的可视化界面,它为用户提供了一个完成指令的窗口,在App中几乎所有可见的的内容都要依托Activity,所以Activity是开发中使用最频繁的一个组件。在这我们主要来讲解一下活动间的通信、活动的生命周期、活动的启动模式。

活动间的通信

Intent通常用于启动活动、启动服务以及发送广播等场景,这里就来介绍一下启动活动。

显示Intent:

Intent intent=new Intent(this,AnotherActivity.class);
startActivity(intent);
  • 向下一个活动传递数据

例如此时我们有FirstActivity和SecondActivity两个活动

在FirstActivity:

String data="你好";
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);//extra_data相当于一个标识,在下一个活动我们也是通过extra_data来接收数据
startActivity(intent);

这样我们就将“你好”这个数据传递到了SecondActivity,接下来只需要在SecondActivity取出来即可

在SecondActivity:

Intent intent=getIntent();
String data=intent.getStringExtra("extra_data");

字符串类型数据使用getStringExtra()方法接收,整型数据使用getIntExtra()方法接收,布尔型数据使用getBooleanExtra()方法接收

  • 返回数据给上一个活动

当我们从一个活动退出返回到上一个活动时,如果我们想传递数据给上一个活动,就可以使用到这里。

在启动活动时,我们不再使用startActivity()方法,而是使用startActivityForResult()方法,这个方法期望我们启动的活动在销毁时可以返回一个结果给上一个活动。

在FirstActivity:

Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);// 设置请求码为1

在SecondActivity:

Intent intent=new Intent();
intent.putExtra("data_return","你好");
setResult(RESULT_OK,intent);
finish();

接着我们需要在FirstActivity中定义一个onActivityResult()方法来接收返回的数据:

@Override
protected void onActivityResult(int requestCode,int resultCode,Intent data){ //requestCode是我们在启动活动时传入的请求码switch(requestCode){case 1:if(resultCode==RESULT_OK){String returnData=data.getStringExtra("data_return");}break;default:}
}

另外需要注意的是,如果我们是通过点击按钮返回FirstActivity,那么按钮的点击事件就和上面SecondActivity的代码那样即可,但如果我们是通过按下Back键返回到FirstActivity,那我们就要在SecondActivity重写onBackPressed方法了

@Override
public void onBackPressed(){Intent intent=new Intent();intent.putExtra("data_return","你好");setResult(RESULT_OK,intent);finish();
}

启动活动的最佳写法

在原本的启动活动的写法中,当我们要向启动的活动传数据时,我们可能并不知道传什么类型的数据,此时我们就可以换一种启动活动的写法。

在SecondActivity中定义一个actionStart()方法:

public static void actionStart(Context context,String data1,String data2){Intent intent=new Intent(context,SecondActivity.class);intent.putExtra("data_1",data1);intent.putExtra("data_2",data2);context.startActivity(intent);
}

这样一来,我们就清楚了,要启动SecondActivity要传递两个String类型的数据,接着在FirstActivity中启动SecondActivity:

SecondActivity.actionStart(FirstActivity.this,"data1","data2");

活动的生命周期

Android使用栈来管理活动。

活动状态

  • 运行状态:活动位于栈顶时。
  • 暂停状态:活动不再处于栈顶但却仍然可见,如一个对话框形式的活动A出现在一个活动B上面时,对话框并没有占据整个屏幕,此时活动B就处于暂停状态。
  • 停止状态:不处于栈顶且完全不可见的时候。此时系统仍会保存相应的状态和成员变量。但当其他地方需要内存时,处于停止状态的活动可能被回收。
  • 销毁状态:从栈中被移除了。

活动的生存期

Activity中定义了7个回调方法,覆盖了活动声明周期的每一个环节:

  • onCreate():在活动第一次创建时被调用(完成一些初始化操作,加载布局、绑定事件等)
  • onStart():活动由不可见变为可见时调用
  • onResume():活动准备好和用户进行交互时调用,此时活动一定处于栈顶
  • onPause():在系统准备去启动或恢复另一个活动时调用。通常在这个方法中将一些消耗CPU的资源释放,保留一些关键数据。
  • onStop():在活动完全不可见时调用。其与onPause()方法的区别在于当启动的活动是对话框形式的,onPause()会执行,onStop()不会执行
  • onDestory():在活动被销毁前调用,调用后活动的状态将变为销毁状态
  • onRestart():在活动由停止状态变为运行状态之前调用,也就是活动重新启动了。

可以将活动分为三种生存期

  • 完整生存期:onCreate()方法与onDestroy()方法之间经历的。在onCreate()完成各种初始化操作,在onDestroy()完成释放内存操作。
  • 可见生存期:onStart()方法与onStop()方法间经历的。我们可以通过这两个方法对资源经行合理管理,如在onStart()对资源经行加载,在onStop()对资源经行释放。
  • 前台生存期:onResume()方法和onPause()方法间经历的,此时活动总是处于运行状态,活动位于返回栈的栈顶,常在这里与用户进行交互。

活动的启动模式

在AndroidManifest.xml中的<activity></activity>标签通过android:launchMode来指定

  • standard:活动默认的启动模式,在该模式下,系统不会在乎这个活动在栈中是否已经存在,每次启动该活动都会创建一个新的实例。
  • singleTop:在启动这个活动时如果发现返回栈中栈顶已经是该活动了,直接去使用它,不再去新建一个活动实例。
  • singleTask:在启动这个活动时每次去检查返回栈中是否存在活动的实例,存在则直接使用,不存在则创建新的活动实例。
  • singleInstance:指定为该模式的活动会启动一个新的返回栈来管理这个活动。这样做可以实现其他程序和我们这个程序共享这个活动实例,不管哪个程序来访问这个活动,都公用同一个返回栈。

服务

概述

服务是实现程序后台运行的解决方案,允许用户在切换到另一个程序界面的情况下本程序依然能够正常运行,即切换到后台(例如我们听音乐时将音乐软件切到后台)。但需要注意的是,切到后台此时进程并未结束,如果进程被杀掉,依赖于该进程的服务也会停止。

服务并不会自动开启线程,所有代码都是默认在主线程运行的,但这样可能出现主线程被阻塞的情况,所以我们需要在服务内部手动创建子线程,并在这里面执行具体任务,本文后面也会讲解在子线程处理服务的耗时逻辑,所以我们需要了解一下Android多线程编程的知识,可以看看这篇博客Android多线程编程。

启动和停止服务

在此之前,我们先来创建出一个服务,右击项目包名->New->Service->Service,在弹出的窗口有两个可勾选的属性,Exported表示是否允许当前程序被其他程序访问,Enabled表示是否启用这个服务,这里我们全部勾选上。

将创建好的服务添加一些代码,如下所示:

public class MyService extends Service {public MyService() {}@Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.throw new UnsupportedOperationException("Not yet implemented");}//服务创建时调用@Overridepublic void onCreate(){super.onCreate();Log.d("MyService","onCreate executed");}//服务启动时调用@Overridepublic int onStartCommand(Intent intent,int flags,int startId){Log.d("MyService","onStartCommand executed");return super.onStartCommand(intent,flags,startId);}//服务销毁时调用@Overridepublic void onDestroy(){super.onDestroy();Log.d("MyService","onDestroy executed");}
}

通常情况,如果我们希望服务一旦启动就立刻去执行某个动作,就可以将逻辑写在onStartCommand()方法中。当服务被销毁时,在onDestroy()方法中去回收不再使用的资源。还有一点就是onCreate()只有在服务被第一次创建是才会调用,onStartCommand()在每次启动服务时都会调用。

服务的启动和停止也是借助Intent来实现的,如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button start=(Button) findViewById(R.id.start);Button stop=(Button) findViewById(R.id.stop);start.setOnClickListener(this);stop.setOnClickListener(this);}@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.start:Intent startIntent=new Intent(this, MyService.class);//启动服务startService(startIntent);break;case R.id.stop:Intent stopIntent=new Intent(this, MyService.class);//暂停服务stopService(stopIntent);break;default:break;}}
}

其中startService()方法和stopService()方法都是定义在Context类中的,可以直接调用。当然我们也可以在MyService中来自行停止服务,只需在任意地方调用stopSelf()即可。

接着我们就可以通过点击按钮来测试了,如果没问题的话点击开启服务按钮控制台会打印onCreate executed、onStartCommand executed,点击暂停服务按钮控制台会打印onDestroy executed

活动和服务间的通信

还记得我们在创建服务时有一个onBind()方法吗,它允许我们在活动中去控制服务,将服务与活动联系起来。

例如我们想在MyService中实现一个下载功能(模拟),然后希望在活动中可以决定什么时候开始下载,以及可以查看下载进度,要想实现这个功能就需要我们创建一个专门的Binder对象(这里就DownloadBinder了)来对下载功能进行管理了。MyService中代码如下:

public class MyService extends Service {private DownLoadBinder mBinder=new DownLoadBinder();class DownLoadBinder extends Binder{public void startDownload(){Log.d("MainActivity","startDownload executed");}public int getProcess(){Log.d("MainActivity","getProcess executed");return 0;}}@Overridepublic IBinder onBind(Intent intent) {return mBinder;}......
}

在MyService服务中,我们新建了一个DownloadBinder类,让它继承自Binder,并在它的内部实现了开始下载和查看下载进度的方法,当然只是模拟一下。接着在MyService中创建了DownloadBinder的实例mBinder,最后别忘了在onBind()方法中将mBinder返回

接下来我们就可以在活动中去写绑定服务和解绑服务的逻辑了:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {	private MyService.DownLoadBinder downLoadBinder;private ServiceConnection connection=new ServiceConnection() {//绑定成功时调用@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//在这获取了downLoadBinder对象我就可以在任意地方去调用downLoadBinder对象的任意方法了downLoadBinder=(MyService.DownLoadBinder) service;//开始下载downLoadBinder.startDownload();//获取进度downLoadBinder.getProcess();}//活动与服务断开连接时调用@Overridepublic void onServiceDisconnected(ComponentName componentName) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);...Button bindService=(Button) findViewById(R.id.bind_service);Button unbindService=(Button) findViewById(R.id.unbind_service);bindService.setOnClickListener(this);unbindService.setOnClickListener(this);}@Overridepublic void onClick(View view) {switch (view.getId()){...case R.id.bind_service:Intent bindIntent=new Intent(this, MyService.class);//绑定服务bindService(bindIntent,connection,BIND_AUTO_CREATE);break;case R.id.unbind_service://解绑服务unbindService(connection);default:break;}}
}

我们创建出一个ServiceConnection匿名类,在里面重写了onServiceConnection()方法和onServiceDisconnected()方法,分别在活动与服务成功绑定时断开连接时调用。尤其注意onServiceConnected()这个方法,在这里面,我们通过向下转型获得了DownloadBinder的实例,那我们是不是就可以在活动中去随意调用服务中的方法了呢,这样一来我们的活动与服务不就紧密连接起来了。

当然真正让我们的活动与服务绑定起来的代码是bindService(bindIntent,connection,BIND_AUTO_CREATE);,第二个参数是匿名类ServiceConnection的实例,BIND_AUTO_CREATE表示活动与服务绑定之后自动创建服务,这样只会使MyService中的onCreate()方法得到执行,onStartCommand()不执行。

最后:任何一个服务在整个应用程序都是通用的,即MyService可以与任意活动绑定,绑定完获得的是相同的DownloadBinder实例。

服务的生命周期

  • 我们前面在MyService中编写的onCreate()、onStartCommand()、onBind()、onDestory()等方法都是在服务的生命周期中可能回调的方法
  • 一旦调用Context的startService()方法,服务就会启动起来,并回调onStartCommand()方法,如果这个服务之前还没被创建过,onCreate()方法会先于onStartCommand()方法执行。之后服务会一直保持运行状态,直到stopService()或stopSelf()方法被调用。需要注意的是,虽然每调用一次startService()方法onStartCommand()方法就会执行一次,但始终服务只存在一个实例,只需调用一次stopService()或stopSelf()方法服务就会停止。
  • 还可以调用Context的bindService()方法来获取一个服务的持久连接,这时会调用服务中的onBind()方法,如果这个服务之前还没被创建过,onCreate()方法会先于onBind()方法执行。调用方(活动)获得onBind()方法返回的IBinder即可进行通信了
  • 当调用了startService()方法,又调用了stopService()方法,服务中的onDestroy()方法才会执行;同样的调用了bindService()方法,又调用了unbindService()方法,服务中的onDestroy()方法才会执行;那当同时调用了startService()方法和bindService()方法要咋样服务中的onDestroy()方法才会执行呢?答案是同时调用stopService()方法和unbindService()方法

使用前台服务

由于服务的优先级较低,当系统出现内存不足时,就有可能回收掉正在后台运行的服务,如果你不想服务因为内存不足被回收,可以使用前台服务。当然有时候你可能并不是为了防止服务被回收才使用前台服务的,有些特殊需求必须使用前台服务来完成。

前台服务和后台服务最大的区别就是,前台服务会有一个正在运行的图标在系统的状态栏显示,下拉状态栏就可以看到更加详细的信息,类似于通知的效果。

创建一个前台服务如下,在onCreate()方法中:

	@Overridepublic void onCreate(){super.onCreate();Log.d("MyService","OnCreate");//创建notificationManager对通知进行管理NotificationManager notificationManager = getSystemService(NotificationManager.class);if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){String channelId = "channelId";//通知渠道的标识符CharSequence channelName = "...";//通知渠道的位置String channelDescription = "...";//通知渠道的描述//设置通知渠道的级别int importance = NotificationManager.IMPORTANCE_DEFAULT;//创建通知渠道NotificationChannel notificationChannel = new NotificationChannel(channelId,channelName,importance);notificationChannel.setDescription(channelDescription);//在系统中注册消息notificationManager.createNotificationChannel(notificationChannel);}Intent intent = new Intent(this,MainActivity.class);PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);//创建通知Notification notification = new NotificationCompat.Builder(this,"001").setContentTitle("标题").setContentText("内容").setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pi).build();//显示通知startForeground(1,notification);}

调用startForeground()方法可以让你的MyService变成一个前台服务,并在系统状态栏显示出来。

使用IntentService

以上我们的所有代码都是运行在主线程的,如果直接在服务里去做一些耗时逻辑,就非常容易出现ANR(Application Not Responding)的情况。

这里我们先来了解一下什么是ANR,ANR是指在主线程上执行耗时操作或阻塞操作时,无法响应用户输入或与系统交互的情况。当发生ANR时,应用程序会被系统视为未响应的,并可能导致应用崩溃或被用户强制关闭。

这时我们就需要用到Android多线程编程的技术了,我们应该在服务的每个具体的方法里开启一个子线程,在这里去处理那些耗时逻辑,所以一个标准的服务就可以写成以下形式(同时让服务执行完毕自动停下来):

public class MyService extends Service {...@Overridepublic int onStartCommand(Intent intent,int flags,int startId){new Thread(new Runnable(){@Overridepublic void run(){//具体逻辑stopSelf();}}).start();return super.onStartCommand(intent,flags,startId);}
}

为了简化上述步骤,Android提供了一个IntentService类,这个类允许我们简单地创建一个异步的、会自动停止的服务。

首先创建一个MyIntentService继承自IntentService,记得在AndroidManifest.xml文件中注册这个服务:

public class MyIntentService extends IntentService {public MyIntentService() {//调用父类的有参构造super("MyIntentService");}@Overrideprotected void onHandleIntent(@Nullable Intent intent) {//打印当前线程idLog.d("MyIntentService","Thread id is "+Thread.currentThread().getId());}@Overridepublic void onDestroy(){super.onDestroy();Log.d("MyIntentService","onDestroy executed");}
}

在这个类中我们实现了onHandleIntent()这个方法,在这个方法里面我们可以去实现那些耗时逻辑,并且不用担心ANR问题,因为这个方法是在子线程中运行的(通过打印线程id来验证),这里我们还重写了onDestroy()方法,来验证服务执行完会不会自动停止。

在MainActivity中:

		Button startIntentService=(Button) findViewById(R.id.start_intent_service);startIntentService.setOnClickListener(this);@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.start_intent_service:Log.d("MainActivity","Thread id is "+Thread.currentThread().getId());Intent intentService=new Intent(this, MyIntentService.class);startService(intentService);break;default:break;}}

在这通过打印主线程id来与onHandleIntent()方法中线程的id做对比,点击按钮后,你会发现:

在这里插入图片描述

根据打印结果来看,首先MyIntentService的onHandleIntent()方法确实是在子线程中执行的,其次服务执行完后会自动停止

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

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

相关文章

东方博宜 1738. 胜负对决

东方博宜 1738. 胜负对决 以为这道题很简单呢&#xff0c;结果提交两次还不对&#xff0c;气死个人~ 思路&#xff1a;这道题的重点在于看清楚题意&#xff0c;是第奇数个&#xff0c;而不是数是奇数 。 还有&#xff0c;如果按照位数的奇偶来判定&#xff0c;那在读取数组的时…

UltraLight VM-UNet:平行视觉 Mamba 显着减少皮肤病变分割参数

UltraLight VM-UNet&#xff1a;平行视觉 Mamba 显着减少皮肤病变分割参数 摘要IntroductionRelated WorkMethodArchitecture OverviewMamba Parameter Impact AnalysisSkip-connection Path UltraLight VM-UNet: Parallel Vision MambaSignificantly Reduces Parameters for S…

【考研数学】强化跟张宇还是武忠祥?看完这篇就明白了

SO&#xff0c;不管你是选择武忠祥老师还是张宇老师&#xff0c;学长学姐都可以拍着胸脯告诉你&#xff0c;这两位老师都很优秀&#xff0c;跟哪位老师都能学好&#xff0c;所以你也不用担心因为错过一位老师的课而错失整个天下啦~ 根据不同老师的特点&#xff0c;结合自身情况…

RN使用蓝牙扫描

我项目需要用到蓝牙模块,蓝牙扫描到设备并且获取到电量显示到页面上,因此我做了如下demo,使用了react-native-ble-plx这个插件 点击进入官方文档官方文档 1.安卓环境配置(ios暂定,还没做ios,不过下面的方法是兼容的,自行配置ios权限) android/app/src/main/AndroidManifest.xml…

ChatGPT使用小结

ChatGPT是openAI旗下的一款语言模型&#xff0c;说它是语言模型更不如说他是一个全能小助手&#xff0c;自从去年它爆火之后&#xff0c;国内也出现了许多同款语言模型&#xff0c;这些智能语言模型的出现&#xff0c;对于像我这样的低端程序员的效率提升是巨大的。 很多人可能…

(二十七)Flask之数据库连接池DBUtils库

目录&#xff1a; 每篇前言&#xff1a;DBUtils库模式一&#xff08;底层使用threading.local实现&#xff09;&#xff1a;模式二&#xff1a; Flask中使用方式一&#xff1a;直接将DBUtils初始化放到settings.py文件中方式二&#xff1a;从utils文件夹中导入 脚本使用DBUtils…

Qt plugin 开发UI界面插件

目录 1.创建接口 2.创建插件 3.创建插件界面 4.插件实现 5.创建应用工程 6.应用插件 1.创建接口 打开QtCreater&#xff0c;点击左上角“文件”->新建文件或项目&#xff0c;在弹窗中选择C/CHeader File。 输入文件名&#xff0c;选好路径&#xff08;可自行设置名称…

jsonpath在线解析器网址

jsonpath在线解析器网址&#xff1a;https://jsonpath.com/

手动实现简易版RPC(上)

手动实现简易版RPC(上) 前言 什么是RPC&#xff1f;它的原理是什么&#xff1f;它有什么特点&#xff1f;如果让你实现一个RPC框架&#xff0c;你会如何是实现&#xff1f;带着这些问题&#xff0c;开始今天的学习。 本文主要介绍RPC概述以及一些关于RPC的知识&#xff0c;为…

02-结构化程式与自定义函数

视频教程&#xff1a;b站视频【MATLAB教程_台大郭彦甫&#xff08;14课&#xff09;原视频补档】https://www.bilibili.com/video/BV1GJ41137UH/?share_sourcecopy_web&vd_sourc*ed6b9f96888e9c85118cb40c164875dfc 官网教程&#xff1a; MATLAB 快速入门 - MathWorks 中…

【动手学深度学习】15_汉诺塔问题

注&#xff1a; 本系列仅为个人学习笔记&#xff0c;学习内容为《算法小讲堂》&#xff08;视频传送门&#xff09;&#xff0c;通俗易懂适合编程入门小白&#xff0c;需要具备python语言基础&#xff0c;本人小白&#xff0c;如内容有误感谢您的批评指正 汉诺塔&#xff08;To…

基于springboot实现高校学科竞赛平台系统项目【项目源码+论文说明】

基于springboot实现高校学科竞赛平台系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了高校学科竞赛平台的开发全过程。通过分析高校学科竞赛平台管理的不足&#xff0c;创建了一个计算机管理高校学科竞…

day02 51单片机

51单片机学习 1闪烁LED 1.1 需求描述 这个案例,我们要让P00引脚对应的LED按照1秒闪烁1次。 1.2 硬件设计 1.1 软件设计 1)LED闪烁的代码 想让LED闪烁,就需要P00的值不断在0和1之间循环变化。实现这一功能的代码也很简单: #include <STC89C5xRC.H> //包含STC89…

超详细的 Python 文件操作知识!

python进行文件操作&#xff0c;在日常编程中是很常用的。为了方便大家&#xff0c;这里对各种文件操作的知识进行汇总。一文在手&#xff0c;无须它求&#xff01;来一起学习吧。 一、文件的打开和关闭 open()函数 f1 open(rd:\测试文件.txt, moder, encodingutf-8) conte…

《剑指 Offer》专项突破版 - 面试题 105 和 106 : 最大的岛屿和二分图(C++ 实现)

目录 面试题 105 : 最大的岛屿 面试题 106 : 二分图 面试题 105 : 最大的岛屿 题目&#xff1a; 海洋岛屿地图可以用由 0、1 组成的二维数组表示&#xff0c;水平或竖直方向相连的一组 1 表示一个岛屿&#xff0c;请计算最大的岛屿的面积&#xff08;即岛屿中 1 的数目&…

【MATLAB】基于Wi-Fi指纹匹配的室内定位-仿真获取WiFi RSSI数据(附代码)

基于Wi-Fi指纹匹配的室内定位-仿真获取WiFi RSSI数据 WiFi指纹匹配是室内定位最为基础和常见的研究&#xff0c;但是WiFi指纹的采集可以称得上是labor-intensive和time-consuming。现在&#xff0c;给大家分享一下我们课题组之前在做WiFi指纹定位时的基于射线跟踪技术仿真WiFi…

chrome 浏览器 有自带的自动字幕功能,支持英文,控制您的音乐、视频等媒体内容

chrome 浏览器 有自带的自动字幕功能&#xff0c;支持英文&#xff0c;控制您的音乐、视频等媒体内容

【机器学习算法】决策树和随机森林在计算机视觉中的应用

前言 决策树和随机森林在计算机视觉中有着广泛的应用。决策树作为一种简单而强大的分类模型&#xff0c;可以用于图像分类、目标检测、特征提取等任务。它能够根据图像的特征逐层进行判断和分类&#xff0c;从而实现对图像数据的智能分析和理解。随机森林作为一种集成学习方法&…

[dvwa] CSRF

CSRF 0x01 low 跨站&#xff0c;输入密码和确认密码直接写在url中&#xff0c;将连接分享给目标&#xff0c;点击后修改密码 社工方式让目标点击短链接 伪造404页&#xff0c;在图片中写路径为payload&#xff0c;目标载入网页自动请求构造链接&#xff0c;目标被攻击 http…

【STL】栈(stack)

笔者在做下面这道题的时候想到用栈&#xff0c;但写的很麻烦 代码&#xff1a; #include<bits/stdc.h> using namespace std; #define MAXC 255 typedef int SElemType; typedef struct StackNode {SElemType data;struct StackNode *next; }StackNode,*LinkStack; bool…