I have a WebView in one of my Activities, and when it loads a webpage, the page gathers some background data from Facebook.
What I'm seeing though, is the page displayed in the application is the same on each time the app is opened and refreshed.
I've tried setting the WebView not to use cache and clear the cache and history of the WebView.
I've also followed the suggestion here: How to empty cache for WebView?
But none of this works, does anyone have any ideas of I can overcome this problem because it is a vital part of my application.
mWebView.setWebChromeClient(new WebChromeClient()
{
public void onProgressChanged(WebView view, int progress)
{
if(progress >= 100)
{
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
}
else
{
mProgressBar.setVisibility(ProgressBar.VISIBLE);
}
}
});
mWebView.setWebViewClient(new SignInFBWebViewClient(mUIHandler));
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.clearHistory();
mWebView.clearFormData();
mWebView.clearCache(true);
WebSettings webSettings = mWebView.getSettings();
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
Time time = new Time();
time.setToNow();
mWebView.loadUrl(mSocialProxy.getSignInURL()+"?time="+time.format("%Y%m%d%H%M%S"));
So I implemented the first suggestion (Although changed the code to be recursive)
private void clearApplicationCache() {
File dir = getCacheDir();
if (dir != null && dir.isDirectory()) {
try {
ArrayList<File> stack = new ArrayList<File>();
// Initialise the list
File[] children = dir.listFiles();
for (File child : children) {
stack.add(child);
}
while (stack.size() > 0) {
Log.v(TAG, LOG_START + "Clearing the stack - " + stack.size());
File f = stack.get(stack.size() - 1);
if (f.isDirectory() == true) {
boolean empty = f.delete();
if (empty == false) {
File[] files = f.listFiles();
if (files.length != 0) {
for (File tmp : files) {
stack.add(tmp);
}
}
} else {
stack.remove(stack.size() - 1);
}
} else {
f.delete();
stack.remove(stack.size() - 1);
}
}
} catch (Exception e) {
Log.e(TAG, LOG_START + "Failed to clean the cache");
}
}
}
However this still hasn't changed what the page is displaying. On my desktop browser I am getting different html code to the web page produced in the WebView so I know the WebView must be caching somewhere.
On the IRC channel I was pointed to a fix to remove caching from a URL Connection but can't see how to apply it to a WebView yet.
http://www.androidsnippets.org/snippets/45/
If I delete my application and re-install it, I can get the webpage back up to date, i.e. a non-cached version. The main problem is the changes are made to links in the webpage, so the front end of the webpage is completely unchanged.
I found an even elegant and simple solution to clearing cache
WebView obj;
obj.clearCache(true);
http://developer.android.com/reference/android/webkit/WebView.html#clearCache%28boolean%29
I have been trying to figure out the way to clear the cache, but all we could do from the above mentioned methods was remove the local files, but it never clean the RAM.
The API clearCache, frees up the RAM used by the webview and hence mandates that the webpage be loaded again.
The edited code snippet above posted by Gaunt Face contains an error in that if a directory fails to delete because one of its files cannot be deleted, the code will keep retrying in an infinite loop. I rewrote it to be truly recursive, and added a numDays parameter so you can control how old the files must be that are pruned:
//helper method for clearCache() , recursive
//returns number of deleted files
static int clearCacheFolder(final File dir, final int numDays) {
int deletedFiles = 0;
if (dir!= null && dir.isDirectory()) {
try {
for (File child:dir.listFiles()) {
//first delete subdirectories recursively
if (child.isDirectory()) {
deletedFiles += clearCacheFolder(child, numDays);
}
//then delete the files and subdirectories in this dir
//only empty directories can be deleted, so subdirs have been done first
if (child.lastModified() < new Date().getTime() - numDays * DateUtils.DAY_IN_MILLIS) {
if (child.delete()) {
deletedFiles++;
}
}
}
}
catch(Exception e) {
Log.e(TAG, String.format("Failed to clean the cache, error %s", e.getMessage()));
}
}
return deletedFiles;
}
/*
* Delete the files older than numDays days from the application cache
* 0 means all files.
*/
public static void clearCache(final Context context, final int numDays) {
Log.i(TAG, String.format("Starting cache prune, deleting files older than %d days", numDays));
int numDeletedFiles = clearCacheFolder(context.getCacheDir(), numDays);
Log.i(TAG, String.format("Cache pruning completed, %d files deleted", numDeletedFiles));
}
Hopefully of use to other people :)
I found the fix you were looking for:
context.deleteDatabase("webview.db");
context.deleteDatabase("webviewCache.db");
For some reason Android makes a bad cache of the url which it keeps returning by accident instead of the new data you need. Sure, you could just delete the entries from the DB but in my case I am only trying to access one URL so blowing away the whole DB is easier.
And don't worry, these DBs are just associated with your app so you aren't clearing the cache of the whole phone.
To clear all the webview caches while you signOUT form your APP:
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
For Lollipop and above:
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookies(ValueCallback);
To clear cookie and cache from Webview,
// Clear all the Application Cache, Web SQL Database and the HTML5 Web Storage
WebStorage.getInstance().deleteAllData();
// Clear all the cookies
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
webView.clearCache(true);
webView.clearFormData();
webView.clearHistory();
webView.clearSslPreferences();
The only solution that works for me
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
}
This should clear your applications cache which should be where your webview cache is
File dir = getActivity().getCacheDir();
if (dir != null && dir.isDirectory()) {
try {
File[] children = dir.listFiles();
if (children.length > 0) {
for (int i = 0; i < children.length; i++) {
File[] temp = children[i].listFiles();
for (int x = 0; x < temp.length; x++) {
temp[x].delete();
}
}
}
} catch (Exception e) {
Log.e("Cache", "failed cache clean");
}
}
webView.clearCache(true)
appFormWebView.clearFormData()
appFormWebView.clearHistory()
appFormWebView.clearSslPreferences()
CookieManager.getInstance().removeAllCookies(null)
CookieManager.getInstance().flush()
WebStorage.getInstance().deleteAllData()
Simply using below code in Kotlin works for me
WebView(applicationContext).clearCache(true)
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
It can clear google account in my webview
To clear the history, simply do:
this.appView.clearHistory();
Source: http://developer.android.com/reference/android/webkit/WebView.html
Make sure you use below method for the form data not be displayed as autopop when clicked on input fields.
getSettings().setSaveFormData(false);
context.deleteDatabase("webview.db");
context.deleteDatabase("webviewCache.db")
Did the trick
to completely clear the cache in kotlin you can use:
context.cacheDir.deleteRecursively()
Just in case someone needs the kotlin code (:
Previous code has been deprecated. So, you can try this one in Kotlin base android projects:
CookieManager.getInstance().removeAllCookies {
// Do your work here.
}
Related
I am trying to load SVF file on Autodesk forge viewer locally in Xamarin.Android. I copied the content to my project Assets/html folder. My code to load the content looks like this.
In MyWebViewClient.cs
public WebResourceResponse ShouldInterceptRequest(WebView webView, IWebResourceRequest request)
{
try
{
Android.Net.Uri url = request.Url;
//Uri uri = url;
String path = url.Path;
if (path.StartsWith("/android_asset/"))
{
try
{
AssetManager assetManager = this.context.Assets;
String relPath = path.Replace("/android_asset/", "").Replace("gz", "gz.mp3");
//InputStream stream = assetManager.Open(relPath);
return new WebResourceResponse(null, null, assetManager.Open(relPath));
}
catch (IOException ex)
{
String str = ex.Message;
}
}
}
catch (Exception ex) { }
return null;
}
Then in my Activity.cs
SetContentView(Resource.Layout.webview);
var wbMain = FindViewById<WebView>(Resource.Id.webView1);
wbMain.Settings.DomStorageEnabled = true;
wbMain.Settings.JavaScriptEnabled = true;
wbMain.Settings.AllowFileAccessFromFileURLs = true;
wbMain.Settings.AllowUniversalAccessFromFileURLs = true;
var customWebViewClient = new MyWebViewClient(BaseContext);
customWebViewClient.OnPageLoaded += MyWebViewClient_OnPageLoaded;
wbMain.SetWebViewClient(customWebViewClient);
wbMain.LoadUrl("file:///android_asset/html/index.html");
This only loads the side views not the main viewer.
Whats the reason for this and how can I resolve this?
Please note that the sample is a bit outdated and there have been some changes in our legal terms since then. Currently, the legal T&C state that all viewer assets (JS, CSS, icons, images, etc.) must be coming from the Autodesk domain.
If you need to be able to run your viewer-based app in "temporarily offline" scenarios (for example, on a construction site), I'd suggest that you look at the following blog post: https://forge.autodesk.com/blog/disconnected-workflows. This approach (using Service Workers and Cache API) is consistent with the legal requirements.
In my app, I need to download few files form server. I used the following code:
function downloadFile(index:int):void
{
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, onLoadFileComplete);
urlLoader.addEventListener(ProgressEvent.PROGRESS, onLoadFileProgress);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onURLIOError);
urlLoader.load(new URLRequest("some url"));
fileNameToSave = "some name";
trace("file name:" + fileNameToSave);
downloading = true;
}
function onLoadFileProgress(e:ProgressEvent):void
{
var loadedPct:uint = Math.round(100 * (e.bytesLoaded / e.bytesTotal));
}
function onURLIOError(e:IOErrorEvent):void
{
trace("error msg");
e.target.removeEventListener(IOErrorEvent.IO_ERROR, onURLIOError);
}
function onLoadFileComplete(e:Event):void
{
trace("File downloaded");
var file:File;
file = File.documentsDirectory.resolvePath("somelocation/" + fileNameToSave);
if(file != null)
{
var fileStream:FileStream = new FileStream();
fileStream.openAsync(file, FileMode.WRITE);
fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, outputProgressHandler);
fileStream.addEventListener(Event.CLOSE, onSaveFile);
fileStream.writeBytes(e.target.data);
fileStream.close();
}
e.currentTarget.removeEventListener(Event.COMPLETE, onLoadFileComplete);
e.currentTarget.removeEventListener(ProgressEvent.PROGRESS, onLoadFileProgress);
e.currentTarget.removeEventListener(IOErrorEvent.IO_ERROR, onURLIOError);
}
function outputProgressHandler(e:OutputProgressEvent):void
{
if (e.bytesPending == 0)
{
trace("File is completely written");
}
}
function onSaveFile(e:Event):void
{
trace("Saved Complete");
loadfiles();
e.currentTarget.removeEventListener(Event.CLOSE, onSaveFile);
}
It works fine. But the problem I'm having is when the internet is slow, sometime it triggers complete event even if the file is not fully downloaded. Is there anyway to prevent this? Any help would be appreciated.
UPDATE: Is it a good practice to use progress event to check if the download is complete rather than using complete event. Like:
if(e.bytesLoaded >= e.bytesTotal)
{
//downloadComplete
}
else
{
//not complete
}
I can not comment because I have insufficient reputation.
I have previously had similar issue where filestream write reports as complete when in fact it has not. This occurs only on older devices and occurs randomly. My testing seemed to confirm that Flash reports file saved before the OS had actually completed. When a read then occurred it would report as file not found.
I solved it rather crudely using setTimeout and repeatedly checked if the data was complete.
My current monodroid project is having two problems - one in loading from Assets and the other is a generated webpage being passed to be displayed and their inter-related which makes things more annoying!
The assets problem is simple enough, the file can't be found. My current code looks like this
Android.App.Application.SynchronizationContext.Post(delegate
{
string uri = "file:///android_asset/StyleSheet.css";
string settings = string.Empty;
using (var input = c.Assets.Open(uri))
using (StreamReader sr = new System.IO.StreamReader(input))
{
settings = sr.ReadToEnd();
}
string CSS = "<html><head><style>" + settings + "</style></head><body style='height:600px;background: url(Back-AQHA.jpg)' >";
retStr = CSS + tableData.Content + "</body></html>";
callback(retStr);
}, null);
This dies at the sr.ReadToEnd() line as the file can't be found. All the permissions are correctly set for me to be able to access the assets directory and read in.
c is just a Context passed to the method
Now, assuming that the file had be read in and passed back to the caller, it now fails to be displayed (tested this by generating a page within the app and passing it back)... The webviewer code is bog standard
public class webservice_webview : Activity
{
WebView web_view;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.webview);
string res = base.Intent.GetStringExtra("content");
if (res.Length > 0)
{
web_view = FindViewById<WebView>(Resource.Id.webviewer);
web_view.Settings.JavaScriptEnabled = true;
web_view.LoadData(res, "text/html", null);
web_view.SetWebViewClient(new websiteviewClient());
}
else
return;
}
private class websiteviewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
view.LoadData(url, "text/html", null);
return true;
}
}
public override bool OnKeyDown(Android.Views.Keycode keyCode, Android.Views.KeyEvent e)
{
if (keyCode == Keycode.Back && web_view.CanGoBack())
{
web_view.GoBack();
return true;
}
return base.OnKeyDown(keyCode, e);
}
}
}
When the content is passed over and rendered, all that results is a dead webpage saying that the url data;text/html;null,[generated html] may have moved or has gone. How do I get the generated page to display?
Thanks
Paul
It might be worth check your assets are in the assets folder and are marked in the properties pane as being "AndroidAsset" - also please make sure capitalisation is correct.
Assuming they are then you can just pass the assets direct to the webview using code like:
var web = FindViewById<WebView>(Resource.Id.AboutWebView);
web.LoadUrl("file:///android_asset/About/index.html");
from https://github.com/slodge/mobile-samples/blob/master/MWC/MWC.Droid/Views/About/AboutXamarinView.cs
If you do want to read in and manipulate the html text first, then try code like: Assets.Open("About/index.html") - i.e. without the file: scheme attached.
In your second sample, the code for web_view.LoadData(res, "text/html", null); looks correct - but then I'm not sure what your logic inside ShouldOverrideUrlLoading does - that looks like it might just prevent things from loading?
I am writing a app which can programatically clear application cache of all the third party apps installed on the device. Following is the code snippet for Android 2.2
public static void trimCache(Context myAppctx) {
Context context = myAppctx.createPackageContext("com.thirdparty.game",
Context.CONTEXT_INCLUDE_CO|Context.CONTEXT_IGNORE_SECURITY);
File cachDir = context.getCacheDir();
Log.v("Trim", "dir " + cachDir.getPath());
if (cachDir!= null && cachDir.isDirectory()) {
Log.v("Trim", "can read " + cachDir.canRead());
String[] fileNames = cachDir.list();
//Iterate for the fileName and delete
}
}
My manifest has following permissions:
android.permission.CLEAR_APP_CACHE
android.permission.DELETE_CACHE_FILES
Now the problem is that the name of the cache directory is printed but the list of files cachDir.list() always returns null. I am not able to delete the cache directory since the file list is always null.
Is there any other way to clear the application cache?
"android.permission.CLEAR_APP_CACHE" android.permission.DELETE_CACHE_FILES"
Ordinary SDK applications cannot hold the DELETE_CACHE_FILES permission. While you can hold CLEAR_APP_CACHE, there is nothing in the Android SDK that allows you to clear an app's cache.
Is there any other way to clear the application cache?
You are welcome to clear your own cache by deleting the files in that cache.
Check out android.content.pm.PackageManager.clearApplicationUserData: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/android/content/pm/PackageManager.java/
The other hidden methods in that class might be useful, too.
In case you've never used hidden methods before, you can access hidden methods using Java reflection.
poate iti merge asta
static int clearCacheFolder(final File dir, final int numDays) {
int deletedFiles = 0;
if (dir!= null && dir.isDirectory()) {
try {
for (File child:dir.listFiles()) {
//first delete subdirectories recursively
if (child.isDirectory()) {
deletedFiles += clearCacheFolder(child, numDays);
}
//then delete the files and subdirectories in this dir
//only empty directories can be deleted, so subdirs have been done first
if (child.lastModified() < new Date().getTime() - numDays * DateUtils.DAY_IN_MILLIS) {
if (child.delete()) {
deletedFiles++;
}
}
}
}
catch(Exception e) {
Log.e("ATTENTION!", String.format("Failed to clean the cache, error %s", e.getMessage()));
}
}
return deletedFiles;
}
public static void clearCache(final Context context, final int numDays) {
Log.i("ADVL", String.format("Starting cache prune, deleting files older than %d days", numDays));
int numDeletedFiles = clearCacheFolder(context.getCacheDir(), numDays);
Log.i("ADVL", String.format("Cache pruning completed, %d files deleted", numDeletedFiles));
}
I'm not sure how appropriate this is in terms of convention, but this works so far for me in my Global Application class:
File[] files = cacheDir.listFiles();
for (File file : files){
file.delete();
}
Of course, this doesn't address nested directories, which might be done with a recursive function like this (not tested extensively with subdirectories):
deleteFiles(cacheDir);
private void deleteFiles(File dir){
if (dir != null){
if (dir.listFiles() != null && dir.listFiles().length > 0){
// RECURSIVELY DELETE FILES IN DIRECTORY
for (File file : dir.listFiles()){
deleteFiles(file);
}
} else {
// JUST DELETE FILE
dir.delete();
}
}
}
I didn't use File.isDirectory because it was unreliable in my testing.
I have an app that loads an URL. My problem is that it shows me the old version of that webpage and never loads the new one(I think it keeps the webpage in cache and loads it from there even though I have wireless connection). Is there a way to programmatically specify my app to never keep the webpages in memory? Or maybe I have a different problem that need another approach.
try something like this(not tested) for deleting the cache at the end of session:
private int clearCacheFolder(){
deletedFiles = 0;
File dir = context.getCacheDir();
if (dir!= null && dir.isDirectory()) {
try {
for (File child:dir.listFiles()) {
//delete subderictories
if (child.delete()) {
deletedFiles++;
}
}
}
}
catch(Exception e) {
}
}
return deletedFiles;
}
or
setAppCacheEnabled(false);
for disabling it
//cache is per-application, so this will clear the cache for all WebViews used.
clearCache(boolean includeDiskFiles)
// prob not what you want
clearFormData()
// prob not what you want
clearHistory()