Android开发音效增强中铃声播放Ringtone及声音池调度SoundPool的讲解及实战(超详细 附源码)

简介: Android开发音效增强中铃声播放Ringtone及声音池调度SoundPool的讲解及实战(超详细 附源码)

需要源码请点赞关注收藏后评论区留下QQ~~~

一、铃声播放

虽然媒体播放器MediaPlayer既可用来播放视频,也可以用来播放音频,但是在具体的使用场合,MediaPlayer存在某些播音方面的不足之处 包括以下几点

1:初始化比较消耗资源 尤其是播放短铃声时反应偏慢

2:同时只能播放一个媒体文件  无法同时播放多个声音

3:只能播放已经完成转码的音频文件,无法播放原始音频,也不能进行流式播放

Android提供了铃声工具Ringtone处理铃声的播放 下面式Ringtone的常用方法

play 开始播放铃声

stop 停止播放铃声

isPlaying  判断铃声是否正在播放

下面是实战效果 可以在下拉框中选择不同的声音

此处带着耳机或者真机测试可以听到不同的声音~~

代码如下

Java类

package com.example.audio;
import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
@SuppressLint("DefaultLocale")
public class RingToneActivity extends AppCompatActivity {
    private TextView tv_volume; // 声明一个文本视图对象
    private Ringtone mRingtone; // 声明一个铃声对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ring_tone);
        tv_volume = findViewById(R.id.tv_volume);
        initVolumeInfo(); // 初始化音量信息
        initRingSpinner(); // 初始化铃声下拉框
        // 生成本App自带的铃声文件res/raw/ring.ogg的Uri实例
        uriArray[uriArray.length - 1] = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.ring);
    }
    // 初始化音量信息
    private void initVolumeInfo() {
        // 从系统服务中获取音频管理器
        AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        // 获取铃声的最大音量
        int maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_RING);
        // 获取铃声的当前音量
        int nowVolume = audio.getStreamVolume(AudioManager.STREAM_RING);
        String desc = String.format("当前铃声音量为%d,最大音量为%d,请先将铃声音量调至最大",
                nowVolume, maxVolume);
        tv_volume.setText(desc);
    }
    // 初始化铃声下拉框
    private void initRingSpinner() {
        ArrayAdapter<String> ringAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, ringArray);
        Spinner sp_ring = findViewById(R.id.sp_ring);
        sp_ring.setPrompt("请选择要播放的铃声");
        sp_ring.setAdapter(ringAdapter);
        sp_ring.setOnItemSelectedListener(new RingSelectedListener());
        sp_ring.setSelection(0);
    }
    private String[] ringArray = {"来电铃声", "通知铃声", "闹钟铃声",
            "相机快门声", "视频录制声", "门铃叮咚声"};
    private Uri[] uriArray = {
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), // 来电铃声
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), // 通知铃声
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), // 闹钟铃声
            Uri.parse("file:///system/media/audio/ui/camera_click.ogg"), // 相机快门声
            Uri.parse("file:///system/media/audio/ui/VideoRecord.ogg"), // 视频录制声
            null
    };
    class RingSelectedListener implements OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            if (mRingtone != null) {
                mRingtone.stop(); // 停止播放铃声
            }
            // 从铃声文件的URI中获取铃声对象
            mRingtone = RingtoneManager.getRingtone(RingToneActivity.this, uriArray[arg2]);
            mRingtone.play(); // 开始播放铃声
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    @Override
    protected void onStop() {
        super.onStop();
        mRingtone.stop(); // 停止播放铃声
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/tv_volume"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp"
        android:orientation="horizontal" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="待播放的铃声:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_ring"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
</LinearLayout>

二、声音池调度

对于MediaPlayer无法同时播放多个声音的问题,Android提供了声音池工具SoundPoo,通过声音池即可同时播放多个音频,声音池可以事先加载多个音频,在需要时再播放指定音频 有以下几个好处

1:资源占用最小 不像MediaPlayer那么占用资源

2:相对MediaPlayer来说延迟时间非常短

3:可以同时播放多个音频 从而实现游戏过程中多个声音叠加的情景

SoundPool同样有以下几个缺陷

1:声音池最大只能申请1MB的内存 只能播放一些很短的声音片段

2:不要轻易调用pause和stop方法 容易引起App崩溃

3:建议使用声音池播放ogg格式的音频 对WAV格式的支持不太友好

4:待播放的音频要提前加载到声音池中 不要等到播放的时候才加载 否则可能播放不出来

实战效果如下

点击按钮即可播放对应的音频 注意此处可以多种音频同时播放

代码如下

Java类

package com.example.audio;
import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.HashMap;
@SuppressLint("DefaultLocale")
public class SoundPoolActivity extends AppCompatActivity implements OnClickListener {
    private TextView tv_volume; // 声明一个文本视图对象
    private SoundPool mSoundPool; // 初始化一个声音池对象
    private HashMap<Integer, Integer> mSoundMap = new HashMap<>(); // 声音编号映射
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sound_pool);
        tv_volume = findViewById(R.id.tv_volume);
        findViewById(R.id.btn_play_all).setOnClickListener(this);
        findViewById(R.id.btn_play_first).setOnClickListener(this);
        findViewById(R.id.btn_play_second).setOnClickListener(this);
        findViewById(R.id.btn_play_third).setOnClickListener(this);
        initVolumeInfo(); // 初始化音量信息
        initSound(); // 初始化声音池
    }
    // 初始化音量信息
    private void initVolumeInfo() {
        // 从系统服务中获取音频管理器
        AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        // 获取音乐的最大音量
        int maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        // 获取音乐的当前音量
        int nowVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
        String desc = String.format("当前音乐音量为%d,最大音量为%d,请先将音乐音量调至最大",
                nowVolume, maxVolume);
        tv_volume.setText(desc);
    }
    // 初始化声音池
    private void initSound() {
        // 初始化声音池,最多容纳三个声音
        AudioAttributes attributes = new AudioAttributes.Builder()
                .setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
        SoundPool.Builder builder = new SoundPool.Builder();
        builder.setMaxStreams(3).setAudioAttributes(attributes);
        mSoundPool = builder.build();
        loadSound(1, R.raw.beep1); // 加载第一个声音
        loadSound(2, R.raw.beep2); // 加载第二个声音
        loadSound(3, R.raw.ring); // 加载第三个声音
    }
    // 把音频资源添加进声音池
    private void loadSound(int seq, int resid) {
        // 把声音文件加入到声音池中,同时返回该声音文件的编号
        int soundID = mSoundPool.load(this, resid, 1);
        mSoundMap.put(seq, soundID);
    }
    // 播放指定序号的声音
    private void playSound(int seq) {
        int soundID = mSoundMap.get(seq);
        // 播放声音池中指定编号的音频
        mSoundPool.play(soundID, 1.0f, 1.0f, 1, 0, 1.0f);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_play_all) { //同时播放三个声音
            playSound(1); // 播放指定序号的声音
            playSound(2); // 播放指定序号的声音
            playSound(3); // 播放指定序号的声音
        } else if (v.getId() == R.id.btn_play_first) { // 播放第一个声音
            playSound(1); // 播放指定序号的声音
        } else if (v.getId() == R.id.btn_play_second) { // 播放第二个声音
            playSound(2); // 播放指定序号的声音
        } else if (v.getId() == R.id.btn_play_third) { // 播放第三个声音
            playSound(3); // 播放指定序号的声音
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mSoundPool != null) {
            mSoundPool.release(); // 释放声音池资源
        }
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/tv_volume"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
    <Button
        android:id="@+id/btn_play_all"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="播放所有ogg音效"
        android:textColor="@color/black"
        android:textSize="17sp" />
        <Button
            android:id="@+id/btn_play_first"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放第一支ogg"
            android:textColor="@color/black"
            android:textSize="17sp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <Button
            android:id="@+id/btn_play_second"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放第二支ogg"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Button
            android:id="@+id/btn_play_third"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放第三支ogg"
            android:textColor="@color/black"
            android:textSize="17sp" />
    </LinearLayout>
</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

相关文章
|
11月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1824 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
11月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1170 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
11月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1234 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
12月前
|
开发工具 Android开发
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
977 11
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
|
11月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
490 0
|
存储 Java 开发工具
Android开发的技术与开发流程
Android开发的技术与开发流程
761 1
|
编解码 Android开发 iOS开发
安卓与iOS开发:平台差异下的技术创新之路
在数字时代的浪潮中,移动应用开发如同两股潮流——安卓与iOS,各自携带着独特的技术生态和文化基因。本文将深入探讨这两大平台的开发环境、编程语言和工具的差异,以及它们如何塑造了不同的用户体验和技术趋势。通过比较分析,我们旨在揭示跨平台开发的可能性和挑战,同时探索未来技术创新的方向。让我们一起跟随代码的足迹,穿越安卓的开放草原和iOS的精密园林,发现那些隐藏在平台差异之下的创新机遇。
288 28
|
安全 Android开发 Swift
安卓与iOS开发:平台差异与技术选择
【8月更文挑战第26天】 在移动应用开发的广阔天地中,安卓和iOS两大平台各占一方。本文旨在探索这两个系统在开发过程中的不同之处,并分析开发者如何根据项目需求选择合适的技术栈。通过深入浅出的对比,我们将揭示各自平台的优势与挑战,帮助开发者做出更明智的决策。
392 5
|
移动开发 搜索推荐 Android开发
安卓与iOS开发:一场跨平台的技术角逐
在移动开发的广阔舞台上,两大主角——安卓和iOS,持续上演着激烈的技术角逐。本文将深入浅出地探讨这两个平台的开发环境、工具和未来趋势,旨在为开发者揭示跨平台开发的秘密,同时激发读者对技术进步的思考和对未来的期待。
|
移动开发 开发工具 Android开发
探索安卓与iOS开发的差异:技术选择的影响
【8月更文挑战第17天】 在移动应用开发的广阔天地中,安卓和iOS两大平台各领风骚。本文通过比较这两个平台的编程语言、开发工具及市场策略,揭示了技术选择对开发者和产品成功的重要性。我们将从开发者的视角出发,深入探讨不同平台的技术特性及其对项目实施的具体影响,旨在为即将步入移动开发领域的新手提供一个清晰的指南,同时给予资深开发者新的思考角度。
293 3

热门文章

最新文章