Xamarin IOS: HybridWebView не вызывает функцию (C #) - PullRequest
0 голосов
/ 14 марта 2019

Я внедрил Hybrid WebView в Xamarin IOS, следуя документации на официальном сайте Microsoft Xamarin.https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview.

Мой код точно такой же, как приведенный в документации, но я все еще публикую его для целей отладки.Постановка проблемы: функция DidReceiveScriptMessage иногда не вызывается, когда я обновляю URI веб-страницы и загружаю новую HTML-страницу.Он прекрасно работает, когда тот же обновленный URI загружается в качестве начальной страницы в веб-представлении.

По умолчанию URI (рабочий)
Обновлено URI [URI1 - URI2] (не работает)

Шаги выполнены:
1: очистить сборку и развернуть
2. Обновлены формы Xamrinдо 3,6

public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
{
    const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
    WKUserContentController userController;
    ILogUtils log = DependencyService.Get<ILogUtils>();

    protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
    {
        base.OnElementChanged(e);

        if (Control == null)
        {

            userController = new WKUserContentController();
            var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
            userController.AddUserScript(script);
            userController.AddScriptMessageHandler(this, "invokeAction");


            var config = new WKWebViewConfiguration { UserContentController = userController };
            var webView = new WKWebView(Frame, config);
            //custom delegate for enabling alert
            webView.UIDelegate = new MyWKWebViewDelegate();
            SetNativeControl(webView);
        }
        if (e.OldElement != null)
        {
            userController.RemoveAllUserScripts();
            userController.RemoveScriptMessageHandler("invokeAction");
            e.OldElement.PageChange += UpdateUri;
            var hybridWebView = e.OldElement as HybridWebView;
            hybridWebView.Cleanup();
        }
        if (e.NewElement != null)
        {
            e.NewElement.PageChange += UpdateUri;   
        }
    }

    //this method gets called when invokeCSharpAction(data) in javascript gets called
    public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
    {
        //send data to arduino
        Console.WriteLine("Data Send to Arduino "+ message.Body.ToString());
        BluetoothManager.SendData(message.Body.ToString());

    }

    public void UpdateUri(object sender, PageChangeEventArgs e)
    {
        try
        {           
            string filePath = Path.Combine(AppConstants.IOS_DIRECTORY, e.Uri);
            //this is the path for the assets
            //string filePath = Path.Combine(NSBundle.MainBundle.BundlePath, string.Format("Content/{0}", e.Uri));

            //this method tests if all files exist
            //bool allFilesExist = DoAllFilesExist();
            //if (!allFilesExist)
            //{
            //    log.Log("HybridWebViewRenderer.UpdateUri: Error - Not all files exist locally!");
            //}

            //this method tests if you can iterate from the .../index.html to the .../js/ files
            //IterateDirectories(filePath);

            bool fileExist = File.Exists(filePath);
            if (fileExist)
            {
                /*  If you want to use external local files(js, css, ...) you have to give the file the access to read the whole directory
                   --> LoadFileUrl( url: filePath , allowingReadAccessTo: AppConstants.IOS_DIRECTORY
                 */
                Control.LoadFileUrl(new NSUrl(filePath, false), new NSUrl(AppConstants.IOS_DIRECTORY, true));
            }
            else
            {
                Control.LoadHtmlString("<p>Error: File doesn't exist</p>", null);
                log.Log("HybridWebViewRenderer.UpdateUri: Error - File does not exist");
            }


        }
        catch (Exception ex)
        {
            //When there is a Error loading the file, load the following HTML-ErrorMessage
            //log.Log("HybridWebViewRenderer.UpdateUri: " + ex.Message);
            Control.LoadHtmlString("<p>Error</p>", null);
            log.Log("HybridWebViewRenderer.UpdateUri: " + ex.Message);

        }
    }

    public bool DoAllFilesExist()
    {
        iOSFileHandler handler = new iOSFileHandler();
        return handler.checkIfAllFilesAvailable(AppConstants.IOS_DIRECTORY);
    }

    public void IterateDirectories(string filePath)
    {
        try
        {
            string abc = Path.GetDirectoryName(filePath);
            string[] abcFiles = Directory.GetFiles(abc);
            string documents = Path.GetDirectoryName(abc);
            string[] documentChildFolders = Directory.GetDirectories(documents);

            string js = Path.Combine(documents, "js");
            string[] jsFiles = Directory.GetFiles(js);
            for(int i=0; i < jsFiles.Length; i++)
            {
                string content = File.ReadAllText(jsFiles[i]);
            }
        }            
        catch(Exception e)
        {

        }
    }
}

Еще один класс здесь

public class PageChangeEventArgs : EventArgs
{
    public string Uri { get; set; }
}

public class HybridWebView : View
{
    Action<string> action;
    //public AppDelegate UpdateApp;

    public static readonly BindableProperty UriProperty = BindableProperty.Create(
        propertyName: "Uri",
        returnType: typeof(string),
        declaringType: typeof(HybridWebView),
        defaultValue: default(string));

    public static readonly BindableProperty AppProperty = BindableProperty.Create(
        propertyName: "App",
        returnType: typeof(string),
        declaringType: typeof(HybridWebView),
        defaultValue: default(string));

    public string Uri
    {
        get { return (string)GetValue(UriProperty); }
        set { SetValue(UriProperty, value); }
    }

    public event EventHandler<PageChangeEventArgs> PageChange;


    public virtual void OnPageChange(PageChangeEventArgs e)
    {
        PageChange?.Invoke(this, e);
    }

    public void RegisterAction(Action<string> callback)
    {
        action = callback;
    }

    public void Cleanup()
    {
        action = null;
    }

    public void Pair()
    {
    }

    public void InvokeAction(string data)
    {
        if (action == null || data == null)
        {
            return;
        }
        action.Invoke(data);
    }
}

Главная страница здесь.

public partial class MainPage : ContentPage, IMainPage
{
    IDownloader downloader = DependencyService.Get<IDownloader>();
    IFileHandler fileHandler = DependencyService.Get<IFileHandler>();
    ILogUtils log = DependencyService.Get<ILogUtils>();

    private string StartPageId
    {
        get { return "ac"; }
    }

    public MainPage(bool firstStart)//if the app starts the first time --> true; else --> false
    {
        InitializeComponent();
        //initialize downloadEvent
        downloader.OnFileDownloaded += OnFileDownloaded;
        //update Connectionstatus
        UpdateConnectionStatusFooter();

        //labelOS.TextColor = Xamarin.Forms.Color.FromHex("#FF6600");
        //labelOS.FontAttributes = FontAttributes.Bold;
        NavigationPage.SetHasNavigationBar(this, false);

        if (firstStart == true)
        {
            //copy assets only when the app starts the first time
            CopyAssets();
            //you have to reset the dictionary because otherwise the id from the previous demonstrator is still saved(dictionary is persistent!)
            ResetDictionary();
        }


    }


    private void CopyAssets()
    {
        fileHandler.CopyAssetsToFolder().ContinueWith(t =>
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                hybridWebView.OnPageChange(new PageChangeEventArgs() { Uri = AppConstants.START_PAGE_ID + "/index.html" });
                //this method has to be called after the connection with the demo was successful. Its here for testing purposes and because the Bluetooth is not implemented yet
                downloader.test(AppConstants.START_PAGE_ID);
                //this method reads the log data to check if the log works                  
                string logtest = readLogData();
            });
            //calling this code after CopyAssetsToFolder finished
            if (t.Status != TaskStatus.RanToCompletion)
                return;
        });
    }

    private string readLogData()
    {
        string logPath = Path.Combine(AppConstants.IOS_DIRECTORY, "Log/log.txt");
        if (File.Exists(logPath))
        {
            string logContent = File.ReadAllText(logPath);
            return logContent;
        }
        else
        {
            return "";
        }
    }


    protected override void OnAppearing()
    {
        base.OnAppearing();

        UpdateConnectionStatusFooter();

        //updates the Html only when there is an id in the dictionary(after connecting to arduino)
        try
        {
            string id = Application.Current.Properties["id"].ToString();
            UpdateHTML(id);
        }
        catch (KeyNotFoundException e)
        {
            Console.WriteLine("Error in MainPage.OnAppearing: " + e.Message);
        }
    }

    public bool StartCheckingConnectionLoop()
    {
        //check every second if you are disconnected
        //if the connectionState change from true to false-->update UI and stop loop
        //you only get false here, when the following code is executed: BthSocket.Close();    (in Bluetooth.Disconnect)
        if (BluetoothManager.GetConnectionStatus() == false)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                UpdateConnectionStatusFooter();

            });
            //stop the loop
            return false;
        }
        ////if connectionState is the same -->continue with loop
        return true;
    }

    private void ResetDictionary()
    {
        try
        {
            //delete all key-values that were saved persistent
            Application.Current.Properties.Clear();
            Application.Current.SavePropertiesAsync();
        }
        catch (Exception e)
        {
            Console.WriteLine("Error in MainPage.resetDictionary: " + e.Message);
            log.Log("MainPage.ResetDictionary: " + e.Message);
        }
    }


    public void UpdateHTML(string id)
    {
        try
        {
            string savedId = Application.Current.Properties["id"].ToString();

            hybridWebView.OnPageChange(new PageChangeEventArgs() { Uri = savedId + "/index.html" });
        }
        catch (Exception e)
        {
            Console.WriteLine("Error in MainPage.updateHTML: " + e.Message);
            log.Log("MainPage.UpdateHTML: " + e.Message);
        }
    }

Может кто-нибудь дать мне подсказки, как это исправить.

...