为什么软键盘应用程序因 java.lang.NullPointerException 而崩溃?
Why soft keyboard app crashes because of java.lang.NullPointerException?
我已在 Play 商店发布 Android 软键盘,发现以下设备在一天内发生 5 次崩溃:
以下是崩溃报告:
java.lang.NullPointerException: Attempt to read from field 'int android.inputmethodservice.Keyboard$Key.width' on a null object reference
at com.sunzala.afghankeyboard.android.LatinKeyboard.setLanguageSwitchKeyVisibility(LatinKeyboard.java:93)
at com.sunzala.afghankeyboard.android.SoftKeyboard.setLatinKeyboard(SoftKeyboard.java:210)
at com.sunzala.afghankeyboard.android.SoftKeyboard.onStartInputView(SoftKeyboard.java:368)
at android.inputmethodservice.InputMethodService.showWindowInner(InputMethodService.java:2187)
at android.inputmethodservice.InputMethodService.showWindow(InputMethodService.java:2081)
at android.inputmethodservice.InputMethodService$InputMethodImpl.showSoftInput(InputMethodService.java:651)
at android.inputmethodservice.IInputMethodWrapper.executeMessage(IInputMethodWrapper.java:216)
at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:37)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7237)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
这是 LatinKeyboard.setLanguageSwitchKeyVisibility(LatinKeyboard.java:93:
的代码
public class LatinKeyboard extends Keyboard {
private Key mEnterKey;
private Key mSpaceKey;
/**
* Stores the current state of the mode change key. Its width will be dynamically updated to
* match the region of {@link #mModeChangeKey} when {@link #mModeChangeKey} becomes invisible.
*/
private Key mModeChangeKey;
/**
* Stores the current state of the language switch key (a.k.a. globe key). This should be
* returns true. When this key becomes invisible, its width will be shrunk to zero.
*/
private Key mLanguageSwitchKey;
/**
* Stores the size and other information of {@link #mModeChangeKey} when
* {@link #mLanguageSwitchKey} is visible. This should be immutable and will be used only as a
* reference size when the visibility of {@link #mLanguageSwitchKey} is changed.
*/
private Key mSavedModeChangeKey;
/**
* Stores the size and other information of {@link #mLanguageSwitchKey} when it is visible.
* This should be immutable and will be used only as a reference size when the visibility of
* {@link #mLanguageSwitchKey} is changed.
*/
private Key mSavedLanguageSwitchKey;
public LatinKeyboard(Context context, int xmlLayoutResId) {
super(context, xmlLayoutResId);
}
public LatinKeyboard(Context context, int layoutTemplateResId,
CharSequence characters, int columns, int horizontalPadding) {
super(context, layoutTemplateResId, characters, columns, horizontalPadding);
}
@Override
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
XmlResourceParser parser) {
Key key = new LatinKey(res, parent, x, y, parser);
if (key.codes[0] == 10) {
mEnterKey = key;
} else if (key.codes[0] == ' ') {
mSpaceKey = key;
} else if (key.codes[0] == Keyboard.KEYCODE_MODE_CHANGE) {
mModeChangeKey = key;
mSavedModeChangeKey = new LatinKey(res, parent, x, y, parser);
} else if (key.codes[0] == LatinKeyboardView.KEYCODE_LANGUAGE_SWITCH) {
mLanguageSwitchKey = key;
mSavedLanguageSwitchKey = new LatinKey(res, parent, x, y, parser);
}
return key;
}
/**
* Dynamically change the visibility of the language switch key (a.k.a. globe key).
*
* @param visible True if the language switch key should be visible.
*/
void setLanguageSwitchKeyVisibility(boolean visible) {
if (visible) {
// The language switch key should be visible. Restore the size of the mode change key
// and language switch key using the saved layout.
mModeChangeKey.width = mSavedModeChangeKey.width;
mModeChangeKey.x = mSavedModeChangeKey.x;
mLanguageSwitchKey.width = mSavedLanguageSwitchKey.width;
mLanguageSwitchKey.icon = mSavedLanguageSwitchKey.icon;
mLanguageSwitchKey.iconPreview = mSavedLanguageSwitchKey.iconPreview;
} else {
// The language switch key should be hidden. Change the width of the mode change key
// to fill the space of the language key so that the user will not see any strange gap.
mModeChangeKey.width = mSavedModeChangeKey.width + mSavedLanguageSwitchKey.width;
mLanguageSwitchKey.width = 0;
mLanguageSwitchKey.icon = null;
mLanguageSwitchKey.iconPreview = null;
}
}
/**
* This looks at the ime options given by the current editor, to set the
* appropriate label on the keyboard's enter key (if it has one).
*/
void setImeOptions(Resources res, int options) {
if (mEnterKey == null) {
return;
}
switch (options & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
case EditorInfo.IME_ACTION_GO:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_go_circle_filled_24dp);
break;
case EditorInfo.IME_ACTION_NEXT:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_next_circle_filled_24dp);
break;
case EditorInfo.IME_ACTION_SEARCH:
mEnterKey.icon = res.getDrawable(R.drawable.ic_search_24dp);
mEnterKey.label = null;
break;
case EditorInfo.IME_ACTION_SEND:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_send_24dp);
break;
default:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_check_circle_24dp);
break;
}
}
void setSpaceIcon(final Drawable icon) {
if (mSpaceKey != null) {
mSpaceKey.icon = icon;
}
}
static class LatinKey extends Keyboard.Key {
public LatinKey(Resources res, Keyboard.Row parent, int x, int y,
XmlResourceParser parser) {
super(res, parent, x, y, parser);
}
/**
* Overriding this method so that we can reduce the target area for the key that
* closes the keyboard.
*/
@Override
public boolean isInside(int x, int y) {
return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y);
}
}
}
这是 SoftKeyboard.setLatinKeyboard(SoftKeyboard.java:210) nextKeyboard.setLanguageSwitchKeyVisibility(shouldSupportLanguageSwitchKey);
的代码
编辑:
我认为问题是由以下代码引起的:
private void setLatinKeyboard(LatinKeyboard nextKeyboard) {
boolean shouldSupportLanguageSwitchKey = true;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
shouldSupportLanguageSwitchKey = mInputMethodManager.shouldOfferSwitchingToNextInputMethod(getToken());
}
nextKeyboard.setLanguageSwitchKeyVisibility(shouldSupportLanguageSwitchKey);
mInputView.setKeyboard(nextKeyboard);
}
我尝试 运行 模拟器上的软键盘 Android 5.0 和 6.0 版,但没有收到任何错误。导致错误的原因是什么?我该如何解决?
看起来你在 createKeyFromXml()
中基于 key.codes
初始化了 mModeChangedKey
和 mSavedModeChangeKey
,但是 setLanguageSwitchKeyVisibility()
将使用其中一个基于完全不同的键参数 - 可见性。
所以,发生的事情是在 createKeyFromXml()
中创建了一个键,但在 setLanguageSwitchKeyVisibility()
中使用了另一个键(仍然引用空值)。
我已在 Play 商店发布 Android 软键盘,发现以下设备在一天内发生 5 次崩溃:
以下是崩溃报告:
java.lang.NullPointerException: Attempt to read from field 'int android.inputmethodservice.Keyboard$Key.width' on a null object reference
at com.sunzala.afghankeyboard.android.LatinKeyboard.setLanguageSwitchKeyVisibility(LatinKeyboard.java:93)
at com.sunzala.afghankeyboard.android.SoftKeyboard.setLatinKeyboard(SoftKeyboard.java:210)
at com.sunzala.afghankeyboard.android.SoftKeyboard.onStartInputView(SoftKeyboard.java:368)
at android.inputmethodservice.InputMethodService.showWindowInner(InputMethodService.java:2187)
at android.inputmethodservice.InputMethodService.showWindow(InputMethodService.java:2081)
at android.inputmethodservice.InputMethodService$InputMethodImpl.showSoftInput(InputMethodService.java:651)
at android.inputmethodservice.IInputMethodWrapper.executeMessage(IInputMethodWrapper.java:216)
at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:37)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7237)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
这是 LatinKeyboard.setLanguageSwitchKeyVisibility(LatinKeyboard.java:93:
的代码 public class LatinKeyboard extends Keyboard {
private Key mEnterKey;
private Key mSpaceKey;
/**
* Stores the current state of the mode change key. Its width will be dynamically updated to
* match the region of {@link #mModeChangeKey} when {@link #mModeChangeKey} becomes invisible.
*/
private Key mModeChangeKey;
/**
* Stores the current state of the language switch key (a.k.a. globe key). This should be
* returns true. When this key becomes invisible, its width will be shrunk to zero.
*/
private Key mLanguageSwitchKey;
/**
* Stores the size and other information of {@link #mModeChangeKey} when
* {@link #mLanguageSwitchKey} is visible. This should be immutable and will be used only as a
* reference size when the visibility of {@link #mLanguageSwitchKey} is changed.
*/
private Key mSavedModeChangeKey;
/**
* Stores the size and other information of {@link #mLanguageSwitchKey} when it is visible.
* This should be immutable and will be used only as a reference size when the visibility of
* {@link #mLanguageSwitchKey} is changed.
*/
private Key mSavedLanguageSwitchKey;
public LatinKeyboard(Context context, int xmlLayoutResId) {
super(context, xmlLayoutResId);
}
public LatinKeyboard(Context context, int layoutTemplateResId,
CharSequence characters, int columns, int horizontalPadding) {
super(context, layoutTemplateResId, characters, columns, horizontalPadding);
}
@Override
protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
XmlResourceParser parser) {
Key key = new LatinKey(res, parent, x, y, parser);
if (key.codes[0] == 10) {
mEnterKey = key;
} else if (key.codes[0] == ' ') {
mSpaceKey = key;
} else if (key.codes[0] == Keyboard.KEYCODE_MODE_CHANGE) {
mModeChangeKey = key;
mSavedModeChangeKey = new LatinKey(res, parent, x, y, parser);
} else if (key.codes[0] == LatinKeyboardView.KEYCODE_LANGUAGE_SWITCH) {
mLanguageSwitchKey = key;
mSavedLanguageSwitchKey = new LatinKey(res, parent, x, y, parser);
}
return key;
}
/**
* Dynamically change the visibility of the language switch key (a.k.a. globe key).
*
* @param visible True if the language switch key should be visible.
*/
void setLanguageSwitchKeyVisibility(boolean visible) {
if (visible) {
// The language switch key should be visible. Restore the size of the mode change key
// and language switch key using the saved layout.
mModeChangeKey.width = mSavedModeChangeKey.width;
mModeChangeKey.x = mSavedModeChangeKey.x;
mLanguageSwitchKey.width = mSavedLanguageSwitchKey.width;
mLanguageSwitchKey.icon = mSavedLanguageSwitchKey.icon;
mLanguageSwitchKey.iconPreview = mSavedLanguageSwitchKey.iconPreview;
} else {
// The language switch key should be hidden. Change the width of the mode change key
// to fill the space of the language key so that the user will not see any strange gap.
mModeChangeKey.width = mSavedModeChangeKey.width + mSavedLanguageSwitchKey.width;
mLanguageSwitchKey.width = 0;
mLanguageSwitchKey.icon = null;
mLanguageSwitchKey.iconPreview = null;
}
}
/**
* This looks at the ime options given by the current editor, to set the
* appropriate label on the keyboard's enter key (if it has one).
*/
void setImeOptions(Resources res, int options) {
if (mEnterKey == null) {
return;
}
switch (options & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
case EditorInfo.IME_ACTION_GO:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_go_circle_filled_24dp);
break;
case EditorInfo.IME_ACTION_NEXT:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_next_circle_filled_24dp);
break;
case EditorInfo.IME_ACTION_SEARCH:
mEnterKey.icon = res.getDrawable(R.drawable.ic_search_24dp);
mEnterKey.label = null;
break;
case EditorInfo.IME_ACTION_SEND:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_send_24dp);
break;
default:
mEnterKey.label = null;
mEnterKey.icon = res.getDrawable(R.drawable.ic_check_circle_24dp);
break;
}
}
void setSpaceIcon(final Drawable icon) {
if (mSpaceKey != null) {
mSpaceKey.icon = icon;
}
}
static class LatinKey extends Keyboard.Key {
public LatinKey(Resources res, Keyboard.Row parent, int x, int y,
XmlResourceParser parser) {
super(res, parent, x, y, parser);
}
/**
* Overriding this method so that we can reduce the target area for the key that
* closes the keyboard.
*/
@Override
public boolean isInside(int x, int y) {
return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y);
}
}
}
这是 SoftKeyboard.setLatinKeyboard(SoftKeyboard.java:210) nextKeyboard.setLanguageSwitchKeyVisibility(shouldSupportLanguageSwitchKey);
编辑: 我认为问题是由以下代码引起的:
private void setLatinKeyboard(LatinKeyboard nextKeyboard) {
boolean shouldSupportLanguageSwitchKey = true;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
shouldSupportLanguageSwitchKey = mInputMethodManager.shouldOfferSwitchingToNextInputMethod(getToken());
}
nextKeyboard.setLanguageSwitchKeyVisibility(shouldSupportLanguageSwitchKey);
mInputView.setKeyboard(nextKeyboard);
}
我尝试 运行 模拟器上的软键盘 Android 5.0 和 6.0 版,但没有收到任何错误。导致错误的原因是什么?我该如何解决?
看起来你在 createKeyFromXml()
中基于 key.codes
初始化了 mModeChangedKey
和 mSavedModeChangeKey
,但是 setLanguageSwitchKeyVisibility()
将使用其中一个基于完全不同的键参数 - 可见性。
所以,发生的事情是在 createKeyFromXml()
中创建了一个键,但在 setLanguageSwitchKeyVisibility()
中使用了另一个键(仍然引用空值)。