将代码移动到async / await后,Java.Lang.JavaSystem.LoadLibrary(...)失败

时间:2014-10-15 18:33:15

标签: android xamarin mvvmcross

我在Xamarin Android应用程序(使用MVVMCross)中使用pocketsphinx进行语音识别。当我在View中使用它时效果很好,当我将它从View移动到插件中时它很有用,这样我就可以在ViewModel中使用它(使用回调事件处理程序)。

现在我正在重构(再次!)使用async / await,并且app无法加载pocketsphinx库。我的代码如下。当我接到电话时

Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");

我得到一个例外:

Java.Lang.UnsatisfiedLinkError: Library pocketsphinx_jni not found; tried [/vendor/lib/libpocketsphinx_jni.so, /system/lib/libpocket...

我使用Application.Context.ApplicationInfo.NativeLibraryDirJava.Lang.JavaSystem.Load尝试了一些组合,但没有快乐。

关于为什么现在会发生这种情况的任何想法?大概与线程有关,但我对如何解决这个问题感到难过。在我开始重构以使用async / await之前,所有这些代码都运行良好。

我的ViewModel中的调用是:

var result = await _speechRecognitionService.ListenAsync(delay);

这是我的SpeechRecognitionService类:

public class SpeechRecognitionService : Java.Lang.Object, 
    ISpeechRecognitionService, IRecognitionListener
{
    private SpeechRecognizer _recognizer;
    private Java.IO.File _assetDir;
    private bool _isHandlingResponse;
    private bool _isInitialised;
    private static EventWaitHandle _waitHandle = new AutoResetEvent(false);
    private string _response;

    public event EventHandler HeardSpeechEventHandler;

    public bool IsInitialised()
    {
        return _isInitialised;
    }

    /// <summary>
    /// Hopefully fire off the listen asynchronously
    /// </summary>
    /// <param name="delay"></param>
    /// <returns></returns>
    public async Task<string> ListenAsync(int delay)
    {
        return await Task.Run(() => DoListen(delay));
    }

    /// <summary>
    /// Initialise if required then fire up the listener
    /// </summary>
    /// <param name="delay"></param>
    /// <returns></returns>
    private string DoListen(int delay)
    {
        if (!IsInitialised())
        {
            Init();
        }
        Listen(delay);
        return "";
    }

    /// <summary>
    /// Set up the config for the listener
    /// </summary>
    public void Init()
    {
        Console.WriteLine("SpeechRecognitionIntentService ====== Init");

        try
        {
            // THIS CALL FAILS!
            Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");
            _assetDir = Edu.Cmu.Pocketsphinx.Assets.SyncAssets(Application.Context);
        }
        catch (IOException e)
        {
            throw new Exception("Failed to synchronize assets when attempting to load voice recognition software", e);
        }

        var config = Decoder.DefaultConfig();
        config.SetString("-dict", JoinPath(_assetDir, "models/lm/cmu07a.dic"));
        config.SetString("-hmm", JoinPath(_assetDir, "models/hmm/en-us-semi"));
        config.SetString("-rawlogdir", _assetDir.AbsolutePath);
        config.SetInt("-maxhmmpf", 10000);
        config.SetBoolean("-fwdflat", false);
        config.SetBoolean("-bestpath", false);
        config.SetFloat("-kws_threshold", 1e-5);
        //config.SetFloat("-samprate", 8000);
        //config.SetBoolean("-remove_noise", false);

        _recognizer = new SpeechRecognizer(config);
        _recognizer.AddListener(this);

        // Create keyword-activation search.
        //_recognizer.SetKws(KwsSearchName, Keyphrase);

        // Create grammar-based search for guide.
        var lw = config.GetInt("-lw");
        AddSearch("guide", "guide.gram", "<guide.command>", lw);

        // Create language model search.
        //var path = JoinPath(_assetDir, "models/lm/weather.dmp");
        //var lm = new NGramModel(config, _recognizer.Logmath, path);
        //_recognizer.SetLm("forecast", lm);

        _isInitialised = true;

        Console.WriteLine("InitVoiceRecognition done.");
    }

    /// <summary>
    /// Listen for "delay" seconds. Use a timer. The timer and the speech recognition callbacks both use the _isHandlingResponse flag to decide
    /// who is handling the response. If the user speaks before the timer elapses then the speech recognizer callbacks set the flag and handle
    /// it by returning the spoken string to the HeardSpeechEventHandler. If the timer elapses first, then it sets the flag and sends an empty string
    /// back to the HeardSpeechEventHandler. 
    /// </summary>
    /// <param name="delay"></param>
    public string Listen(int delay)
    {
        Console.WriteLine("Start listening for {0} seconds.", delay);
        //_recognizer.StopListening();

        try
        {
            _isHandlingResponse = false;
            _recognizer.SetSearch("guide");

            var timer = new Timer(_ =>
            {
                // If the speech recognizer is not handling the response already (i.e. the user hasn't spoken) then
                // set the flag and return an empty response
                if (!_isHandlingResponse)
                {
                    _isHandlingResponse = true;
                    _recognizer.StopListening();
                    //Debug.WriteLine("The timer elapsed for the delay of {0} seconds with NO RESPONSE ({0})", delay, CurrentObject.Label);
                    //if (HeardSpeechEventHandler != null)
                    //{
                    //  HeardSpeechEventHandler(this, new SpeechResult() {Result = string.Empty});
                    //}
                    Console.Write("Timer elapsed so setting response to '' and setting wait handle");
                    _response = string.Empty;
                    _waitHandle.Set();
                }
            }, null, delay * 1000, Timeout.Infinite);

            _recognizer.StartListening();
            Console.WriteLine("Started listening, now waiting for wait handle...");
            _waitHandle.WaitOne();
            return _response;
        }
        catch (Exception ex)
        {
            Console.WriteLine("An exception occurred attempting to listen: {0}", ex.Message);
            return "";
        }
    }

    //public IntPtr Handle { get; private set; }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="p0"></param>
    public void OnPartialResult(Hypothesis p0)
    {
        Console.WriteLine("Partial result: " + p0.Hypstr);
        // If the timer is not handling the response already (i.e. timer hasn't timed out yet) then
        // set the flag and stop listening. This will kick off the OnResult function.
        if (!_isHandlingResponse)
        {
            _isHandlingResponse = true;
            _recognizer.StopListening();

            Console.WriteLine("Partial result: " + p0.Hypstr);
        }
    }

    //http://www.albahari.com/
    /// <summary>
    /// 
    /// </summary>
    /// <param name="p0"></param>
    public void OnResult(Hypothesis p0)
    {
        //if (!_isHandlingResponse)
        //{
        //  _isHandlingResponse = true;
        Console.WriteLine("Full result, setting response to '{0}' and setting wait handle", p0.Hypstr);

        //if (HeardSpeechEventHandler != null)
        //{
        //  HeardSpeechEventHandler(this, new SpeechResult() {Result = p0.Hypstr});
        //}
        _response = p0.Hypstr;
        _waitHandle.Set();
        //}
    }

    public void OnVadStateChanged(bool p0)
    {
        //throw new NotImplementedException();
        //Console.WriteLine("OnVadStateChanged: {0}", p0);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="name"></param>
    /// <param name="path"></param>
    /// <param name="ruleName"></param>
    /// <param name="lw"></param>
    private void AddSearch(String name, String path, String ruleName, int lw)
    {
        //File grammarParent = new File(Path.Combine(_assetDir.AbsolutePath, "grammar"));
        var grammarParent = JoinPath(_assetDir, "grammar");

        var jsgf = new Jsgf(Path.Combine(grammarParent, path));
        var rule = jsgf.GetRule(ruleName);
        var fsg = jsgf.BuildFsg(rule, _recognizer.Logmath, lw);
        _recognizer.SetFsg(name, fsg);
        Console.WriteLine("AddSearch done.");
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="parent"></param>
    /// <param name="path"></param>
    /// <returns></returns>
    private static String JoinPath(Java.IO.File parent, String path)
    {
        return new Java.IO.File(parent, path).AbsolutePath;
    }
}

1 个答案:

答案 0 :(得分:1)

我通过获取当前活动的UI线程来加载Pocket Sphinx库来实现这一点,如下所示:

Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity.RunOnUiThread(() => 
                Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni"));

这样做感觉有点不对(为什么我的插件需要知道当前UI线程的任何内容?) - 但它确实有效。

相关问题