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

Android 之 MediaPlayer 播放音频与视频

本节引言:

本节带来的是Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码 和播放音视频。它支持三种不同的媒体来源:

  • 本地资源
  • 内部的URI,比如你可以通过ContentResolver来获取
  • 外部URL(流) 对于Android所支持的的媒体格式列表

对于Android支持的媒体格式列表,可见:Supported Media Formats 文档

本节我们就来用MediaPlayer来写个简单的播放音视频的例子!

官方API文档:MediaPlayer


1.相关方法详解


1)获得MediaPlayer实例:

可以直接new或者调用create方法创建:

MediaPlayer mp = new MediaPlayer();
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);  //无需再调用setDataSource

另外create还有这样的形式: create(Context context, Uri uri, SurfaceHolder holder) 通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器


2)设置播放文件:

//①raw下的资源:
MediaPlayer.create(this, R.raw.test);//②本地文件路径:
mp.setDataSource("/sdcard/test.mp3");//③网络URL文件:
mp.setDataSource("http://www.xxx.com/music/test.mp3");

另外setDataSource()方法有多个,里面有这样一个类型的参数:FileDescriptor,在使用这个 API的时候,需要把文件放到res文件夹平级的assets文件夹里,然后使用下述代码设置DataSource:

AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());

3)其他方法

  • getCurrentPosition( ):得到当前的播放位置
  • getDuration() :得到文件的时间
  • getVideoHeight() :得到视频高度
  • getVideoWidth() :得到视频宽度
  • isLooping():是否循环播放
  • isPlaying():是否正在播放
  • pause():暂停
  • prepare():准备(同步)
  • prepareAsync():准备(异步)
  • release():释放MediaPlayer对象
  • reset():重置MediaPlayer对象
  • seekTo(int msec):指定播放的位置(以毫秒为单位的时间)
  • setAudioStreamType(int streamtype):指定流媒体的类型
  • setDisplay(SurfaceHolder sh):设置用SurfaceHolder来显示多媒体
  • setLooping(boolean looping):设置是否循环播放
  • setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener): 网络流媒体的缓冲监听
  • setOnCompletionListener(MediaPlayer.OnCompletionListener listener): 网络流媒体播放结束监听
  • setOnErrorListener(MediaPlayer.OnErrorListener listener): 设置错误信息监听
  • setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener): 视频尺寸监听
  • setScreenOnWhilePlaying(boolean screenOn):设置是否使用SurfaceHolder显示
  • setVolume(float leftVolume, float rightVolume):设置音量
  • start():开始播放
  • stop():停止播放

2.使用代码示例

示例一:使用MediaPlayer播放音频:

运行效果图

关键代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener{private Button btn_play;private Button btn_pause;private Button btn_stop;private MediaPlayer mPlayer = null;private boolean isRelease = true;   //判断是否MediaPlayer是否释放的标志@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bindViews();}private void bindViews() {btn_play = (Button) findViewById(R.id.btn_play);btn_pause = (Button) findViewById(R.id.btn_pause);btn_stop = (Button) findViewById(R.id.btn_stop);btn_play.setOnClickListener(this);btn_pause.setOnClickListener(this);btn_stop.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.btn_play:if(isRelease){mPlayer = MediaPlayer.create(this,R.raw.fly);isRelease = false;}mPlayer.start();   //开始播放btn_play.setEnabled(false);btn_pause.setEnabled(true);btn_stop.setEnabled(true);break;case R.id.btn_pause:mPlayer.pause();     //停止播放btn_play.setEnabled(true);btn_pause.setEnabled(false);btn_stop.setEnabled(false);break;case R.id.btn_stop:mPlayer.reset();     //重置MediaPlayermPlayer.release();   //释放MediaPlayerisRelease = true;btn_play.setEnabled(true);btn_pause.setEnabled(false);btn_stop.setEnabled(false);break;}}
}

注意事项:

播放的是res/raw目录下的音频文件,创建MediaPlayer调用的是create方法,第一次启动播放前 不需要再调用prepare(),如果是使用构造方法构造的话,则需要调用一次prepare()方法! 另外贴下官方文档中,从其他两种途径播放音频的示例代码:

本地Uri

Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

外部URL

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

Note:假如你通过一个URL以流的形式播放在线音频文件,该文件必须可以进行 渐进式下载


示例二:使用MediaPlayer播放视频

MediaPlayer主要用于播放音频,没有提供图像输出界面,所以我们需要借助其他的 组件来显示MediaPlayer播放的图像输出,我们可以使用用SurfaceView 来显示,下面我们使用SurfaceView来写个视频播放的例子:

运行效果图

实现代码

布局文件:activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="5dp"><SurfaceViewandroid:id="@+id/sfv_show"android:layout_width="match_parent"android:layout_height="300dp" /><Buttonandroid:id="@+id/btn_start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始" /><Buttonandroid:id="@+id/btn_pause"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="暂停 " /><Buttonandroid:id="@+id/btn_stop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="终止" /></LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener, SurfaceHolder.Callback {private MediaPlayer mPlayer = null;private SurfaceView sfv_show;private SurfaceHolder surfaceHolder;private Button btn_start;private Button btn_pause;private Button btn_stop;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bindViews();}private void bindViews() {sfv_show = (SurfaceView) findViewById(R.id.sfv_show);btn_start = (Button) findViewById(R.id.btn_start);btn_pause = (Button) findViewById(R.id.btn_pause);btn_stop = (Button) findViewById(R.id.btn_stop);btn_start.setOnClickListener(this);btn_pause.setOnClickListener(this);btn_stop.setOnClickListener(this);//初始化SurfaceHolder类,SurfaceView的控制器surfaceHolder = sfv_show.getHolder();surfaceHolder.addCallback(this);surfaceHolder.setFixedSize(320, 220);   //显示的分辨率,不设置为视频默认}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_start:mPlayer.start();break;case R.id.btn_pause:mPlayer.pause();break;case R.id.btn_stop:mPlayer.stop();break;}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {mPlayer = MediaPlayer.create(MainActivity.this, R.raw.lesson);mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mPlayer.setDisplay(surfaceHolder);    //设置显示视频显示在SurfaceView上}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}@Overrideprotected void onDestroy() {super.onDestroy();if (mPlayer.isPlaying()) {mPlayer.stop();}mPlayer.release();}
}

代码很简单,布局有个SurfaceView,然后调用getHolder获得一个SurfaceHolder对象, 在这里完成SurfaceView相关的设置,设置了显示的分辨率以及一个Callback接口, 重写了SurfaceView创建时,发生变化时,以及销毁时的三个方法!然后按钮控制播放 以及暂停而已~


示例三:使用VideoView播放视频

除了使用MediaPlayer + SurfaceView播放视频的方式,我们还可以使用VideoView来直接 播放视频,我们稍微改点东西就可以实现视频播放!运行效果和上面的一致,就不贴了, 直接上代码!

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private VideoView videoView;private Button btn_start;private Button btn_pause;private Button btn_stop;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bindViews();}private void bindViews() {videoView = (VideoView) findViewById(R.id.videoView);btn_start = (Button) findViewById(R.id.btn_start);btn_pause = (Button) findViewById(R.id.btn_pause);btn_stop = (Button) findViewById(R.id.btn_stop);btn_start.setOnClickListener(this);btn_pause.setOnClickListener(this);btn_stop.setOnClickListener(this);//根据文件路径播放if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/lesson.mp4");}//读取放在raw目录下的文件//videoView.setVideoURI(Uri.parse("android.resource://com.jay.videoviewdemo/" + R.raw.lesson));videoView.setMediaController(new MediaController(this));}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_start:videoView.start();break;case R.id.btn_pause:videoView.pause();break;case R.id.btn_stop:videoView.stopPlayback();break;}}
}

相关文章:

Android 之 MediaPlayer 播放音频与视频

本节引言&#xff1a; 本节带来的是Android多媒体中的——MediaPlayer&#xff0c;我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件&#xff0c;通过该类&#xff0c;我们可以以最小的步骤来获取&#xff0c;解码 和播放音视频。它支持三种不同的…...

React中事件处理器的基本使用

在React中&#xff0c;为了提高性能&#xff0c;跨浏览器兼容性和开发体验&#xff0c;React实现了一套自己的事件机制&#xff0c;利用事件委托和合成事件的方式统一管理事件订阅和分发。 为了让组件能够响应用户的交互行为&#xff0c;React提供了一系列的事件处理器&#xf…...

RobotFramework

一、RobotFramework的简介和特点 1、关键字驱动&#xff1a; 把项目中的业务逻辑封装成一个一个的关键字&#xff0c;然后调用不同的关键字组成不同的业务 2、数据驱动 把测试数据放到excel&#xff1a;yaml文件中 通过改变文件中的数据去驱动测试用例执行 3、特点&#xff…...

【Matplotlib 绘制折线图】

使用 Matplotlib 绘制折线图 在数据可视化中&#xff0c;折线图是一种常见的图表类型&#xff0c;用于展示随着变量的变化&#xff0c;某个指标的趋势或关系。Python 的 Matplotlib 库为我们提供了方便易用的功能来绘制折线图。 绘制折线图 下面的代码展示了如何使用 Matplo…...

ARM汇编基本变量的定义和使用

一、ARM汇编中基本变量是什么? 数字变量: GBLA LCLA SETA 逻辑变量:GBLL LCLL SETL 字符串:GBLS LCLS SETLS 注意需要TAB键定义变量和行首改变值 二、使用步骤 1.引入库 代码如下(示例): GBLA led_num Reset_Handler PROCEXPORT Reset_Handler [WEA…...

排序算法汇总

每日一句&#xff1a;你的日积月累终会成为别人的望尘莫及 目录 常数时间的操作 选择排列 冒泡排列 【异或运算】 面试题&#xff1a; 1&#xff09;在一个整形数组中&#xff0c;已知只有一种数出现了奇数次&#xff0c;其他的所有数都出现了偶数次&#xff0c;怎么找到…...

cocos2d 中UserDefault在windows平台下的路径问题

在使用cocos2dx c开发项目时&#xff0c;通常使用cocos自带的UserDefault来存储一些项目所用到的一些配置信息&#xff1a;如游戏的音量&#xff0c;游戏的闯关数等... 但是windows平台下&#xff0c;测试发现如果用户的帐户名使用是中文&#xff0c;在启动程序时会报错&#…...

ChatGPT与高等教育变革:价值、影响及未来发展

最近一段时间&#xff0c;ChatGPT吸引了社会各界的目光&#xff0c;它可以撰写会议通知、新闻稿、新年贺信&#xff0c;还可以作诗、写文章&#xff0c;甚至可以撰写学术论文。比尔盖茨、马斯克等知名人物纷纷为此发声&#xff0c;谷歌、百度等知名企业纷纷宣布要提供类似产品。…...

Matlab Image Processing toolbox 下载安装方法

当安装好Matlab之后&#xff0c;发现没有Image Processing toolbox这个图像处理工具箱 从新安装一遍&#xff0c; 选上 Image Processing toolbox 但是不用选matlab即可 1.找到之前安装时的Setup安装程序包&#xff0c;按照之前安装Matlab步骤&#xff0c;到选择需要安装的Ma…...

什么是消息键(Key)?如何使用消息键进行消息顺序性保证?

消息键&#xff08;Key&#xff09;是Kafka消息的一个可选属性&#xff0c;用于标识消息的逻辑关联关系。每条消息可以携带一个关键字作为其键&#xff0c;这个键可以是字符串、整数等数据类型。 使用消息键可以在Kafka中实现消息的顺序性保证&#xff0c;具体方式如下&#x…...

慎思笃行,兴业致远:金融行业的数据之道

《中庸》中说&#xff0c;“博学之&#xff0c;审问之&#xff0c;慎思之&#xff0c;明辨之&#xff0c;笃行之”。这段话穿越千年&#xff0c;指引着中国千行百业的发展。对于金融行业来说&#xff0c;庞大的数据量可以说是“博学”的来源。但庞大的数据体量&#xff0c;既是…...

Git-分支管理

文章目录 1.分支管理2.合并冲突3.合并模式4.补充 1.分支管理 Git分支管理是指在Git版本控制系统中&#xff0c;使用分支来管理项目的不同开发线路和并行开发的能力。通过分支&#xff0c;开发者可以在独立的环境中进行功能开发、bug修复等工作&#xff0c;而不会影响到主分支上…...

[Ubuntu 22.04] containerd配置HTTP方式拉取私仓Harbor

文章目录 1. 基础环境配置2. Docker安装3. 部署Harbor&#xff0c;HTTP访问4. 部署ContainerD5. 修改docker配置文件&#xff0c;向harbor中推入镜像6. 配置containerd6.1. 拉取镜像验证6.2. 推送镜像验证 1. 基础环境配置 [Ubuntu 22.04] 安装K8S基础环境准备脚本 2. Docker安…...

入门指南:深入解析OpenCV的copyTo函数及其与rect的应用场景

文章目录 导言copyTo函数的示例copyTo函数与rect的应用场景结论 导言 OpenCV是一个功能强大的开源计算机视觉库&#xff0c;广泛应用于图像处理和计算机视觉任务。在OpenCV中&#xff0c;copyTo函数是一个重要的图像处理函数&#xff0c;它允许我们在不同的图像之间复制像素数…...

2018年全国硕士研究生入学统一考试管理类专业学位联考写作试题——解析版

2018年1月真题 四、写作&#xff1a;第56~57小题&#xff0c;共65分。其中论证有效性分析30 分&#xff0c;论说文35分。 56.论证有效性分析&#xff1a; 分析下述论证中存在的缺陷和漏洞&#xff0c;选择若干要点&#xff0c;写一篇600字左右的文章&#xff0c;对该论证的有…...

系统集成|第七章(笔记)

目录 第七章 范围管理7.1 项目范围管理概念7.2 主要过程7.2.1 规划范围管理7.2.2 收集需求7.2.3 定义范围7.2.4 创建工作分解结构 - WBS7.2.5 范围确认7.2.6 范围控制 上篇&#xff1a;第六章、整体管理 第七章 范围管理 7.1 项目范围管理概念 概述&#xff1a;项目范围管理就…...

Qt —— Vs2017编译hiredis源码并测试调用(附调用hiredis库源码)

下载hiredis源码 编译hiredis源码 1、解压下载的hiredis源码包,如图使用Vs2017打开hiredis_win.sln 2、如下两图,Vs2017打开.sln后点击升级。 分别对两个工程的debug、release进行配置。Debug配置为多线程调试DLL(MDd)、Release配置为多线程DLL(/MD),这样做是为了配合被调用…...

深入理解设计模式:设计模式定义、设计原则以及组织编目

文章目录 一、设计模式1.1 设计模式的起源1.2 设计模式的定义1.3 记录要素1.4 合理使用模式 二、设计模式的六大原则2.1 开闭原则(Open-Closed Principle, OCP)2.1.1 定义2.1.2 原则分析2.1.3 开闭原则的意义所在 2.2 单一职责原则(Single Responsibility Principle, SRP)2.4.1…...

鸿鹄协助管理华为云与炎凰Ichiban

炎凰对华为云的需求 在炎凰日常的开发中&#xff0c;对于服务器上的需求&#xff0c;我们基本都是采用云服务。目前我们主要选择的是华为云&#xff0c;华为云的云主机比较稳定&#xff0c;提供的云主机配置也比较多样&#xff0c;非常适合对于不同场景硬件配置的需求&#xff…...

Vite创建Vue+TS项目引入文件路径报错

使用vite搭建vue3脚手架的时候&#xff0c;发现main.ts中引入App.vue编辑器会报错&#xff0c;但是不影响代码运行。 报错信息&#xff1a;TS2307: Cannot find module ‘./App.vue’ or its corresponding type declarations. 翻译过来是找不到模块或者相关的声明类型&#…...

计算机里基本硬件的组成以及硬件协同

文章目录 冯诺依曼体系输入设备输出设备存储器运算器控制器协同工作的流程 冯诺依曼体系 世界上第一台通用计算机&#xff0c;ENIAC&#xff0c;于1946年诞生于美国一所大学。 ENIAC研发的前期&#xff0c;需要工作人员根据提前设计好的指令手动接线&#xff0c;以这种方式输入…...

2023软件设计师中级备考经验分享(文中有资料链接分享)

先摊结论吧&#xff0c;软考中级设计师备考只是备考半个月&#xff08;期间还摆烂了几天&#xff09;&#xff0c;然而成绩如下&#xff1a; 我自己都没想到会这么好的成绩。。。 上午题&#xff1a;推荐把软考通APP里的历年真题刷3-4遍&#xff0c;直接刷真题&#xff0c;然后…...

Windows 10 中无法最大化任务栏中的程序

方法1&#xff1a;仅选择选项 PC 屏幕 如果您使用双显示器&#xff0c;有时这可能会发生在您的 1 台计算机已插入但您正在访问的应用程序正在另一台计算机上运行的情况下&#xff0c;因此您看不到任何选项。因此&#xff0c;请设置仅在主计算机上显示显示的 PC 屏幕选项。 第…...

【iOS】KVOKVC原理

1 KVO 键值监听 1.1 KVO简介 KVO的全称是Key-Value Observing&#xff0c;俗称"键值监听"&#xff0c;可以用于监听摸个对象属性值得改变。 KVO一般通过以下三个步骤使用&#xff1a; // 1. 添加监听 [self.student1 addObserver:self forKeyPath:"age"…...

当机器人变硬核:探索深度学习中的时间序列预测

收藏自&#xff1a;Wed, 15 Sep 2021 10:32:56 UTC 摘要&#xff1a;时间序列预测是机器学习和深度学习领域的一个重要应用&#xff0c;它可以用于预测未来趋势、分析数据模式和做出决策。本文将介绍一些基本概念和常用方法&#xff0c;并结合具体的案例&#xff0c;展示如何使…...

C# Solidworks二次开发:自动创建虚拟零件及使用注意事项

今天要讲的是关于在solidworks中如何自动创建虚拟零件的功能&#xff0c;也就是solidworks中插入新零件这个功能。 实现这个功能需要使用的API如下所示&#xff1a; InsertNewVirtualPart&#xff08;swFaceOrPlane1, out swcomp2&#xff09;&#xff1b; 其中这个方法中使…...

vim工具 windows系统使用

vim常用命令&#xff1a; 编辑–>输入&#xff1a; i: 在当前光标所在字符的前面&#xff0c;转为输入模式&#xff1b; 粘贴命令 p p: 如果删除或复制为整行内容&#xff0c;则粘贴至光标所在行的下方&#xff0c;如果复制或删除的内容为非整行&#xff0c;则粘贴至光标所…...

Tesseract开源的OCR工具及python pytesseract安装使用

一 、介绍 Tesseract是一款由Google赞助的开源OCR。 pytesseract是python包装器&#xff0c;它为可执行文件提供了pythonic API。 Tesseract 已经有 30 年历史&#xff0c;开始它是惠普实验室的一款专利软件&#xff0c;在2005年后由Google接手并进一步开发和完善。Tesseract支…...

【数理知识】自由度 degree of freedom 及自由度的计算方法

放在最前的一句话&#xff1a;自由度是一个存在于两个学科中的概念&#xff0c;一个是存在于统计学中的自由度&#xff0c;另一个是存在于物理学中的自由度。而我本人需要的是研究物理学中的自由度概念&#xff0c;同时本笔记全篇也是在了解物理学中的自由度。 文章目录 自由度…...

苍穹外卖day09——历史订单模块(用户端)+订单管理模块(管理端)

查询历史订单——需求分析与设计 产品原型 业务规则 分页查询历史订单 可以根据订单状态查询 展示订单数据时&#xff0c;需要展示的数据包括&#xff1a;下单时间、订单状态、订单金额、订单明细&#xff08;商品名称、图片&#xff09; 接口设计 查询历史订单——代码开…...

江宁区建设工程局网站进不去/百度广告推广

近一周的时候都在玩linux 内核及文件系统移植&#xff0c;使用的版本如下&#xff1a;Bootloader&#xff1a; u-boot-2010.06.tgzKernel&#xff1a; linux-3.0.y.tgzBusyBox&#xff1a; busybox-1.16.1.tgz搜集相应介绍文档后分别编译出U-boot.bin&#xff0c;uImage和ro…...

江苏建设纸质考试网站/在线培训课程

首先关于生成器的那些事&#xff1a;1.通常的for…in…循环中&#xff0c;in后面是一个数组&#xff0c;这个数组就是一个可迭代对象&#xff0c;类似的还有链表&#xff0c;字符串&#xff0c;文件。它的缺陷是所有数据都在内存中&#xff0c;如果有海量数据的话将会非常耗内存…...

网站公安备案必须么/域名查询ip地址

出处&#xff1a;http://www.cnblogs.com/wangqiguo/p/4625611.html 阅读目录 编译环境与开发流程QT项目的构成及原理QT中的布局QT中的通用控件QVariant 类型QComboBox控件QTableWidget控件QTabWidget控件QWebview控件使用QSS编码问题QT的内存管理QT的信号槽QT中绘图QT的线程QT…...

做网站政府扶持/南京seo培训

内容来源&#xff1a;2017年5月20日&#xff0c;腾讯高级软件工程师吴友强在“中生代技术沙龙系列之互联网大数据”进行《腾讯云大数据实战》演讲分享。IT 大咖说作为独家视频合作方&#xff0c;经主办方和讲者审阅授权发布。阅读字数&#xff1a;1954 | 3分钟阅读嘉宾演讲视频…...

一个网站做app/爱站权重查询

Linux之ARM&#xff08;IMX6U&#xff09;BSP工程管理实验1、工程管理简介1.1、创建bsp、imx6ul、obj和project这四个文件夹1.2、文件分类2、实验程序编写2.1、创建 imx6ul.h 文件2.2、创建个.vscode文件修改includePath2.2.1、修改includePath2.3、编写led驱动文件2.2.1、 bsp…...

建国汽车网址大全/苏州seo快速优化

-----Java培训、Android培训、iOS培训、.Net培训、期待与您交流&#xff01;----- 一、protocol简单使用 1>基本用途 1.可用来声明方法。 2.只要某个类遵守了这个协议&#xff0c;就相当于拥有这个协议中的所有方法声明。 3.只要父类遵守了这个协议&#xff0c;就相当于子类…...