英文:
"null object reference error" when trying to use TTS
问题
我正在尝试使用文本转语音功能,但是遇到了以下错误:
> 2020-07-26 22:58:51.876 15964-15964/ch.yourclick.kitt
> E/AndroidRuntime: FATAL EXCEPTION: main
> Process: ch.yourclick.kitt, PID: 15964
> java.lang.NullPointerException: 尝试调用虚拟方法 'int android.speech.tts.TextToSpeech.speak(java.lang.String, int, java.util.HashMap)' 但空对象引用
> at ch.yourclick.kitt.MainActivity.speak(MainActivity.java:61)
> at ch.yourclick.kitt.fragments.GeneralFragment$1.onClick(GeneralFragment.java:59)
**MainActivity.java**
```java
package ch.yourclick.kitt;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import java.util.Locale;
import ch.yourclick.kitt.ui.main.SectionsPagerAdapter;
public class MainActivity extends AppCompatActivity {
public TextToSpeech tts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
FloatingActionButton fab = findViewById(R.id.fab);
// 文本转语音
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) { // <— 我从未进入这个 if 语句
int result = tts.setLanguage(Locale.getDefault());
// 不支持该语言
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS", "不支持的语言");
}
}
else {
Log.e("TTS", "" + status); // 返回 -1
Log.e("TTS", "" + TextToSpeech.SUCCESS); // 返回 0
}
}
});
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "用自己的操作替换它", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
/**
* 朗读
*/
public void speak() {
String text = "你好";
tts.speak(text, TextToSpeech.QUEUE_ADD, null);
}
/**
* 关闭
*/
@Override
public void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
}
英文:
I am trying to use Text-To-Speech but get the following error:
> 2020-07-26 22:58:51.876 15964-15964/ch.yourclick.kitt
> E/AndroidRuntime: FATAL EXCEPTION: main
> Process: ch.yourclick.kitt, PID: 15964
> java.lang.NullPointerException: Attempt to invoke virtual method 'int android.speech.tts.TextToSpeech.speak(java.lang.String, int,
> java.util.HashMap)' on a null object reference
> at ch.yourclick.kitt.MainActivity.speak(MainActivity.java:61)
> at ch.yourclick.kitt.fragments.GeneralFragment$1.onClick(GeneralFragment.java:59)
MainActivity.java
package ch.yourclick.kitt;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import java.util.Locale;
import ch.yourclick.kitt.ui.main.SectionsPagerAdapter;
public class MainActivity extends AppCompatActivity {
public TextToSpeech tts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
FloatingActionButton fab = findViewById(R.id.fab);
// Text to speech
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) { // <-- I never get into that if statement
int result = tts.setLanguage(Locale.getDefault());
// Language is not supported
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS", "Language not supported");
}
}
else {
Log.e("TTS", "" + status); // Returns -1
Log.e("TTS", "" + TextToSpeech.SUCCESS); // Returns 0
}
}
});
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
/**
* Speak
*/
public void speak() {
String text = "Hello";
tts.speak(text, TextToSpeech.QUEUE_ADD, null);
}
/**
* Turn off
*/
@Override
public void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
}
GeneralFragment.java
package ch.yourclick.kitt.fragments;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import ch.yourclick.kitt.MainActivity;
import ch.yourclick.kitt.R;
/**
* A simple {@link Fragment} subclass.
* Use the {@link GeneralFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class GeneralFragment extends Fragment {
public GeneralFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @return A new instance of fragment General.
*/
// TODO: Rename and change types and number of parameters
public static GeneralFragment newInstance() {
GeneralFragment fragment = new GeneralFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_general, container, false);
Button hello = view.findViewById(R.id.hello);
hello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MainActivity mainActivity = new MainActivity();
mainActivity.speak();
}
});
return view;
}
}
It seems like tts
returns null but why?
答案1
得分: 2
考虑一下你的帖子中的以下两个代码片段:
// 在 MainActivity.java 的 onCreate(...) 中找到的代码
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
// 这里有一堆代码
});
和
// 在 GeneralFragment.java 的 OnClickListener 中找到的代码
MainActivity mainActivity = new MainActivity();
mainActivity.speak();
你很可能会因为在调用 mainActivity.speak()
时 tts
为 null 而收到 NullPointerException
。
虽然你的意图是好的,但之所以不能按计划工作的原因是,实际上你正在创建一个新的 MainActivity
实例,这个实例尚未经过适当的生命周期步骤,意味着 onCreate(...)
从未被调用,因此 tts
从未被分配...在该特定活动实例中。值得注意的是,你的片段正在其中运行的“真实”活动实例应该已经正确设置。
那么,使其工作的最快方法是什么呢?嗯,我建议获取当前活动,如果可能的话将其转换为 MainActivity
,然后尝试在该实例上调用 speak()
:
hello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final Activity activity = getActivity();
if (activity instanceof MainActivity) {
((MainActivity) activity).speak();
}
}
});
英文:
Consider the following two snippets from your post:
// Code found in onCreate(...) in MainActivity.java
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
// A bunch of code here
}
and
// Code found in an OnClickListener in GeneralFragment.java
MainActivity mainActivity = new MainActivity();
mainActivity.speak();
You're probably getting a NullPointerException
due to the fact that tts
is null when you call through to mainActivity.speak()
.
While your intentions were good, the reason for this not working as planned is that you're actually creating a new instance of MainActivity
that hasn't gone through the appropriate lifecycle steps, meaning that onCreate(...)
was never called and thus tts
was never assigned... in that specific activity instance. Worth mentioning is that the "real" activity instance your fragment is running in however should have it set-up correctly.
So, what's the fastest way to get it working? Well, I'd say get the current activity, cast it to MainActivity
if possible and try to call speak()
on that instance instead:
hello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final Activity activity = getActivity();
if (activity instanceof MainActivity) {
((MainActivity) activity).speak();
}
}
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论