Código fuente del sistema Android 9_gestión de audio (1) análisis del código fuente de efectos de sonido clave

Prefacio

Cuando el usuario hace clic en el botón del dispositivo inteligente Android, si va acompañado de un efecto de sonido clave, le brindará al usuario una mejor experiencia interactiva. En esta edición, combinaremos el código fuente del sistema Android para analizar específicamente cómo el control emite efectos de sonido clave.

1. El sistema carga recursos clave de efectos de sonido.

1. En la versión de TV de los dispositivos inteligentes Android, podemos controlar la presencia o ausencia de efectos de sonido de los botones ajustando el interruptor en la página de configuración. El código fuente del sistema correspondiente a la página de configuración es el siguiente.

paquetes/aplicaciones/TvSettings/Settings/src/com/android/tv/settings/device/sound/SoundFragment.java

public class SoundFragment extends PreferenceControllerFragment implements
        Preference.OnPreferenceChangeListener {
    
    

    private AudioManager mAudioManager;
    private Map<Integer, Boolean> mFormats;

    public static SoundFragment newInstance() {
    
    
        return new SoundFragment();
    }

    @Override
    public void onAttach(Context context) {
    
    
        mAudioManager = context.getSystemService(AudioManager.class);
        mFormats = mAudioManager.getSurroundFormats();
        super.onAttach(context);
    }
    
	//用户的点击行为首先触发此方法
    @Override
    public boolean onPreferenceTreeClick(Preference preference) {
    
    
        if (TextUtils.equals(preference.getKey(), KEY_SOUND_EFFECTS)) {
    
    
            final TwoStatePreference soundPref = (TwoStatePreference) preference;
            //调用setSoundEffectsEnabled来设置按键音的开启与关闭
            setSoundEffectsEnabled(soundPref.isChecked());
        }
        return super.onPreferenceTreeClick(preference);
    }

	//获取按键音效是否开启
    public static boolean getSoundEffectsEnabled(ContentResolver contentResolver) {
    
    
        return Settings.System.getInt(contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 1)
                != 0;
    }

	//设置是否开启按键音效
    private void setSoundEffectsEnabled(boolean enabled) {
    
    
        if (enabled) {
    
    
        	//如果开启按键音,则调用AudioManager的loadSoundEffects方法来加载按键音效资源
            mAudioManager.loadSoundEffects();
        } else {
    
    
            mAudioManager.unloadSoundEffects();
        }
        Settings.System.putInt(getActivity().getContentResolver(),
                Settings.System.SOUND_EFFECTS_ENABLED, enabled ? 1 : 0);
    }

}

Cuando hacemos clic en el botón de cambio de efecto de sonido clave en la página de configuración, eventualmente se activará el método setSoundEffectsEnabled de SoundFragment. Este método determinará si el sonido clave está activado. Si está activado, se llamará al método loadSoundEffects de AudioManager. para cargar el recurso de efectos de sonido clave. De lo contrario, se llamará al método unloadSoundEffects para no cargarlo. Recursos de efectos de sonido.

2. El método loadSoundEffects de AudioManager es el siguiente.

marcos/base/media/java/android/media/AudioManager.java

public class AudioManager {
    
    

    //获取AudioService的代理对象
    private static IAudioService getService()
    {
    
    
        if (sService != null) {
    
    
            return sService;
        }
        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
        sService = IAudioService.Stub.asInterface(b);
        return sService;
    }

    public void loadSoundEffects() {
    
    
        final IAudioService service = getService();
        try {
    
    
        	//调用AudioService服务的loadSoundEffects方法
            service.loadSoundEffects();
        } catch (RemoteException e) {
    
    
            throw e.rethrowFromSystemServer();
        }
    }
    
    public void unloadSoundEffects() {
    
    
        final IAudioService service = getService();
        try {
    
    
            service.unloadSoundEffects();
        } catch (RemoteException e) {
    
    
            throw e.rethrowFromSystemServer();
        }
    }
}

AudioManager primero obtiene el objeto proxy del servicio de audio AudioService a través del método getService y luego llama al método específico del objeto.

3. El método loadSoundEffects de AudioService es el siguiente.

public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener {
    
    
       
     private AudioHandler mAudioHandler;

	 //加载音效资源
     public boolean loadSoundEffects() {
    
    
        int attempts = 3;
        LoadSoundEffectReply reply = new LoadSoundEffectReply();

        synchronized (reply) {
    
    
        	//调用sendMsg发送消息给mAudioHandler。
            sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
            while ((reply.mStatus == 1) && (attempts-- > 0)) {
    
    
                try {
    
    
                    reply.wait(SOUND_EFFECTS_LOAD_TIMEOUT_MS);
                } catch (InterruptedException e) {
    
    
                    Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");
                }
            }
        }
        return (reply.mStatus == 0);
    }
    
    //不加载音效资源
    public void unloadSoundEffects() {
    
    
        sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
    }

    private static void sendMsg(Handler handler, int msg,
            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
    
    

        if (existingMsgPolicy == SENDMSG_REPLACE) {
    
    
            handler.removeMessages(msg);
        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
    
    
            return;
        }
        synchronized (mLastDeviceConnectMsgTime) {
    
    
            long time = SystemClock.uptimeMillis() + delay;

            if (msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
                msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||
                msg == MSG_SET_HEARING_AID_CONNECTION_STATE ||
                msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
                msg == MSG_A2DP_DEVICE_CONFIG_CHANGE ||
                msg == MSG_BTA2DP_DOCK_TIMEOUT) {
    
    
                if (mLastDeviceConnectMsgTime >= time) {
    
    
                  // add a little delay to make sure messages are ordered as expected
                  time = mLastDeviceConnectMsgTime + 30;
                }
                mLastDeviceConnectMsgTime = time;
            }
			//调用handler的sendMessageAtTime方法
            handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
        }
    }
    
    private class AudioHandler extends Handler {
    
    
    		//加载音效
            private boolean onLoadSoundEffects() {
    
    
            	...代码暂时省略...
            }
            @Override
        	public void handleMessage(Message msg) {
    
    
            switch (msg.what) {
    
    
            	...代码省略...
            	 case MSG_UNLOAD_SOUND_EFFECTS:
                    onUnloadSoundEffects();//不加载音效
                    break;
            	 case MSG_LOAD_SOUND_EFFECTS://加载音效
                    boolean loaded = onLoadSoundEffects();//调用onLoadSoundEffects加载音效,并将加载结果赋值给loaded
                    if (msg.obj != null) {
    
    
                        LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;
                        synchronized (reply) {
    
    
                            reply.mStatus = loaded ? 0 : -1;
                            reply.notify();
                        }
                    }
                    break;
              	...代码省略...
            }
    }
}

Aquí solo necesitamos analizar el método loadSoundEffects de AudioService para cargar recursos de efectos de sonido. Este método llamará a sendMsg y enviará un mensaje de tipo MSG_UNLOAD_SOUND_EFFECTS a mAudioHandler. Luego, el método handleMessage de AudioHandler se activará aún más y el mensaje eventualmente activará el método onLoadSoundEffects.

4. El método onLoadSoundEffects de AudioHandler es el siguiente.

public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener {
    
    
    //音效资源文件名称
    private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
    
    private class AudioHandler extends Handler {
    
    
		//加载音效
        private boolean onLoadSoundEffects() {
    
    
            int status;
            synchronized (mSoundEffectsLock) {
    
    
            	//如果系统未启动完毕直接返回
                if (!mSystemReady) {
    
    
                    Log.w(TAG, "onLoadSoundEffects() called before boot complete");
                    return false;
                }
                //如果mSoundPool不为空直接返回
                if (mSoundPool != null) {
    
    
                    return true;
                }
                //加载触摸音效
                loadTouchSoundAssets();
   				...代码暂时省略...
        }
        
    //加载按键音效资源
    private void loadTouchSoundAssets() {
    
    
        XmlResourceParser parser = null;
        //如果音效资源文件列表不为空直接返回
        if (!SOUND_EFFECT_FILES.isEmpty()) {
    
    
            return;
        }
        //加载按键默认的音效资源
        loadTouchSoundAssetDefaults();
       ...代码省略...
	}
	
    private void loadTouchSoundAssetDefaults() {
    
    
    	//在类型为List<String>的SOUND_EFFECT_FILES中添加默认的按键音效资源Effect_Tick.ogg
        SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
        for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
    
    
            SOUND_EFFECT_FILES_MAP[i][0] = 0;
            SOUND_EFFECT_FILES_MAP[i][1] = -1;
        }
    }
}

El método onLoadSoundEffects primero determina si el sistema se ha iniciado y regresa directamente si no se ha iniciado; luego determina si mSoundPool está vacío y regresa directamente si no está vacío; luego primero llama a un método clave loadTouchSoundAssets, que primero Determina si la
lista de archivos de recursos de efectos de sonido SOUND_EFFECT_FILES está vacía, regresa directamente si no está vacía. Si ninguno de los juicios anteriores es cierto, se llamará al método loadTouchSoundAssetDefaults para cargar el recurso de efectos de sonido predeterminado para el botón. Este método primero agrega el recurso de efectos de sonido Effect_Tick.ogg a SOUND_EFFECT_FILES.

5. Continúe observando el método onLoadSoundEffects de AudioHandler.

public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener {
    
    
    //音效资源文件名称
    private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
    
    private class AudioHandler extends Handler {
    
    
		//加载音效
        private boolean onLoadSoundEffects() {
    
    
            int status;

            synchronized (mSoundEffectsLock) {
    
    
                if (!mSystemReady) {
    
    
                    Log.w(TAG, "onLoadSoundEffects() called before boot complete");
                    return false;
                }

                if (mSoundPool != null) {
    
    
                    return true;
                }
                //加载触摸音效
                loadTouchSoundAssets();
                //创建SoundPool对象
                mSoundPool = new SoundPool.Builder()
                        .setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
                        .setAudioAttributes(new AudioAttributes.Builder()
                            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                            .build())
                        .build();
         		...代码省略...
                int numSamples = 0;
                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
    
    
                    // Do not load sample if this effect uses the MediaPlayer
                    if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
    
    
                        continue;
                    }
                    if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
    
    
                        //获取音效资源文件路径
                        String filePath = getSoundEffectFilePath(effect);
                        //使用SoundPool加载音效资源文件
                        int sampleId = mSoundPool.load(filePath, 0);
                        if (sampleId <= 0) {
    
    
                            Log.w(TAG, "Soundpool could not load file: "+filePath);
                        } else {
    
    
                            SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
                            poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
                            numSamples++;
                        }
                    } else {
    
    
                        SOUND_EFFECT_FILES_MAP[effect][1] =
                                poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
                    }
                }
			...代码省略...
        }
        
        //获取音效资源文件路径,默认返回的音效资源文件路径为/system/media/audio/ui/Effect_Tick.ogg
        private String getSoundEffectFilePath(int effectType) {
    
    
            //  /product + /media/audio/ui/ + Effect_Tick.ogg
            String filePath = Environment.getProductDirectory() + SOUND_EFFECTS_PATH
                    + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
            if (!new File(filePath).isFile()) {
    
    
                //   /system + /media/audio/ui/ + Effect_Tick.ogg
                filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH
                        + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
            }
            return filePath;
        }
 	 }
 }

onLoadSoundEffects primero llama al método loadTouchSoundAssets para cargar el nombre del archivo de recursos de efectos de sonido predeterminado, luego construye un objeto de instancia SoundPool y luego llama a getSoundEffectFilePath para obtener la ruta clave del archivo de recursos de efectos de sonido. La ruta de archivo de recursos de efectos de sonido predeterminada devuelta es /system/media /audio/ui/Effect_Tick.ogg Y llame a SoundPool para cargar el recurso de efectos de sonido.

6. Revise brevemente los pasos anteriores.
El sistema carga recursos clave de efectos de sonido.

2. Haga clic en el control para reproducir el recurso de efectos de sonido.

Después de que el sistema activa el efecto de sonido de las teclas, cuando hacemos clic en cualquier control, se activará el efecto de sonido de las teclas. A continuación, ordenaremos el proceso según el código fuente del sistema de View.

1. Cuando hacemos clic en un control, primero se activará el método performClick de la Vista.

marcos/base/core/java/android/view/View.java

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    
    
	
    public boolean performClick() {
    
    
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
    
    
            playSoundEffect(SoundEffectConstants.CLICK);//播放按键点击音效
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
    
    
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }
    
    public void playSoundEffect(int soundConstant) {
    
    
        //判断mAttachInfo.mRootCallbacks是否为空,以及系统是否开启了按键音效
        if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
    
    
            return;
        }
        //调用mAttachInfo.mRootCallbacks的playSoundEffect方法
        mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
    }
 }

El método performClick de View llamará al método playSoundEffect, que primero determina si mAttachInfo.mRootCallbacks está vacío y si el sistema ha activado el efecto de sonido clave, y luego llama al método playSoundEffect de mAttachInfo.mRootCallbacks. Sabemos que WindowManager necesita usar la clase ViewRootImpl en el proceso de agregar Vista a la ventana. Para obtener más información, consulte el artículo Código fuente del sistema Android 9.0_Administración de ventanas (2) Proceso de administración de ventanas de WindowManager .

2. mAttachInfo se creó inicialmente en el constructor de ViewRootImpl.

marcos/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
    
        
    public ViewRootImpl(Context context, Display display) {
    
    
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrace();
        mWidth = -1;
        mHeight = -1;
        mDirty = new Rect();
        mTempRect = new Rect();
        mVisRect = new Rect();
        mWinFrame = new Rect();
        mWindow = new W(this);
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
        mViewVisibility = View.GONE;
        mTransparentRegion = new Region();
        mPreviousTransparentRegion = new Region();
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        //创建AttachInfo对象,倒数第二个参数就是View的playSoundEffect方法所用到的回调对象
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context);
		...代码省略...
    }
}

3. Después de leer el método de construcción de ViewRootImpl, veamos el método de construcción de AttachInfo.

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    
    
        
    final static class AttachInfo {
    
    

		//关键回调接口
        interface Callbacks {
    
    
        	//播放音效
            void playSoundEffect(int effectId);
            boolean performHapticFeedback(int effectId, boolean always);
        }

        final Callbacks mRootCallbacks;

        AttachInfo(IWindowSession session, IWindow window, Display display,
                ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,
                Context context) {
    
    
            mSession = session;
            mWindow = window;
            mWindowToken = window.asBinder();
            mDisplay = display;
            mViewRootImpl = viewRootImpl;
            mHandler = handler;
            mRootCallbacks = effectPlayer;//View的playSoundEffect方法所用到的回调对象就是这个
            mTreeObserver = new ViewTreeObserver(context);
        }
    }
 }

El último parámetro del método de construcción AttachInfo es crítico, porque este es el objeto llamado por el método playSoundEffect de View. Combinando el código de ViewRootImpl, podemos saber que ViewRootImpl implementa esta devolución de llamada.

4. El método playSoundEffect de ViewRootImpl es el siguiente.

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
    
        
    public void playSoundEffect(int effectId) {
    
    
        checkThread();//检测是否是UI线程
        try {
    
    
            final AudioManager audioManager = getAudioManager();

            switch (effectId) {
    
    
                case SoundEffectConstants.CLICK://播放按键点击音效
                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
                    return;
                case SoundEffectConstants.NAVIGATION_DOWN:
                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
                    return;
                case SoundEffectConstants.NAVIGATION_LEFT:
                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
                    return;
                case SoundEffectConstants.NAVIGATION_RIGHT:
                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
                    return;
                case SoundEffectConstants.NAVIGATION_UP:
                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
                    return;
                default:
                    throw new IllegalArgumentException("unknown effect id " + effectId +
                            " not defined in " + SoundEffectConstants.class.getCanonicalName());
            }
        } catch (IllegalStateException e) {
    
    
            // Exception thrown by getAudioManager() when mView is null
            Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e);
            e.printStackTrace();
        }
    }
}

El método playSoundEffect de ViewRootImpl primero detectará si el hilo actual es un hilo de UI y luego determinará qué efecto de sonido reproducir según el tipo de effectId pasado. Debido a que el método performClick de la Vista pasa en SoundEffectConstants.CLICK, se activará audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK).

4. El método playSoundEffect de AudioManager es el siguiente.

public class AudioManager {
    
    
    public void  playSoundEffect(int effectType) {
    
    
        //检测音效类型是否合规
        if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
    
    
            return;
        }
        //确定音效是否可用
        if (!querySoundEffectsEnabled(Process.myUserHandle().getIdentifier())) {
    
    
            return;
        }
        //获取AudioService服务
        final IAudioService service = getService();
        try {
    
    
            //调用服务的playSoundEffect方法
            service.playSoundEffect(effectType);
        } catch (RemoteException e) {
    
    
            throw e.rethrowFromSystemServer();
        }
    }
    
    /**
     * Settings has an in memory cache, so this is fast.
     */
    private boolean querySoundEffectsEnabled(int user) {
    
    
        return Settings.System.getIntForUser(getContext().getContentResolver(),
                Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
    }
}

playSoundEffect de AudioManager realizará alguna verificación. Si la verificación pasa, obtendrá el objeto de servicio AudioService y llamará al método playSoundEffect del objeto para reproducir el efecto de sonido.

5. Los códigos relacionados con AudioService y playSoundEffect son los siguientes.

public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener {
    
    
            
    /**
     * 播放音效
     * @param effectType
     */
    public void playSoundEffect(int effectType) {
    
    
        playSoundEffectVolume(effectType, -1.0f);
    }

    public void playSoundEffectVolume(int effectType, float volume) {
    
    
        // do not try to play the sound effect if the system stream is muted
        if (isStreamMutedByRingerOrZenMode(STREAM_SYSTEM)) {
    
    
            return;
        }

        if (effectType >= AudioManager.NUM_SOUND_EFFECTS || effectType < 0) {
    
    
            Log.w(TAG, "AudioService effectType value " + effectType + " out of range");
            return;
        }
		//发送播放音效的消息给mAudioHandler
        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
                effectType, (int) (volume * 1000), null, 0);
    }
    
    private class AudioHandler extends Handler {
    
    
    		//加载音效
            private boolean onLoadSoundEffects() {
    
    
            	...代码暂时省略...
            }
            @Override
        	public void handleMessage(Message msg) {
    
    
            switch (msg.what) {
    
    
            	...代码省略...
            	 case MSG_UNLOAD_SOUND_EFFECTS:
                    onUnloadSoundEffects();//不加载音效
                    break;
            	 case MSG_LOAD_SOUND_EFFECTS://加载音效
	               boolean loaded = onLoadSoundEffects();//调用onLoadSoundEffects加载音效,并将加载结果赋值给loaded
            		...代码省略...
                    break;
                case MSG_PLAY_SOUND_EFFECT://播放音效
                    onPlaySoundEffect(msg.arg1, msg.arg2);
                    break;
              	...代码省略...
            }
    }
}

El método playSoundEffect de AudioService llama además a playSoundEffectVolume, que envía el mensaje MSG_PLAY_SOUND_EFFECT para reproducir el efecto de sonido en mAudioHandler y, finalmente, activa el método onPlaySoundEffect.

6. El método onPlaySoundEftec de AudioService es el siguiente.

public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener {
    
    
            
        private void onPlaySoundEffect(int effectType, int volume) {
    
    
            synchronized (mSoundEffectsLock) {
    
    

                onLoadSoundEffects();//加载音效

                if (mSoundPool == null) {
    
    
                    return;
                }
                float volFloat;
                // use default if volume is not specified by caller
                if (volume < 0) {
    
    
                    volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
                } else {
    
    
                    volFloat = volume / 1000.0f;
                }

                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
    
    
                	//通过SoundPool播放音效
                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],
                                        volFloat, volFloat, 0, 0, 1.0f);
                } else {
    
    
                    //通过MediaPlayer播放音效
                    MediaPlayer mediaPlayer = new MediaPlayer();
                    try {
    
    
                        String filePath = getSoundEffectFilePath(effectType);
                        mediaPlayer.setDataSource(filePath);
                        mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
                        mediaPlayer.prepare();
                        mediaPlayer.setVolume(volFloat);
                        mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
    
    
                            public void onCompletion(MediaPlayer mp) {
    
    
                                cleanupPlayer(mp);
                            }
                        });
                        mediaPlayer.setOnErrorListener(new OnErrorListener() {
    
    
                            public boolean onError(MediaPlayer mp, int what, int extra) {
    
    
                                cleanupPlayer(mp);
                                return true;
                            }
                        });
                        mediaPlayer.start();
                    } catch (IOException ex) {
    
    
                        Log.w(TAG, "MediaPlayer IOException: "+ex);
                    } catch (IllegalArgumentException ex) {
    
    
                        Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
                    } catch (IllegalStateException ex) {
    
    
                        Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
                    }
                }
            }
        }
   }

7. Revise brevemente los pasos anteriores.
Haga clic en el control para reproducir el recurso de efectos de sonido.

Supongo que te gusta

Origin blog.csdn.net/abc6368765/article/details/132025465
Recomendado
Clasificación