安卓 - 不支持的服务:音频

13

我正在尝试理解并解决在Android应用程序中实现输入法时在Eclipse工作区日志中看到的错误。我对Android和Eclipse都很陌生。

错误信息是"com.utterkaos.keyboard.LatinKeyboardView无法实例化"。

相关的堆栈跟踪如下:

java.lang.UnsupportedOperationException: 不支持的服务:音频 在com.android.layoutlib.bridge.android.BridgeContext.getSystemService(BridgeContext.java:434) android.inputmethodservice.KeyboardView.(KeyboardView.java:376) android.inputmethodservice.KeyboardView.(KeyboardView.java:279) com.utterkaos.keyboard.LatinKeyboardView.(LatinKeyboardView.java:30) sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) java.lang.reflect.Constructor.newInstance(Unknown Source) com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.instantiateClass(ProjectCallback.java:402) com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.loadView(ProjectCallback.java:166) android.view.BridgeInflater.loadCustomView(BridgeInflater.java:207) android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:135) android.view.LayoutInflater.inflate(LayoutInflater.java:466) android.view.LayoutInflater.inflate(LayoutInflater.java:372) com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:321) com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:324) com.android.ide.common.rendering.LayoutLibrary.createSession(LayoutLibrary.java:325) com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService.createRenderSession(RenderService.java:372) com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart.renderWithBridge(GraphicalEditorPart.java:1361) com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart.recomputeLayout(GraphicalEditorPart.java:1115) com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart.activated(GraphicalEditorPart.java:941) com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate.delegatePageChange(LayoutEditorDelegate.java:450) com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor.pageChange(CommonXmlEditor.java:358) org.eclipse.ui.part.MultiPageEditorPart.setActivePage(MultiPageEditorPart.java:1067) org.eclipse.ui.forms.editor.FormEditor.setActivePage(FormEditor.java:607) com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor.selectDefaultPage(AndroidXmlEditor.java:380) com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor.addPages(AndroidXmlEditor.java:285) com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor.addPages(CommonXmlEditor.java:283) org.eclipse.ui.forms.editor.FormEditor.createPages(FormEditor.java:138) org.eclipse.ui.part.MultiPageEditorPart.createPartControl(MultiPageEditorPart.java:348) org.eclipse.ui.internal.EditorReference.createPartHelper(EditorReference.java:670) org.eclipse.ui.internal.EditorReference.createPart(EditorReference.java:465) org.eclipse.ui.internal.WorkbenchPartReference.getPart(WorkbenchPartReference.java:595) org.eclipse.ui.internal.EditorReference.getEditor(EditorReference.java:289) org.eclipse.ui.internal.WorkbenchPage.busyOpenEditorBatched(WorkbenchPage.java:2945) org.eclipse.ui.internal.WorkbenchPage.busyOpenEditor(WorkbenchPage.java:2850) org.eclipse.ui.internal.WorkbenchPage.access$11(WorkbenchPage.java:2842) org.eclipse.ui.internal.WorkbenchPage$10.run(WorkbenchPage.java:2793) org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70) org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:2789) org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:2773) org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:2764) org.eclipse.ui.ide.IDE.openEditor(IDE.java:651) org.eclipse.ui.ide.IDE.openEditor(IDE.java:610) org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor(EditorUtility.java:355) org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor(EditorUtility.java:164) org.eclipse.jdt.ui.actions.OpenAction.run(OpenAction.java:249) org.eclipse.jdt.ui.actions.OpenAction.run(OpenAction.java:228) org.eclipse.jdt.ui.actions.SelectionDispatchAction.dispatchRun(SelectionDispatchAction.java:275) org.eclipse.jdt.ui.actions.SelectionDispatchAction.run(SelectionDispatchAction.java:251) org.eclipse.jdt.internal.ui.packageview.PackageExplorerActionGroup.handleOpen(PackageExplorerActionGroup.java:376) org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart$4.open(PackageExplorerPart.java:538
LatinKeyboardView.java相关部分如下:
public class LatinKeyboardView extends KeyboardView {

    static final int KEYCODE_OPTIONS = -100;

    public LatinKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);

}

第30行是"super(context, attrs);"。

查看KeyboardView.java文件,第376行:

mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

这里的"Context.AUDIO_SERVICE"似乎是字符串"audio",出现在错误堆栈跟踪中。

BridgeContext.java中相关的部分如下:

    public Object getSystemService(String service) {
414        if (LAYOUT_INFLATER_SERVICE.equals(service)) {
415            return mBridgeInflater;
416        }
417
418        if (TEXT_SERVICES_MANAGER_SERVICE.equals(service)) {
419            // we need to return a valid service to avoid NPE
420            return TextServicesManager.getInstance();
421        }
422
423        // AutoCompleteTextView and MultiAutoCompleteTextView want a window
424        // service. We don't have any but it's not worth an exception.
425        if (WINDOW_SERVICE.equals(service)) {
426            return null;
427        }
428
429        // needed by SearchView
430        if (INPUT_METHOD_SERVICE.equals(service)) {
431            return null;
432        }
433
434        throw new UnsupportedOperationException("Unsupported Service: " + service);
435    }

我在这个例程中特别困惑的是,我不明白它怎么可能处理“音频”服务,但BridgeContext.java和KeyboardView.java都是Android代码的一部分,而不是我错误编写的类。

如果有任何指针可以帮助我理解为什么会发生这种错误以及如何避免它,那将不胜感激。


我也遇到了同样的问题。请问您解决了这个问题吗?这是我的问题链接:[https://dev59.com/eWzXa4cB1Zd3GeqPQBpO] - Shakti Malik
5个回答

4

您是否使用API 14或更高版本?如果是,那么这就是问题所在。我猜这是该版本中的一个错误。在API 11中,它可以正常工作。

如果您尝试API 11,您需要使用覆盖getResources()方法的一些技巧。请查看此处获取更多信息。之后它将正常工作。

实际上,我认为在API 14(或更高版本)上从LatinKeyboardView跳转没有办法,因为您甚至不能使用isInEditMode(),因为您绝对必须调用带有super的View构造函数。而那个构造函数将尝试获取音频系统服务,但它只会简单地失败,因为我猜您试图在eclipse图形编辑器中运行此操作(当我尝试将我的自定义视图放置到布局中时,我得到了这个错误)。

我认为唯一的解决方法是实现自己的KeyboardView而不使用getSystemService。如果isInEditMode == true,则可能不应调用该方法。


出于其他原因,我已经从应用程序中删除了键盘,所以这对我来说不再是一个问题,但如果我尝试实现另一个键盘,可能会再次成为问题 - 感谢您的指导。 - Ian

4

这是一个与android.inputmethodservice.KeyboardView有关的bug。

出错的代码是:

public KeyboardView(Context context, AttributeSet attrs, int defStyle) { 
...
    mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 
...
}

在布局编辑期间,应将其包装在isInEditMode()检查中以跳过获取音频管理器的操作。奇怪的是,我在Android错误跟踪器中没有找到任何问题报告!


3
我找到了解决方案。
使用KeyboardViewFix替换KeyboardView:
public class KeyboardViewFix extends KeyboardView {
    public static boolean inEditMode = true;

    @TargetApi(21) // Build.VERSION_CODES.L
    public KeyboardViewFix(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(inEditMode ? new ContextWrapperInner(context) : context, attrs, defStyleAttr, defStyleRes);
    }

    public KeyboardViewFix(Context context, AttributeSet attrs, int defStyleAttr) {
        super(inEditMode ? new ContextWrapperInner(context) : context, attrs, defStyleAttr);
    }

    public KeyboardViewFix(Context context, AttributeSet attrs) {
        super(inEditMode ? new ContextWrapperInner(context) : context, attrs);
    }

    public static class ContextWrapperInner extends ContextWrapper {
        Context base;
        public ContextWrapperInner(Context base) {
            super(base);
            this.base = base;
        }
        public Object getSystemService(String name) {
            return Context.AUDIO_SERVICE.equals(name) ? null : base.getSystemService(name);
        }       
    }
}

请注意:在启动应用程序之前,在任何其他代码之前,您需要设置KeyboardViewFix.inEditMode=false;,否则可能会出现一些错误。


嗨@Enyby,我使用了你的解决方案,但请根据以下改进进行编辑(在我的新答案中)。 - SRombauts
@SRombauts 您是正确的。这段代码是错误的,会导致问题。特别是,需要使用6个android.ContextWrapper来实现此目的。在家里我已经纠正了,然后忘记了。 - Enyby

2

我通过扩展ContextWrapper而不是Context(代码更少)来改进了@Enyvy的解决方案。 使用ContextWrapper类,将所有方法委托给基本Context,除了禁止请求“音频”的getService()方法:

public class ContextWrapperFix extends ContextWrapper {
    private boolean editMode;

    public ContextWrapperFix(Context context, boolean editMode) {
        super(context);
        this.editMode = editMode;
    }

    public Object getSystemService(String name) {
        if (editMode && Context.AUDIO_SERVICE.equals(name)) {
            return null;
        }
        return super.getSystemService(name);
    }
}

下一步是创建一个扩展KeyboardView的自定义类:
public class KeyboardViewFix extends KeyboardView {
    public static boolean inEditMode = true;

    @TargetApi(21) // Build.VERSION_CODES.L
    public KeyboardViewFix(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(new ContextWrapperFix(context, inEditMode), attrs, defStyleAttr, defStyleRes);
    }

    public KeyboardViewFix(Context context, AttributeSet attrs, int defStyleAttr) {
        super(new ContextWrapperFix(context, inEditMode), attrs, defStyleAttr);
    }

    public KeyboardViewFix(Context context, AttributeSet attrs) {
        super(new ContextWrapperFix(context, inEditMode), attrs);
    }

}

使用KeyboardViewFix替换KeyboardView

注意:在启动您的应用程序之前,需要在任何其他代码之前设置KeyboardViewFix.inEditMode = false;否则可能会出现一些错误。


如果您正在使用带有自定义键盘的弹出键盘,在弹出窗口上点击关闭按钮会导致NullPointerException。 - tagy22

0

我以前遇到过这个问题,现在我更新到了Android Studio 3.0.1,无法再次复现(可能已经很久以前修复了,我有一段时间没有打开这个项目)。

我尝试了两种AS3配置:

minSdkVersion 10, compileSdkVersion 26, support library 25.4.0
minSdkVersion 14, compileSdkVersion 26, support library 27.0.2

Android Studio的预览窗口可能有所改变。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接