当前位置: 首页 > 知识库问答 >
问题:

连接耳机时,如何将默认音频路由到耳机?

连志义
2023-03-14

我正在开发一个应用程序,我们只需要将耳机插孔用作按钮。

要求:连接耳机时通过听筒播放默认音频(通话)(无需通过耳机收听音频)

有许多通过扬声器和耳机以及蓝牙耳机路由音频的示例,但没有任何关于通过连接耳机的设备的耳式扬声器路由音频的内容。我已经尝试了很多,有些链接是

Android:强制音频路由(在我的场景中不起作用)

我查过sound about(https://play . Google . com/store/apps/details?id = com . Woods link . Android . wiredheadphoneroutingfix

如果连接了耳机,我可以将音频连接到扬声器:这是我的代码

if (Build.VERSION.SDK_INT >= 21) {
            ForegroundService.audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
            ForegroundService.audioManager.setSpeakerphoneOn(true);
            SplashScreen.preferences.edit().putBoolean("isKey", true).commit();
        } else {
            Class audioSystemClass = null;
            try {
                audioSystemClass = Class.forName("android.media.AudioSystem");
                Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);
                setForceUse.invoke(null, FOR_MEDIA, FORCE_SPEAKER);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }


            SplashScreen.preferences.edit().putBoolean("isKey", true).commit();
            ForegroundService.audioManager.setSpeakerphoneOn(true);
        }

共有2个答案

荣轶
2023-03-14

经过大量研究,我发现没有任何方法可以在不使用反射的情况下实现这种功能。首先,您需要将耳机插孔放入,然后使用合适的参数调用方法setWiredDeviceConnectionState(),然后它的行为就像耳机断开连接一样,但点击仍然有效。所以这是一个黑客,但根据我的要求,这不是一个万无一失的解决方案,但目前有效。这是我的代码,

private void sendIntent(Intent i) {
        Method m;
        Log.i(TAG, "Device sdk = " + Build.VERSION.SDK_INT);
        try {
            if (Build.VERSION.SDK_INT < 16) {
                Class<?> clazz = Class.forName("android.app.ActivityManagerNative");
                m = clazz.getMethod("broadcastStickyIntent", Intent.class, String.class);
                m.setAccessible(true);
                m.invoke(clazz, i, null);
                return;
            } else if (Build.VERSION.SDK_INT < 23) {
                //int type, int state, String address, String name
                m = am.getClass().getMethod("setWiredDeviceConnectionState", Integer.TYPE, Integer.TYPE, String.class);
                m.setAccessible(true);
                Object[] objArr = new Object[3];
                objArr[0] = (i.getIntExtra("microphone", 0) == 0) ? 8 : 4;
                objArr[1] = i.getIntExtra("state", 0);
                objArr[2] = i.getStringExtra("name");
                m.invoke(am, objArr);
            } else {
                //int type, int state, String address, String name
                m = am.getClass().getMethod("setWiredDeviceConnectionState", Integer.TYPE, Integer.TYPE, String.class, String.class);
                m.setAccessible(true);
                Object[] objArr = new Object[4];
                objArr[0] = (i.getIntExtra("microphone", 0) == 0) ? 8 : 4;
                objArr[1] = i.getIntExtra("state", 0);
                objArr[2] = i.getStringExtra("address");
                objArr[3] = i.getStringExtra("name");
                m.invoke(am, objArr);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

发送的意图:

@TargetApi(Build.VERSION_CODES.M)
public class HeadSetJackReciever extends AudioDeviceCallback {
    public static boolean isAudioChecked;

    public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
        if (addedDevices.length != 0) {
            for (int i = 0; i < addedDevices.length; i++) {
                if (addedDevices[i].getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
                    AudioDeviceInfo audioDeviceInfo = addedDevices[i];
                    int microphone = audioDeviceInfo.getType();
                    String headsetName = "DCS";
                    String headsetAddress = "";
                    try {
                        Method method = audioDeviceInfo.getClass().getMethod("getAddress");
                        method.setAccessible(true);
                        headsetAddress = (String) method.invoke(audioDeviceInfo);
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    Log.e("TEST", "microphone:"+microphone);
                    Log.e("TEST", "headsetName:"+headsetName);
                    Log.e("TEST", "headsetAddress:"+headsetAddress );
                    Intent intent = new Intent(ForegroundService.context, SelectAudioOutput.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.putExtra("microphone",microphone);
                    intent.putExtra("headsetName",headsetName);
                    intent.putExtra("headsetAddress",headsetAddress);
                    ForegroundService.context.startActivity(intent);
                }
            }
        }
    }

    public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
        if (removedDevices.length != 0) {
            Log.e("TEST", "Audio deinserted");
            if (SplashScreen.preferences.getBoolean("isKey", false)) {
                Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
                startIntent.setAction(Constants.ACTION.STARTNOTIFICATION_ACTION);
                ForegroundService.context.startService(startIntent);
            } else {
                Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
                startIntent.setAction(Constants.ACTION.STOPNOTIFICATION_ACTION);
                ForegroundService.context.startService(startIntent);
            }
            ForegroundService.audioManager.setMode(AudioManager.MODE_IN_CALL);
            ForegroundService.audioManager.setSpeakerphoneOn(false);
        }
    }
}

对于Lollipop和更低版本:

if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
            headsetName = intent.getStringExtra("name");
            microphone = intent.getIntExtra("microphone", 0);

            int state = intent.getIntExtra("state", -1);
            switch (state) {
                case 0:
                    Log.d("onReceive", "Headset unplugged");
                    Log.e("TEST", "Audio deinserted");
                    if (SplashScreen.preferences.getBoolean("isKey", false)) {
                        Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
                        startIntent.setAction(Constants.ACTION.STARTNOTIFICATION_ACTION);
                        context.startService(startIntent);
                    } else {
                        Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
                        startIntent.setAction(Constants.ACTION.STOPNOTIFICATION_ACTION);
                        context.startService(startIntent);
                    }
                    ForegroundService.audioManager.setMode(AudioManager.MODE_IN_CALL);
                    ForegroundService.audioManager.setSpeakerphoneOn(false);
                    break;
                case 1:

                    Log.d("onReceive", "Headset plugged");
                    Log.e("TEST", "microphone:"+microphone);
                    Log.e("TEST", "headsetName:"+headsetName);
                    Log.e("TEST", "headsetAddress:"+headsetAddress );
                    Intent intentone = new Intent(ForegroundService.context, SelectAudioOutput.class);
                    intentone.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intentone.putExtra("microphone",microphone);
                    intentone.putExtra("headsetName",headsetName);
                    intentone.putExtra("headsetAddress",headsetAddress);
                    context.startActivity(intentone);
                    break;
            }
        }

如果我错过了什么,请告诉我。谢谢。

海鸣
2023-03-14

听筒在Android中从不用于媒体,只有在手机处于“通话”或“通信”(VoIP)状态时才能使用。

我猜你已经注意到没有“FORCE_EARPIECE”常量,所以它不能在对< code>setForceUse的调用中指定。

此外,听筒在呼叫的输出设备选择中具有最低的优先级,因此,如果电话连接了任何东西(在您的情况下,有假耳机),则将选择该设备(请参阅 html" target="_blank">https://android.googlesource.com/platform/frameworks/av/ /322b4d2/services/audiopolicy/enginedefault/src/Engine.cpp#381)。

对不起,似乎不可能实现你的意图。

更新

在SoundAbout强制对媒体使用耳机时检查< code>media.audio_policy状态后,我发现了该应用程序使用的以下技巧:

>

  • 它调用AudioSystem.setPhoneState(MODE_IN_COMMUNICATION)来强制执行“通信”电话状态(通常用于VoIP呼叫)。

    如果连接了耳机(或多个耳机),为了防止由于优先级较高而将声音传送到耳机,应用程序会调用AudioSystem。设置DeviceConnectionState(DEVICE_OUT_WIRED_HEADSET,DEVICE_ STATE_UNAVAILABLE,…)欺骗音频管理器相信没有耳机。

    这些都是黑客攻击,需要应用程序密切监控手机状态。也不是一直都管用。

    另一个缺点是,使用听筒会禁用片上音频解压缩,因此电池使用率较高。

    一般来说,我不建议使用这些技术。

  •  类似资料:
    • 我正在寻找一种方法来播放Android设备扬声器中的音频,即使是在插入耳机的情况下。 事实上,典型的行为是,当插入耳机时,扬声器不会输出任何音频。然而,一些应用程序,例如默认的时钟应用程序(com.google.android.deskclock),即使插入耳机,也能够将音频路由到扬声器。 如何以编程方式获得此行为? 我正在寻找(至少)在Nexus 5设备上运行KitKat(Android 4.4

    • 我试图让用户能够在我的应用程序中切换音频输出,我可以使用AVAudioSession在扬声器和背部之间切换,但我找不到在连接的蓝牙设备和耳机之间切换的方法,以任何顺序。 谢谢你的帮助。

    • 我创建了语音通话录音应用程序,它与手机的麦克风和听筒配合得很好。但是当插入耳机时,它不能录制音频。我试图将AudioSource更改为AudioSource.default,以为它会自动接受默认音频源。它什么也没记录。

    • 我正在使用语音识别应用程序。蓝牙耳机成功连接到我的android设备后,我想在内置麦克风和耳机麦克风之间自由切换音频输入,如何做到?

    • 在我的应用程序中,有一个使用设备音频插孔的读卡器。现在,我的问题是,当读卡器在设备的耳机插孔中完好无损时,我想从内置扬声器中发出声音。 以下是我尝试过的代码: 1) 使用反射方法 2)使用setMode方法 但这两种代码仅在具有默认FM应用程序的设备中运行。但我想在所有设备中都具有此功能。 请分享你的经验!!

    • 当有线耳机或USB耳机都连接到Android设备时,我在检测哪个设备播放音频时遇到问题。 有没有一个API可以检查它? 我注意到不同的设备以不同的方式工作。例如,在装有Android 9的三星S10上,后来连接的外围设备用于播放音频。然而,在Moto G6 Android 7和三星S8 Android 9上,无论后来连接了哪种外设,都始终使用有线耳机。 我需要这些信息来正确配置音频流并显示当前正在