Android Voice Interactor运行时空指针异常问题求助
我正在Android项目里实现Voice Interactor功能,想让用户说出类似“Ok Google”的指令来唤起特定功能(比如导航)。照着教程写了代码后,运行时直接抛出Runtime Exception,下面是我的代码片段和错误信息,麻烦帮忙排查一下!
Manifest 文件
<?xml version="1.0" encoding="utf-8"?> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:largeHeap="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="com.ivy.ivyvoicebot.MainActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="com.ivy.ivyvoicebot.MY_ACTION_INTENT" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.VOICE" /> </intent-filter> <intent-filter> <action android:name="android.media.action.STILL_IMAGE_CAMERA"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.VOICE"/> </intent-filter> <intent-filter> <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.VOICE"/> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="com.google.android.voicesearch.SELF_NOTE" /> </intent-filter> </activity> </application>
MainActivity 代码
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { startVoiceTrigger(); } else { Toast.makeText(this, "Device not supported", Toast.LENGTH_SHORT).show(); } } @RequiresApi(api = Build.VERSION_CODES.M) private void startVoiceTrigger() { VoiceInteractor.PickOptionRequest.Option option1 = new VoiceInteractor.PickOptionRequest.Option("cheese", 1); option1.addSynonym("ready"); option1.addSynonym("go"); option1.addSynonym("take it"); option1.addSynonym("ok"); VoiceInteractor.Prompt prompt = new VoiceInteractor.Prompt("Say Cheese"); VoiceInteractor voiceInteractor = getVoiceInteractor(); voiceInteractor.submitRequest(new VoiceInteractor.PickOptionRequest(prompt, new VoiceInteractor.PickOptionRequest.Option[]{option1}, null) { @Override public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { if (finished && selections.length == 1) { Message message = Message.obtain(); message.obj = result; Toast.makeText(MainActivity.this, "Navigate To next screen", Toast.LENGTH_SHORT).show(); } else { getActivity().finish(); } } @Override public void onCancel() { Toast.makeText(MainActivity.this, "Didn't work", Toast.LENGTH_SHORT).show(); getActivity().finish(); } }); } }
错误日志
2020-06-19 17:02:51.633 23588-23588/com.ivy.ivyvoicebot E/AndroidRuntime: FATAL EXCEPTION: main Process: com.ivy.ivyvoicebot, PID: 23588 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ivy.ivyvoicebot/com.ivy.ivyvoicebot.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.app.VoiceInteractor.submitRequest(android.app.VoiceInteractor$Request)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3375) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3514) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2110) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7697) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.app.VoiceInteractor.submitRequest(android.app.VoiceInteractor$Request)' on a null object reference at com.ivy.ivyvoicebot.MainActivity.startVoiceTrigger(MainActivity.java:259) at com.ivy.ivyvoicebot.MainActivity.onCreate(MainActivity.java:85) at android.app.Activity.performCreate(Activity.java:7815) at android.app.Activity.performCreate(Activity.java:7804) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1325) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3350) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3514) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2110) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7697) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 2020-06-19 17:02:51.640 23588-23588/com.ivy.ivyvoicebot I/Process: Sending signal. PID: 23588 SIG: 9
问题分析和解决方案
核心问题:空指针的原因
你遇到的NullPointerException是因为getVoiceInteractor()返回了null!这里要注意:VoiceInteractor只有在你的Activity是通过语音交互系统启动的时候才会存在,如果是直接点击桌面图标打开Activity,这个对象就是null,自然调用submitRequest就会崩溃。
另外,你的代码里还有个小问题:在Activity中调用getActivity()是错误的(这是Fragment里的方法),应该换成MainActivity.this或者直接this,否则也会导致后续崩溃。
具体修复步骤
- 添加非空判断:在使用VoiceInteractor之前先检查是否为null,避免崩溃:
@RequiresApi(api = Build.VERSION_CODES.M) private void startVoiceTrigger() { VoiceInteractor voiceInteractor = getVoiceInteractor(); // 先判断VoiceInteractor是否存在 if (voiceInteractor == null) { Toast.makeText(this, "请通过语音指令启动应用来使用此功能", Toast.LENGTH_LONG).show(); return; } // 原来的代码继续 VoiceInteractor.PickOptionRequest.Option option1 = new VoiceInteractor.PickOptionRequest.Option("cheese", 1); option1.addSynonym("ready"); option1.addSynonym("go"); option1.addSynonym("take it"); option1.addSynonym("ok"); VoiceInteractor.Prompt prompt = new VoiceInteractor.Prompt("Say Cheese"); voiceInteractor.submitRequest(new VoiceInteractor.PickOptionRequest(prompt, new VoiceInteractor.PickOptionRequest.Option[]{option1}, null) { @Override public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { if (finished && selections.length == 1) { Message message = Message.obtain(); message.obj = result; Toast.makeText(MainActivity.this, "Navigate To next screen", Toast.LENGTH_SHORT).show(); } else { // 替换getActivity()为MainActivity.this MainActivity.this.finish(); } } @Override public void onCancel() { Toast.makeText(MainActivity.this, "Didn't work", Toast.LENGTH_SHORT).show(); // 同样替换getActivity() MainActivity.this.finish(); } }); }
正确启动方式:要测试VoiceInteractor功能,你不能直接点击图标打开App,而是需要通过语音指令触发,比如对着手机说“Ok Google,启动IvyVoiceBot”或者对应你Manifest里注册的action指令(比如“Ok Google,拍照”对应
STILL_IMAGE_CAMERA的action),这样启动的Activity才会持有有效的VoiceInteractor对象。关于唤醒词的补充:如果你想实现自定义的唤醒词(比如自己的“Ok Ivy”),VoiceInteractor是做不到的,它只是用于语音启动后的交互环节。自定义唤醒词需要借助Google Assistant的App Actions,或者自己实现持续音频监听(但后者会有耗电和系统权限限制的问题,不推荐)。
内容的提问来源于stack exchange,提问作者Anish Kumar

