2011-02-09 45 views
5

Tôi có dịch vụ web mà tôi đang cố gắng xác thực bằng nền trong chế độ xem web. Khi tôi gửi yêu cầu ban đầu nó sẽ hoạt động một cách thích hợp (thất bại/thành công dựa trên thông tin đăng nhập), nhưng sau khi nó có vẻ như tôi đang nhận được một phản hồi được lưu trữ.Vô hiệu hóa bộ nhớ đệm, cookie và mọi thứ khác trong WebView

Đây là mã thiết lập webview của tôi:

WebView browser = new WebView(this); 
WebSettings settings = browser.getSettings(); 
settings.setJavaScriptEnabled(true); 
settings.setSavePassword(false); 
settings.setCacheMode(WebSettings.LOAD_NO_CACHE); 
settings.setAppCacheEnabled(false); 
browser.setWebChromeClient(new WebChromeClient() { 
    public void onProgressChanged(WebView view, int progress) { 
    Log.d("BROWSERPROGRESS", Integer.toString(progress)); 
} 
}); 
jsInterface = new AddAccountJSInterface(); 
browser.addJavascriptInterface(jsInterface, "ADDACCOUNTJSINTERFACE"); 
browser.setWebViewClient(new AddAccountClient(this)); 

Vì vậy, như bạn có thể thấy tôi có hai lớp học thêm điều khiển WebView tôi:

  1. Một đối tượng cung cấp một giao diện cho javascript (AddAccountJSInterface)
  2. Một WebViewClient

Ngoài ra tôi có một WebChromeClient, nhưng nó chỉ có để gỡ lỗi và tôi khá chắc chắn rằng nó sẽ không can thiệp với bất cứ điều gì.

Giao diện JS chỉ đơn giản là cung cấp một cách dễ dàng để lấy HTML của cơ thể để thực hiện phân tích, vì vậy tôi tự tin rằng đó không phải là vấn đề.

WebViewClient có mã sau trong đó thực hiện hầu hết công việc "tùy chỉnh" để định tuyến dựa trên các phản hồi khác nhau từ webservice.

@Override 
    public boolean shouldOverrideUrlLoading(WebView view, String url) { 
     if(url.contains(INSTALL_PREFIX)) { 
      HashMap<String, String> params = extractParameters(url); 
      verificationComplete(params); 
      return true; 
     } 
     return false; 
    } 

    @Override 
    public void onPageFinished(WebView view, String url){ 
     if(invalidShop(view)) { 
      Toast.makeText(context, context.getString(R.string.no_find_shop), Toast.LENGTH_SHORT).show(); 
      shopAddressField.requestFocus(); 
      replaceUiElements(loadingBar, addAccountButton); 
     } else if(url.contains(ADMIN_AUTH_LOGIN)) { 
      if(invalidLogin(view)) { 
       Toast.makeText(context, context.getString(R.string.invalid_login),Toast.LENGTH_SHORT).show(); 
       emailField.requestFocus(); 
       replaceUiElements(loadingBar, addAccountButton); 
      } else { 
       String email = emailField.getText().toString(); 
       String password = passwordField.getText().toString(); 
       String submitJS = String.format(FORM_SUBMISSION_JS, email, password); 

       jsInterface.setInnerHTML(""); 

       browser.loadUrl(submitJS); 
      } 
     } 
    } 

Trong hoạt động của mình, tôi có 3 trường văn bản cần điền sau đó nhấp vào nút để gửi. Các hoạt động sau đó có trong dữ liệu từ 3 lĩnh vực văn bản (shopAddressField, usernameField, passwordField) và sau đó thực hiện một số javascript mà populates một số dữ liệu mẫu (được nạp trong webView vô hình) sau đó nhấp vào nút gửi.

Đây là phần cuối cùng gây rối, có vẻ như đang lưu vào bộ nhớ đệm từ máy chủ (có thể sử dụng cookie?) Và trả lại thay vì yêu cầu máy chủ nếu dữ liệu chính xác hay không.

Một chút làm rõ:

JSInterface chỉ đơn giản là một đối tượng Java cho phép tôi thực hiện javascript trên webview của tôi được gắn với một chức năng trong đối tượng đó. Trong trường hợp của tôi JSInterface của tôi có một hàm được setInnerHtml (String html).

Đây là javascript được thực hiện trên webview:

javascript:window.ADDACOUNTJSINTERFACE.setInnerHTML(document.body.innerHTML) 

Và đây là chức năng setInnerHtml:

public void setInnerHtml(String innerHtml) { 
    this.innerHtml = innerHtml; 
} 

Vì vậy, khi tôi thực sự thực hiện jsInterface.setInnerHtml ("") Tôi m chỉ viết quá mức HTML đã được kéo vào (để chắc chắn tôi không nhận được dữ liệu cũ của tôi từ đó vì một lý do nào đó).

Đối với submitJS của tôi nó là một lần nữa một số Javascript được thực thi trên WebView của tôi như sau:

// submitJS will be something like this once all the credentials have been set 
// Note: I know that the server will make jQuery available 
// Note: Much of the Java string formatting has been removed to help clarify 
// the code. 
String submitJS = 
    "javascript:(function() { 
     $('login-input').value='username'; 
     $('password').value='password'; 
     $('sign-in-form').up().submit(); 
    })()" 
// I then simply get the webview to execute the javascript above 
webView.loadData(submitJS); 
+3

phàn nàn trên Twitter rằng câu hỏi của bạn không nhận được sự chú ý chỉ sau 45 phút là xúc phạm. Nếu bạn muốn các câu trả lời phụ được đảm bảo, hãy thuê một nhà tư vấn. Trong khi chờ đợi, hãy xem xét giải thích những gì 'jsInterface.setInnerHTML (" ");' và 'submitJS' là, vì bạn nói chúng là nơi mà vấn đề của bạn nằm. – CommonsWare

+0

Xin lỗi. Tôi đã thêm một số chi tiết hơn về chính xác những gì javascript đang làm và làm thế nào tôi đang sử dụng đối tượng java jsInterface của tôi. Tôi hy vọng điều này sẽ giúp làm sáng tỏ câu hỏi thêm một chút. – csaunders

+0

Bổ sung phần này: "android.permission.DELETE_CACHE_FILES" trên tệp kê khai. –

Trả lời

2

Vì vậy, nó quay ra vấn đề đã không dựa trên các Caching, và có thể không cookie.

Khi thực thi javascript trên web của bạnXem nó thực hiện điều này trong một chuỗi riêng biệt và có thể khá chậm.Điều này dẫn đến một điều kiện chủng tộc gây ra mã được thực hiện theo thứ tự sai.

Tôi đã giải quyết vấn đề này bằng cách sử dụng Semaphore làm Mutex. Điều này cho phép tôi ngăn chặn getter của tôi quay trở lại trước khi Javascript trên webView có thể thực thi.

Giao diện Tôi tạo ra bây giờ trông như thế này:

private class AddAccountJSInterface { 
    private final String TAG = getClass().getName().toUpperCase(); 
    private Semaphore mutex = new Semaphore(1, false); 
    private String innerHTML; 

    public void aquireSemaphore() { 
     Log.d(TAG, "Attempting to lock semaphore"); 
     try { 
      mutex.acquire(); 
     } catch(InterruptedException e) { 
      Log.d(TAG, "Oh snap, we got interrupted. Just going to abort."); 
      return; 
     } 
     Log.d(TAG, "Semaphore has been aquired"); 
    } 

    @SuppressWarnings("unused") 
    public void setInnerHTML(String html) { 
      this.innerHTML = html; 
      Log.d(TAG, "setInnerHTML is now releasing semaphore."); 
      mutex.release(); 
      Log.d(TAG, "setInnerHTML has successfully released the semaphore."); 
    } 

    public synchronized String getInnerHTML() { 
     Log.d(TAG, "getInnerHTML attempting to aquire semaphore, may block..."); 
     String innerHTML = ""; 
     try { 
      mutex.acquire(); 

      Log.d(TAG, "getInnerHTML has aquired the semaphore, grabbing data."); 
      innerHTML = this.innerHTML; 

      Log.d(TAG, "getInnerHTML no longer needs semaphore, releasing"); 
      mutex.release(); 
     } catch (InterruptedException e) { 
      Log.d(TAG, "Something has gone wrong while attempting to aquire semaphore, aborting"); 
     } 

     return innerHTML; 
    } 
} 

Bây giờ cách tôi sử dụng điều này trong mã của tôi là như sau:

// I have access to the jsInterface object which is an instance of the class above as well as a webView which I will be executing the javascript on. 
String getInnerHtmlJS = "javascript:window.MYJSINTERFACE.setInnerHTML(document.body.innerHTML);" 
jsInterface.aquireSemaphore() 
// Execute my JS on the webview 
jsInterface.loadUrl(getInnerHtmlJS) 
// Now we get our inner HTML 
// Note: getInnerHTML will block since it must wait for the setInnerHTML (executed via the JS) function to release the semaphore 
String theInnerHTML = jsInterface.getInnerHTML(); 
+0

Tôi sẽ không đánh giá điều này là chính xác cho đến khi tôi nhận được một chút phản hồi. – csaunders

Các vấn đề liên quan