Skip to content Skip to sidebar Skip to footer

Webview How To Run Even When App Is In Background/closed (foreground Service Active)

I'm building an app which will scrape some data from a website and shows a notification when some criteria are met. Everything works well without problems when the app is open (bec

Solution 1:

[BETTER-FINAL-SOLUTION] After several hours I've discovered AndroidWebView which does exactly what I need (I'm developing this app only for Android) I've written this Browser helper class

classBrowser {
    public Android.Webkit.WebView WB;
    staticstring JSResult;

    publicclassCustomWebViewClient : WebViewClient {
        publicevent EventHandler<bool> OnPageLoaded;

        publicoverridevoidOnPageFinished(Android.Webkit.WebView view, string url) {
            OnPageLoaded?.Invoke(this, true);
        }
    }

    publicBrowser(CustomWebViewClient wc, string url = "") {
        WB = new Android.Webkit.WebView(Android.App.Application.Context);
        WB.Settings.JavaScriptEnabled = true;


        WB.SetWebViewClient(wc);
        WB.LoadUrl(url);
    }

    publicstringEvalJS(string js) {
        JSInterface jsi = new JSInterface();

        WB.EvaluateJavascript($"javascript:(function() {{ return {js}; }})()", jsi);

        return JSResult;
    }

    classJSInterface : Java.Lang.Object, IValueCallback {
        publicvoidOnReceiveValue(Java.Lang.Object value) {
            JSResult = value.ToString();
        }
    }
}

[EDIT] Improved the JS returning function with async callbacks (so the JS return value will be always delivered). Credits to ChristineZuckerman

classBrowser {
    public Android.Webkit.WebView WB;

    publicclassCustomWebViewClient : WebViewClient {
        publicevent EventHandler<bool> OnPageLoaded;

        publicoverridevoidOnPageFinished(Android.Webkit.WebView view, string url) {
            OnPageLoaded?.Invoke(this, true);
        }
    }

    publicBrowser(CustomWebViewClient wc, string url = "") {
        WB = new Android.Webkit.WebView(Android.App.Application.Context);
        WB.ClearCache(true);
        WB.Settings.JavaScriptEnabled = true;
        WB.Settings.CacheMode = CacheModes.NoCache;
        WB.Settings.DomStorageEnabled = true;
        WB.Settings.SetAppCacheEnabled(false);
        WB.Settings.UserAgentString = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/534.10";
        WB.LoadUrl(url);

        WB.SetWebViewClient(wc);
    }

    publicasync Task<string> EvalJS(string js, bool returnNullObjectWhenNull = true) {
        string JSResult = "";
        ManualResetEvent reset = new ManualResetEvent(false);

        Device.BeginInvokeOnMainThread(() => {
            WB?.EvaluateJavascript($"javascript:(function() {{ return {js}; }})()", new JSInterface((r) => {
                JSResult = r;
                reset.Set();
            }));
        });

        await Task.Run(() => { reset.WaitOne(); });
        return JSResult == "null" ? returnNullObjectWhenNull ? null : "null" : JSResult;
    }

    classJSInterface : Java.Lang.Object, IValueCallback {
        private Action<string> _callback;

        publicJSInterface(Action<string> callback) {
            _callback = callback;
        }

        publicvoidOnReceiveValue(Java.Lang.Object value) {
            string v = value.ToString();

            if (v.StartsWith('"') && v.EndsWith('"'))
                v = v.Remove(0, 1).Remove(v.Length - 2, 1);

            _callback?.Invoke(v);
        }
    }
}

Example:

Browser.CustomWebViewClient wc = new Browser.CustomWebViewClient();
wc.OnPageLoaded += BrowserOnPageLoad;

Browser browser = new Browser(wc, "https://www.google.com/");

voidBrowserOnPageLoad(object sender, bool e) {
    string test = browser.EvalJS("document.getElementsByClassName('Q8LRLc')[0].innerText");

    // 'test' will contain the value returned from the JS script// You can acces the real WebView object by using// browser.WB
}

// OR WITH THE NEW RETURNING FUNCTIONasyncvoidBrowserOnPageLoad(object sender, bool e) {
    string test = await browser.EvalJS("document.getElementsByClassName('Q8LRLc')[0].innerText");

    // 'test' will contain the value returned from the JS script// You can acces the real WebView object by using// browser.WB
}

[FINAL-SOLUTION] Finally I've found an easy and efficient alternative to WebView. Now I'm using SimpleBroswer and works great!

[SEMI-SOLUTION] Alright, I've written a workaround but I don't really like this idea, so please, if you know a better method let me know.

Below my workaround:

In my ForegroundServiceHelper interface I've added a method to check if the MainActivity (where the WebView it's rendered) is visible or not, if isn't visible the MainActivity will be shown and immediately will be hidden back. And the app will be removed from the last used applications

Method inside my ForegroundServiceHelper Interface

publicvoidInitBackgroundWebView() {
    if ((bool)SharedSettings.Entries.Get("MainPage.IsVisible") == false) {
        // Shows the activity
        Intent serviceIntent = new Intent(context, typeof(MainActivity));
        serviceIntent.AddFlags(ActivityFlags.NewTask);
        context.StartActivity(serviceIntent);
        // And immediately hides it back
        Intent main = new Intent(Intent.ActionMain);
        main.AddFlags(ActivityFlags.NewTask);
        main.AddCategory(Intent.CategoryHome);
        context.StartActivity(main);
        // Removes from the last app used
        ActivityManager am = (new ContextWrapper(Android.App.Application.Context)).GetSystemService(Context.ActivityService).JavaCast<ActivityManager>();
        if (am != null) {
            System.Collections.Generic.IList<ActivityManager.AppTask> tasks = am.AppTasks;
            if (tasks != null && tasks.Count > 0) {
                tasks[0].SetExcludeFromRecents(true);
            }
        }
    }
}

The SharedSettings class is an helper class wrapped around the App.Current.Properties Dictionary

And in the OnAppearing and OnDisappearing callbacks I set the shared values to true/false

[EDIT] This workaround works only if the user is on the homepage, so I need to find an another solution...

Post a Comment for "Webview How To Run Even When App Is In Background/closed (foreground Service Active)"