In my app, I am loading a list of external url's in webview and allow user to flip through them. Webviews are loaded on to a view flipper. I find the performance is really bad in webview load url. I have tried everything from using the frame layout to limiting the number of webviews to load. Still the performance is not satisfactory.
How do I optimize the performance of webview? This should be a common usage. Am I missing something obvious.
My Webview settings are -
webView.setInitialScale(WEBVIEW_SCALE);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setBuiltInZoomControls(false);
webView.setWebViewClient(new MyWebViewClient());
webView.setOnTouchListener( new OnTouchListener());
Try this:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else {
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
I think the following works best:
if (Build.VERSION.SDK_INT >= 19) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
else {
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
Android 19 has Chromium engine for WebView. I guess it works better with hardware acceleration.
For more info Android 4.4 KitKat, the browser and the Chrome WebView
This has already been discussed here: Enhance webView performance (should be the same performance as native Web Browser)
I ran into a similar issue, and after some heavy debugging noticed the
native browser and WebView browser seem to be using different caches.
This code can be used to disable the WebView cache, and made WebView
much faster for me (though at the expense of not caching). Note that
it uses private APIs, so by using it you're risking the code will
break in future releases:
try
{
Method m = CacheManager.class.getDeclaredMethod("setCacheDisabled", boolean.class);
m.setAccessible(true);
m.invoke(null, true);
}
catch (Throwable e)
{
Log.i("myapp","Reflection failed", e);
}
Improvise Answer:
The above Solution mentioned CacheManager.class is not supported.
try {
val m: Method = ServiceWorkerWebSettingsCompat.CacheMode::class.java.getDeclaredMethod(
"setCacheDisabled",
Boolean::class.javaPrimitiveType
)
m.isAccessible = true
m.invoke(null, true)
} catch (e: Throwable) {
Log.i("myapp", "Reflection failed", e)
}
Related
I have an android application that has native framework and content itself is presented in web format and in webview. The meaning of the application is to allow users to use the device using predefined
services that may require autentication.
How ever when I try to clean up the webview caches after user has completed his/her tasks the webview will remember everything and all e.g. login credentials are in place, history remains etc.
I have tried the following to do the clean up without any success, what I am missing in this ?
(wvfo if the overlay fragment in which the webview is that each service is using)
wvfo.getWebView().clearCache(true);
wvfo.getWebView().clearFormData();
wvfo.getWebView().clearHistory();
wvfo.getWebView().clearMatches();
// wvfo.getWebView().setWebViewClient(new WebViewClient());
// wvfo.loadUrl("javascript:document.open();document.close();");
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
wvfo.destroyWebview();
Any ideas what is wrong with this and why the history doesn't get cleared ?
Thanks in advance
Yes, You have to delete webview default DB also. Check the below code.
static void clearWebViewAllCache(Context context, WebView webView) {
try {
AgentWebConfig.removeAllCookies(null);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
context.deleteDatabase("webviewCache.db");
context.deleteDatabase("webview.db");
webView.clearCache(true);
webView.clearHistory();
webView.clearFormData();
clearCacheFolder(new File(AgentWebConfig.getCachePath(context)), 0);
} catch (Exception ignore) {
//ignore.printStackTrace();
if (AgentWebConfig.DEBUG) {
ignore.printStackTrace();
}
}
}
I hope, this will help you.
Happy codding...
Is there a way to check if webview is available on the device?
Background:
If I add <uses-feature android:name="android.software.webview"/> to the Manifest the number of supported devices on Google Play drops from over 12,000 to less than 6,000 devices. So I added android:required="false" to this feature. In case webview is available websites should be displayed inside the app otherwise launched in the default browser:
String mUrl = "http://www.example.com";
if (*** WHAT TO PUT HERE? ***) {
WebView webview = (WebView) findViewById(R.id.webView);
if (Build.VERSION.SDK_INT >= 24) {
webview.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.toString());
return false;
}
});
} else {
webview.setWebViewClient(new WebViewClient() {
#SuppressWarnings("deprecation")
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
}
});
}
webview.loadUrl(mUrl);
} else {
Uri uri = Uri.parse(mUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
Edit (to make things clear): <uses-permission android:name="android.permission.INTERNET" /> is (and always had been) part of the manifest. It’s just the addition of <uses-feature android:name="android.software.webview" /> which causes the drop of supported devices.
There is someone having the same issue here: https://issuetracker.google.com/issues/37035282
(unfortunately not answered)
Although #vsatkh pointed out that it is not necessarily needed to declare this feature as required, you can check the device’s feature compatibility as follows:
getPackageManager().hasSystemFeature("android.software.webview")
This method returns true or false.
Some additional information about Google Play’s filtering:
Google Play only filters supported devices based on <uses-feature> elements declared in the manifest. <uses-permission> elements don’t affect Google Play’s filtering unless they imply a feature. android.permission.INTERNET does not imply any feature. Permissions that imply features are listed here:
https://developer.android.com/guide/topics/manifest/uses-feature-element.html#permissions
Alternative solution:
val hasWebView: Boolean = kotlin.runCatching { CookieManager.getInstance() }.isSuccess
It works because if WebView is not available then CookieManager.getInstance() will throw AndroidRuntimeException.
getPackageManager().hasSystemFeature("android.software.webview") is not reliable. It will return true if the device is supposed to have a webview, but has disabled it in system settings.
I tested it on a Huawei P20 by going to Settings -> Apps -> Android System WebView -> Disable
There is a way around: CookieManager
AFAIK: There is no dedicated API that gives you that information. But if I told you there is a way around.
As Torkel mentioned, getPackageManager().hasSystemFeature(..) does not guarantee to tell the Android System WebView is enabled or disabled.
getPackageManager().hasSystemFeature(..) will still return true even a user disables the WebView in Settings.
CookieManager
So an alternative would be to use CookieManager.
An application WebView cookie is managed by CookieManager. It throws an exception if you try to create CookieManager instance when the WebView is disabled.
In Kotlin, you can get result from block statements but it is not possible in Java. So you can use Exception Handling logic in a function to decide.
Here is an example
public boolean webViewEnabled(){
try{
CookieManager.getInstance();
return true;
}catch(Exception e){
return false;
}
}
Note:
The current implementation might change in the future, or they might come up with a dedicated API for this. I have tried finding a perfect solution in many forums but this is the closest I have reached.
This solution will prevent your app from crashing but you will still see the error in your logs because CookieManager.getInstance throws an Exception object.
You can read more detail here https://source.android.com/compatibility/android-cdd.pdf.
According to the section WebView Compatibility, android.software.webview feature is indicate that device or oem must provides a complete implementation of the android.webkit.WebView API. So the number of device on play store was drop because actually, most device did not fully implemented all api required by webkit.
So if your web content required certain html5 feature please check here http://mobilehtml5.org/ if your app target kitkat or even lollipop+ then you should be safe.
So there is no need to declare android.software.webview feature unless your web content really need all api of webkit.
As of API 26, you can use WebView.getCurrentWebViewPackage() to check if there is a valid WebView installed and active. The CookieManager check can be really slow (upwards of 500ms) and can impact startup performance if the check is happening in the critical path.
From the documentation:
If WebView has already been loaded into the current process this method will return the package that was used to load it. Otherwise, the package that would be used if the WebView was loaded right now will be returned; this does not cause WebView to be loaded, so this information may become outdated at any time.
The WebView package changes either when the current WebView package is updated, disabled, or uninstalled. It can also be changed through a Developer Setting. If the WebView package changes, any app process that has loaded WebView will be killed. The next time the app starts and loads WebView it will use the new WebView package instead.
#return the current WebView package, or {#code null} if there is none.
You can verify that this is working as expected by swapping the WebView implementation and then calling this method to list the PackageName (e.g if you're running the dev/beta Chrome WebViews) by calling:
WebView.getCurrentWebViewPackage()?.packageName
You can test disabled state by running the following ADB command (although you might need an emulator running as root via adb root): adb shell pm disable <webview-package-id>.
WebView has been had in Android API 1 so it's always available what's not it's the internet connection
https://developer.android.com/reference/android/webkit/WebView.html
In you manifest you need internet permission :
<uses-permission android:name="android.permission.INTERNET" />
I am having a webApp that loads in a webView. I don't want remote debugging to be enabled on the webview.
I have read several blogs and unable to find any related to this query. Can someone provide any pointer on this.
Appreciate your help!!!
allow only for build debug then
if (BuildConfig.DEBUG) {
WebView.setWebContentsDebuggingEnabled(true);
}
or
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}
( The default is false.)
source :
Documentation
Java Documentation
I am building an app that requires a lot of drawing on the canvas. I notice that the app is a bit laggy in devices with high resolution (nexus 7 for example). I saw there is a Force GPU option in the developer option. When Force GPU is enabled, my app runs absolutely smooth.
I have read that this Force GPU option is called Hardware Acceleration and it is available only for Android 3.0 and above.
My app is targeting Android 2.3 and above.
Is it possible to programmatically enable Hardware Accelerated (or Force GPU--whatever the magic is called) on any Android 3.0 or above devices?
Something like:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
Turn On Hardware Accelerate HERE but How can i do this?
any code snippet would be welcome/helpful/thanks
}
I assume you've already added android:hardwareAccelerated to your Manifest file?
<application android:hardwareAccelerated="true" ...>
That is what enables hardware acceleration within your application per the guide on hardware acceleration and should do exactly what forcing GPU does at a system level.
Set minSdkVersion to 10 and targetSdkVersion to maximum
Like below
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="17" />
then
<application android:hardwareAccelerated="true" ...>
Now will work
And for particularities
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB){
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
or
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB){
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
If you want to build your application using lower api level you can access the method via reflection:
try {
Method setLayerType = view.getClass().getMethod(
"setLayerType", new Class[] { int.class, Paint.class });
if (setLayerType != null)
setLayerType.invoke(view, new Object[] { LAYER_TYPE_X, null });
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
Where LAYER_TYPE_X is the constant integer value of wanted layer type:
LAYER_TYPE_NONE = 0
LAYER_TYPE_SOFTWARE = 1
LAYER_TYPE_HARDWARE = 2
I'm trying to follow the example from google:
http://code.google.com/apis/maps/articles/android_v3.html
Using the example files from their SVN repo:
(http)gmaps-samples.googlecode.com/svn/trunk/articles-android-webmap/
But although it seems to compile and export it fails; can someone sanity check that its not just me this fails for and any hints as to if its a quick thing to fix, I've been prodding it with try/catch for the last 2 hours to no avail.
Thanks :o)
Forgot to add
It Compiles and Uploads to the emulator (and to my phone) but running it just results in
The Application WebMapActivity (process com.google.android.examples.webmap)
has stopped unexpectedly. Please try again. [Force Close]
Filtered it down to
private void setupWebView() {
/*
final String centerURL = "javascript:centerAt("
+ mostRecentLocation.getLatitude() + ","
+ mostRecentLocation.getLongitude() + ")";
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
// Wait for the page to load then send the location information
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
webView.loadUrl(centerURL);
}
});*/
webView = (WebView) findViewById(R.id.webview);
webView.loadUrl(MAP_URL);
}
Bit easier to filter out the actual issue when you know which bit doesnt work thanks to CommonsWare for the great book, poking through that and test-code that ACTUALLY WORKS made it a bit easier to work out what was going on. And thanks for the debug info :o)