Android中播放音乐的几种方式_安卓开发播放音乐-程序员宅基地

技术标签: RxJava  android  播放方式  android多媒体  音乐  

Android中播放音乐的几种方式

前言

前几天一直在研究RxJava2,也写了记录了几篇博客,但因为工作任务原因,需要研究音频相关的知识,暂时放下Rxjava,本文的demo中,MediaPalyer 部分使用RxJava编写一点逻辑,其中涉及,RxJava2的被压、解除订阅等知识点,虽然简单,最起码没有丢了RxJava,后续Rxjava会继续研究,做记录.

andorid提供了对声音和视频处理的api包android.media.本文编写了针对这几种方式播放的Demo,文章最后贴出。

MediaPlayer播放音频

对于android音频的播放,这个类可能是大家最熟悉的了,从入门就一直想编写一个自己的音乐播放器,有木有?MediaPlayer确实强大,提供了对音频播放的各种控制,生命周期:


这里写图片描述

都很熟悉了,讲几个重点,其余不啰嗦了

1. MediaPlayer支持:AAC、AMR、FLAC、MP3、MIDI、OGG、PCM等格式
2. 播放Raw下的元数据

//直接创建,不需要设置setDataSource
mMediaPlayer=MediaPlayer.create(this, R.raw.audio);
mMediaPlayer.start();

3. MediaPlayer设置播放源的4中方式

  • setDataSource (String path)
//从sd卡中加载音乐
mMediaPlayer.setDataSource("../music/samsara.mp3") ;
//从网路加载音乐
mMediaPlayer.setDataSource("http://..../xxx.mp3") ;
//需使用异步缓冲
mMediaPlayer.prepareAsync() ;
  • setDataSource (FileDescriptor fd)
//需将资源文件放在assets文件夹
 AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
 mMediaPlayer.setDataSource(fd)
 mMediaPlayer.prepare() ;

 Ps:此方法系统需大于等于android 
  • setDataSource (Context context, Uri uri)
这个方法没什么好说的,一般通过ContentProvider获取Android系统提供
的共享music获取uri,然后设置数据播放
  • setDataSource (FileDescriptor fd, long offset, long length)
 //需将资源文件放在assets文件夹
 AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
 mMediaPlayer.setDataSource(fd, fd.getStartOffset(), fd.getLegth())
 mMediaPlayer.prepare() ;

4. 注意点

  • 设置完数据源,不要忘记prepare(),尽量使用异步prepareAync(),这样不会阻塞UI线程。
  • 播放完毕即使释放资源

    mediaPlayer.stop();
    mediaPlayer.release();
    mediaPlayer = null;
不足

资源占用量较高、延迟时间较长、不支持多个音频同时播放等

  • ### MeidaPlayer demo片段
//创建播放时间格式化工具
mFormat = new SimpleDateFormat("mm:ss");
//创建MediaPlayer和设置监听
mPlayer = new MediaPlayer() ;
mSeekBar.setOnSeekBarChangeListener(new MySeekBarChangeListener());
mPlayer.setOnPreparedListener(new MyOnPrepareListener());
mPlayer.setOnCompletionListener(new MyOnCompletionListener());


/**
    * 从assets资源文件夹中播放
     * @param name
     */
    private void playSoundFromA(String name) {
        if (mPlayer.isPlaying()) {
            mPlayer.stop();
        }
        // 设置当前播放歌曲的名字
        title.setText(names[current]);
        mPlayer.reset();
        AssetFileDescriptor afd = getAssetFileDescriptor(name);
        try {
            mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            hasResource = true;
            mPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

SoundPool播放音频

SoundPool支持多个音频文件同时播放(组合音频也是有上限的),延时短,比较适合短促、密集的场景,是游戏开发中音效播放的福音。

SoundPool实例化方式
1. new SoundPool(适用与5.0以下)
SoundPool(int maxStreams, int streamType, int srcQuality)
从android5.0开始此方法被标记为过时,稍微说以下几个参数。
1.maxStreams :允许同时播放的流的最大值

2.streamType :音频流的类型描述,
               在Audiomanager中有种类型声明,游戏应用通常会使用流媒体音乐。
3. srcQuality:采样率转化质量
2. SoundPool.Builder(从5.0开始支持)
//设置描述音频流信息的属性
AudioAttributes abs = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build() ;
SoundPool mSoundPoll =  new SoundPool.Builder()
                .setMaxStreams(100)   //设置允许同时播放的流的最大值
                .setAudioAttributes(abs)   //完全可以设置为null
                .build() ;
3. 几个重要的方法
// 几个load方法和上文提到的MediaPlayer基本一致,不做多的解释
//------------------------------------------------------------

int load(AssetFileDescriptor afd, int priority)

int load(Context context, int resId, int priority)

int load(String path, int priority)

int load(FileDescriptor fd, long offset, long length, int priority)

//-------------------------------------------------------------

// 通过流id暂停播放
final void pause(int streamID)

// 播放声音,soundID:音频id; left/rightVolume:左右声道(默认1,1);loop:循环次数(-1无限循环);rate:播放速率(1为标准)
final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)

//释放资源(很重要)
final void release()

//恢复播放
final void resume(int streamID)

//设置指定id的音频循环播放次数
final void setLoop(int streamID, int loop)

//设置加载监听(因为加载是异步的,需要监听加载,完成后再播放)
void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)

//设置优先级(同时播放个数超过最大值时,优先级低的先被移除)
final void setPriority(int streamID, int priority)

//设置指定音频的播放速率,0.5~2.0(rate>1:加快播放,反之慢速播放)
final void setRate(int streamID, float rate)

//停止指定音频播放
final void stop(int streamID)

//卸载指定音频
final boolean unload(int soundID)

//暂停所有音频的播放
final void autoPause()

//恢复所有暂停的音频播放
final void autoResum()

以上方法基本上是SoundPool的所有方法了,也都很常用。

4. 区分2个概念

看了Sounpool的api,是不是感觉对 streamID 和 soundID 一脸懵逼?
1. soundID:加载音乐资源时的返回值,int load(String path, int priority),这个int返回值就是soundID
2. streamID:播放时返回的值,即play()方法的返回值

这两个值都很重要,需要缓存下来

5. SoundPool Demo片段
注:我把SoundPool做了简单封装,SoundPoolUtil,会在文末上传,
有兴趣可下载看一下,时间比较急,还有很多不足的地方

//初始化SoundPool
if(Build.VERSION.SDK_INT >=  Build.VERSION_CODES.LOLLIPOP){
            AudioAttributes aab = new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .build() ;
            mSoundPool = new SoundPool.Builder()
                    .setMaxStreams(10)
                    .setAudioAttributes(aab)
                    .build() ;
        }else{
            mSoundPool = new SoundPool(60, AudioManager.STREAM_MUSIC,8) ;
        }
        mSoundPool = new SoundPool(60, AudioManager.STREAM_MUSIC,8) ;
        //设置资源加载监听
        mSoundPool.setOnLoadCompleteListener(new MyOnLoadCompleteListener());

//加载资源
/**
     * 加载指定路径列表的资源
     * @param map
     */
    public void loadR(Map<String, String> map){
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for(Map.Entry<String, String> entry : entries){
            String key = entry.getKey() ;
            if(checkSoundPool()){
                if(!idCache.containsKey(key)){
                    idCache.put(key, mSoundPool.load(entry.getValue(),1)) ;
                }
            }
        }
    }

    /**
     * 播放指定音频,并返用于停止、暂停、恢复的StreamId
     * @param name
     * @param times
     * @return
     */
    public int play(String name, int times){
        return this.play(name,1,1,1,times,1) ;
    }

AudioTrack播放音频

AudioTrack属于更偏底层的音频播放,MediaPlayerService的内部就是使用了AudioTrack。

AudioTrack用于单个音频播放和管理,相比于MediaPlayer具有:精炼、高效的优点。
更适合实时产生播放数据的情况,如加密的音频,
MediaPlayer是束手无策的,AudioTrack却可以。

AudioTrack用于播放PCM(PCM无压缩的音频格式)音乐流的回放,
如果需要播放其它格式音频,需要响应的解码器,
这也是AudioTrack用的比较少的原因,需要自己解码音频。
AudioTreack的2种播放模式

静态模式—static

静态的言下之意就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作

流模式streaming

流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景,不过我们一般在以下情况下采用:

  • 音频文件过大

  • 音频属性要求高,比如采样率高、深度大的数据

  • 音频数据是实时产生的,这种情况就只能用流模式了

通过write(byte[], int, int), write(short[], int, int)
write(float[], int, int, int)等方法推送解码数据到AudioTrack
使用Demo
private void jetPlayStream(){
        new Thread(new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void run() {
                // 获取最小缓冲区
                int bufSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
                // 实例化AudioTrack(设置缓冲区为最小缓冲区的2倍,至少要等于最小缓冲区)
                AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO,
                        AudioFormat.ENCODING_PCM_16BIT, bufSize*2, AudioTrack.MODE_STREAM);
                // 设置音量
                audioTrack.setVolume(2f) ;
                // 设置播放频率
                audioTrack.setPlaybackRate(10) ;
                audioTrack.play();
                // 获取音乐文件输入流
                InputStream is = getResources().openRawResource(R.raw.zbc);
                byte[] buffer = new byte[bufSize*2] ;
                int len ;
                try {
                    while((len=is.read(buffer,0,buffer.length)) != -1){
                        System.out.println("读取数据中...");
                        // 将读取的数据,写入Audiotrack
                        audioTrack.write(buffer,0,buffer.length) ;
                    }
                    is.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

更深入的研究,请参考以下博客
http://blog.csdn.net/edmond999/article/details/18600323
http://blog.csdn.net/conowen/article/details/7799155/

AsyncPlayer播放音频

1.介绍

从名字就可看出AsyncPlayer属于异步播放器,官方给出的说明是:所有工作都在子线程进行,不影响调用线程任何操作。

AsyncPlayer就是对MediaPlayer的一次简单的封装,对MediaPlaer所有的操作都在新开线程中执行。

AsyncPlayer只适合简单的异步播放,不能控制进度,只能开始或停止播放。如果播放在此调用play()方法,AsyncPlayer会停止当前播放,开始新的播放。

播放源码


/**
*内部线程类
**/
private final class Thread extends java.lang.Thread {
    

        public void run() {
            while (true) {
                Command cmd = null;
                synchronized (mCmdQueue) {

                    //取出链表中新加入的cmd
                    cmd = mCmdQueue.removeFirst();
                }

                switch (cmd.code) {
                case PLAY:
                    if (mDebug) Log.d(mTag, "PLAY");

                    //调用MediaPlayer播放
                    startSound(cmd);
                    break;

                }
                ....
            }
        }
    }

 private void startSound(Command cmd) {
        // Preparing can be slow, so if there is something else
        // is playing, let it continue until we're done, so 
            if (mDebug) Log.d(mTag, "Starting playback");
            MediaPlayer player = new MediaPlayer();
            player.setAudioStreamType(cmd.stream);
            player.setDataSource(cmd.context, cmd.uri);
            player.setLooping(cmd.looping);
            player.prepare();
            player.start();
            ....

    }
2.简单demo
十分简单,不再贴出,可以在文末Demo中看到

JetPlayer播放音频

Jet是由OHA联盟成员SONiVOX开发的一个交互音乐引擎。其包括两部分:JET播放器和JET引擎。JET常用于控制游戏的声音特效,采用MIDI(Musical Instrument Digital Interface)格式。

ps:以后遇到手机中的”.jet”文件,就只到它究竟是什么东东了。。。

  • 获取实例
//获取JetPlayer播放器
JetPlayer mJet = JetPlayer.getJetPlayer() ;
  • 几个重要方法
// 清空分段队列,并清除所有要进行播放的剪辑。
1. boolean clearQueue()  //每次播放前,记得做一次清空操作

// 加载jet文件的方法
2. boolean loadJetFile(String path)
   boolean loadJetFile(AssetFileDescriptor afd)

// 开始播放
3. boolean play()

// 暂停播放
4. boolean pause()

// 释放资源
5. void release()

// 指定jet队列的播放序列(调用play()前需要调用此方法)
6. boolean queueJetSegment(int segmentNum, int libNum, int repeatCount, int transpose, int muteFlags, byte userID)

  • JetPlayer Demo
private void jetPlayer(){
        // 获取JetPlayer播放器
        JetPlayer mJet = JetPlayer.getJetPlayer() ;
        //清空播放队列
        mJet.clearQueue() ;
        //绑定事件监听
        mJet.setEventListener(new JetPlayer.OnJetEventListener() {
            //播放次数记录
            int playNum = 1 ;
            @Override
            public void onJetEvent(JetPlayer player, short segment, byte track, byte channel, byte controller, byte value) {
                Log.i(TAG,"----->onJetEvent") ;
            }

            @Override
            public void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount) {
                Log.i(TAG,"----->onJetUserIdUpdate") ;
            }

            @Override
            public void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments) {
                Log.i(TAG,"----->onJetNumQueuedSegmentUpdate") ;
            }

            @Override
            public void onJetPauseUpdate(JetPlayer player, int paused) {
                Log.i(TAG,"----->onJetPauseUpdate") ;
                if(playNum == 2){
                    playNum = -1 ;
                    //释放资源,并关闭jet文件
                    player.release();
                    player.closeJetFile() ;
                }else{
                    playNum++ ;
                }
            }
        });
        //加载资源
        mJet.loadJetFile(getResources().openRawResourceFd(R.raw.level1)) ;
        byte sSegmentID = 0 ;
        //指定播放序列
        mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
        mJet.queueJetSegment(1, 0, 1, 0, 0, sSegmentID);
        //开始播放
        mJet.play() ;
    }

Ringtone

Ringtone为铃声、通知和其他类似声音提供快速播放的方法,这里还不得不提到一个管理类”RingtoneManager”,提供系统铃声列表检索方法,并且,Ringtone实例需要从RingtoneManager获取。

1. 获取实例
获取实例方法,均为RingtoneManager类提供

//通过铃声uri获取
static Ringtone getRingtone(Context context, Uri ringtoneUri)

//通过铃声检索位置获取
Ringtone getRingtone(int position)

其实,Rington这个类比较简单,只需要掌握,播放、停止(paly(),stop())等方法就可以了,而RingtoneManager却是比较重要的。

2. RingtoneManager几个钟要的方法
1. // 两个构造方法
RingtoneManager(Activity activity)
RingtoneManager(Context context)

2. // 获取指定声音类型(铃声、通知、闹铃等)的默认声音的Uri
static Uri getDefaultUri(int type)

3. // 获取系统所有Ringtone的cursor
Cursor getCursor()

4. // 获取cursor指定位置的Ringtone uri
Uri getRingtoneUri(int position)

5. // 判断指定Uri是否为默认铃声
static boolean isDefault(Uri ringtoneUri)

6. //获取指定uri的所属类型
static int getDefaultType(Uri defaultRingtoneUri)

7. //将指定Uri设置为指定声音类型的默认声音
static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri)

从api看,Ringtone和RingtoneManager还是比较简单的,不多做解释了,直接放上一段使用代码。

 /**
 * 播放来电铃声的默认音乐
*/
private void playRingtoneDefault(){
    Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) ;
    Ringtone mRingtone = RingtoneManager.getRingtone(this,uri);
    mRingtone.play();
}


/**
* 随机播放一个Ringtone(有可能是提示音、铃声等)
*/
private void ShufflePlayback(){
    RingtoneManager manager = new RingtoneManager(this) ;
    Cursor cursor = manager.getCursor();
    int count = cursor.getCount() ;
    int position = (int)(Math.random()*count) ;
    Ringtone mRingtone = manager.getRingtone(position) ;
    mRingtone.play();
    }

    //记得添加下面两个权限
    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

总结

以上介绍了几种播放方式,可以说各有优劣。
本文写的不够详细,只是大概的介绍一下几种播放方式。在此做下简单的总结。

  1. 对于延迟度要求不高,并且希望能够更全面的控制音乐的播放,MediaPlayer比较适合
  2. 声音短小,延迟度小,并且需要几种声音同时播放的场景,适合使用SoundPool
  3. 对于简单的播放,不需要复杂控制的播放,可以给使用AsyncPlayer,所有操作均在子线程不阻塞UI
  4. 播放大文件音乐,如WAV无损音频和PCM无压缩音频,可使用更底层的播放方式AudioTrack。它支持流式播放,可以读取(可来自本地和网络)音频流,却播放延迟较小。
    ps:据我测试AudioTrack直接支持WAV和PCM,其他音频需要解码成PCM格式才能播放。(其他无损格式没有尝试,有兴趣可以使本文提供的例子测试一下)
  5. .jet的音频比较少见(有的游戏中在使用),可使用专门的播放器JetPlayer播放
  6. 对于系统类声音的播放和操作,Ringtone更适合(主要是掌握好RingtoneManager)

android.media包中提供的播放音频的方式,远不止这些,本文只是参考api和其他大牛的博客做一些研究和记录,android.media种还有很多只是等着我们探索……


贴出Demo,仅供参考(其中有一点RxJava2的应用,有兴趣可看一下)

点击下载Demo源码

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013366008/article/details/76577372

智能推荐

机器学习模型评分总结(sklearn)_model.score-程序员宅基地

文章浏览阅读1.5w次,点赞10次,收藏129次。文章目录目录模型评估评价指标1.分类评价指标acc、recall、F1、混淆矩阵、分类综合报告1.准确率方式一:accuracy_score方式二:metrics2.召回率3.F1分数4.混淆矩阵5.分类报告6.kappa scoreROC1.ROC计算2.ROC曲线3.具体实例2.回归评价指标3.聚类评价指标1.Adjusted Rand index 调整兰德系数2.Mutual Informa..._model.score

Apache虚拟主机配置mod_jk_apache mod_jk 虚拟-程序员宅基地

文章浏览阅读344次。因工作需要,在Apache上使用,重新学习配置mod_jk1. 分别安装Apache和Tomcat:2. 编辑httpd-vhosts.conf: LoadModule jk_module modules/mod_jk.so #加载mod_jk模块 JkWorkersFile conf/workers.properties #添加worker信息 JkLogFil_apache mod_jk 虚拟

Android ConstraintLayout2.0 过度动画MotionLayout MotionScene3_android onoffsetchanged-程序员宅基地

文章浏览阅读335次。待老夫kotlin大成,扩展:MotionLayout 与 CoordinatorLayout,DrawerLayout,ViewPager 的 交互众所周知,MotionLayout 的 动画是有完成度的 即Progress ,他在0-1之间变化,一.CoordinatorLayout 与AppBarLayout 交互时,其实就是监听 offsetliner 这个 偏移量的变化 同样..._android onoffsetchanged

【转】多核处理器的工作原理及优缺点_多核处理器怎么工作-程序员宅基地

文章浏览阅读8.3k次,点赞3次,收藏19次。【转】多核处理器的工作原理及优缺点《处理器关于多核概念与区别 多核处理器工作原理及优缺点》原文传送门  摘要:目前关于处理器的单核、双核和多核已经得到了普遍的运用,今天我们主要说说关于多核处理器的一些相关概念,它的工作与那里以及优缺点而展开的分析。1、多核处理器  多核处理器是指在一枚处理器中集成两个或多个完整的计算引擎(内核),此时处理器能支持系统总线上的多个处理器,由总..._多核处理器怎么工作

个人小结---eclipse/myeclipse配置lombok_eclispe每次运行个新项目都需要重新配置lombok吗-程序员宅基地

文章浏览阅读306次。1. eclipse配置lombok 拷贝lombok.jar到eclipse.ini同级文件夹下,编辑eclipse.ini文件,添加: -javaagent:lombok.jar2. myeclipse配置lombok myeclipse像eclipse配置后,定义对象后,直接访问方法,可能会出现飘红的报错。 如果出现报错,可按照以下方式解决。 ..._eclispe每次运行个新项目都需要重新配置lombok吗

【最新实用版】Python批量将pdf文本提取并存储到txt文件中_python批量读取文字并批量保存-程序员宅基地

文章浏览阅读1.2w次,点赞31次,收藏126次。#注意:笔者在2021/11/11当天调试过这个代码是可用的,由于pdfminer版本的更新,网络上大多数的语法没有更新,我也是找了好久的文章才修正了我的代码,仅供学习参考。1、把pdf文件移动到本代码文件的同一个目录下,笔者是在pycharm里面运行的项目,下图中的x1文件夹存储了我需要转换成文本文件的所有pdf文件。然后要在此目录下创建一个存放转换后的txt文件的文件夹,如图中的txt文件夹。2、编写代码 (1)导入所需库# coding:utf-8import ..._python批量读取文字并批量保存

随便推点

vite build-程序员宅基地

文章浏览阅读4.3k次。vite在开发阶段采用的是按需加载的方式,不会将所有文件打包。但是生产环境的部署是需要进行打包的,这里它使用的是rollup打包方式。对于代码切割的需求,使用原生动态导入,因此打包后支持新浏览器,对IE的兼容性不是很好,但是可以用对应的polyfill解决。使用esbuild来处理需要pre-undle的在cli.ts的build命令中引入build.ts调用doBuild方法,在这个方法中配置打包参数(input output plugin等)调用buildHtmlPlugin解析文件入口in_vite build

Scala:访问修饰符、运算符和循环_scala ===运算符-程序员宅基地

文章浏览阅读1.4k次。http://blog.csdn.net/pipisorry/article/details/52902234Scala 访问修饰符Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。如果没有指定访问修饰符符,默认情况下,Scala对象的访问级别都是 public。Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层_scala ===运算符

MySQL导出ER图为图片或PDF_数据库怎么导出er图-程序员宅基地

文章浏览阅读2.6k次,点赞7次,收藏19次。ER图导出为PDF或图片格式_数据库怎么导出er图

oracle触发器修改同一张表,oracle触发器中对同一张表进行更新再查询时,需加自制事务...-程序员宅基地

文章浏览阅读655次。CREATE OR REPLACE TRIGGER Trg_ReimFactBEFORE UPDATEON BP_OrderFOR EACH ROWDECLAREPRAGMA AUTONOMOUS_TRANSACTION;--自制事务fc varchar2(255);BEGINIF ( :NEW.orderstate = 2AND :NEW.TransState = 1 ) THENBEG..._oracle触发器更新同一张表

debounce与throttle区别及其应用场景_throttle和debounce应用在哪些场景-程序员宅基地

文章浏览阅读513次。目录概念debouncethrottle实现debouncethrottle应用场景debouncethrottle场景举例debouncethrottle概念debounce字面理解是“防抖”,何谓“防抖”,就是连续操作结束后再执行,以网页滚动为例,debounce要等到用户停止滚动后才执行,将连续多次执行合并为一次执行。throttle字面理解是“节流”,何谓“节流”,就是确保一段时..._throttle和debounce应用在哪些场景

java操作mongdb【超详细】_java 操作mongodb-程序员宅基地

文章浏览阅读526次。regex() $regex 正则表达式用于模式匹配,基本上是用于文档中的发现字符串 (下面有例子)注意:若未加 @Field("名称") ,则识别mongdb集合中的key名为实体类属性名。也可以对数组进行索引,如果被索引的列是数组时,MongoDB会索引这个数组中的每一个元素。也可以对整个Document进行索引,排序是预定义的按插入BSON数据的先后升序排列。save: 若新增数据的主键已经存在,则会对当前已经存在的数据进行修改操作。_java 操作mongodb