2013-10-07 183 views
6

我想简单地设置和检索android中的webview内的cookie。我已经尝试了许多Cookie管理器脚本来尝试使这个工作。我启用了JavaScript。Android的webview cookie返回null

在三星S3和三星Galaxy Tab 10.1上运行应用程序时,根本没有设置cookie(android 4.1)。但是,在三星Galaxy ace,HTC Desire Z以及android模拟器上运行软件时,cookies会被设置并读取得非常好。

工作时,webview按预期返回字符串,不工作时输出只是“null”;该cookie没有值/未设置。

我的具体情况也使用滑动导航类,它是Actionbar Sherlock的扩展。

我真的很感谢任何帮助,我一直在为此奋斗了几个星期。 谢谢。

HTML:

<html> 
<head> 
    <title> 
    </title> 
<script> 
    function createCookie(name, value) 
    { 
      var day = (1 * 24 * 60 * 60 * 1000); 
      var date = new Date(); 
      date.setTime(date.getTime() + (20 * 365 * day)); 
      var expires = "; expires=" + date.toGMTString(); 

      document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 
      document.cookie = name + "=" + value + expires + "; path=/"; 
    } 

    function readCookie(name) 
    { 
     var nameEQ = name + "="; 
     var ca = document.cookie.split(';'); 
     for(var i=0;i < ca.length;i++) { 
      var c = ca[i]; 
      while (c.charAt(0)==' ') c = c.substring(1,c.length); 
      if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); 
     } 
     return null; 
    } 

</script> 
</head> 
<body> 
<h1 class=""> 
<script type="text/javascript"> 
    createCookie("test", "If this is working, it returns this string. If this is not working, it returns null."); 
    document.write("test: " + readCookie("test")); 
</script> 
</body> 
</html> 

Java代码:

公共类MainActivity扩展SherlockActivity实现ISideNavigationCallback {

public static final String EXTRA_TITLE = "com.devspark.sidenavigation.sample.extra.MTGOBJECT"; 
public static final String EXTRA_RESOURCE_ID = "com.devspark.sidenavigation.sample.extra.RESOURCE_ID"; 
public static final String EXTRA_MODE = "com.devspark.sidenavigation.sample.extra.MODE"; 
public static String WebLoaded = "0"; 
public static String page = "signup.php"; 

private ImageView icon; 
private SideNavigationView sideNavigationView; 
private WebView engine; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.activity_main); 

    icon = (ImageView) findViewById(android.R.id.icon); 
    sideNavigationView = (SideNavigationView) findViewById(R.id.side_navigation_view); 
    sideNavigationView.setMenuItems(R.menu.side_navigation_menu); 
    sideNavigationView.setMenuClickCallback(this); 

    if (getIntent().hasExtra(EXTRA_TITLE)) { 
     String title = getIntent().getStringExtra(EXTRA_TITLE); 
     int resId = getIntent().getIntExtra(EXTRA_RESOURCE_ID, 0); 
     setTitle(title); 
     icon.setImageResource(resId); 
     sideNavigationView.setMode(getIntent().getIntExtra(EXTRA_MODE, 0) == 0 ? Mode.LEFT : Mode.RIGHT); 
    } 

    //test 
    getSupportActionBar().setDisplayHomeAsUpEnabled(true); 

    String domain = "localhost"; 

    CookieManager cookieManager = CookieManager.getInstance(); 
    cookieManager.setAcceptCookie(true); 
    cookieManager.setCookie(domain, "name=value"); 
    cookieManager.setCookie(domain, "path=/"); 
    cookieManager.setCookie(domain, "HttpOnly"); 

    //enable cookies 
    CookieManager.getInstance().setAcceptCookie(true); 
    //navigates web engine, including on nav click 
    engine = (WebView) findViewById(R.id.web_engine); 


    engine.loadUrl("file:///android_asset/" + page); 
    //enable JavaScript support - disabled by default for some weird reason 
    engine.getSettings().setJavaScriptEnabled(true); 
    engine.setWebViewClient(new WebViewClient());   






    //disables text selection 
    engine.setOnLongClickListener(new View.OnLongClickListener() { 
     public boolean onLongClick(View v) { 
      return true; 
     } 
    });  
} 

@Override 
public void onPause() 
{ 
    super.onPause(); 
    engine.getSettings().setJavaScriptEnabled(false); 
} 

@Override 
public void onResume() 
{ 
    super.onResume(); 
    engine.getSettings().setJavaScriptEnabled(true); 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    switch (item.getItemId()) { 
     case android.R.id.home: 
      sideNavigationView.toggleMenu(); 
      break; 
     case R.id.mode_left: 
      item.setChecked(true); 
      sideNavigationView.setMode(Mode.LEFT); 
      break; 
     case R.id.mode_right: 
      item.setChecked(true); 
      sideNavigationView.setMode(Mode.RIGHT); 
      break; 

     default: 
      return super.onOptionsItemSelected(item); 
    } 
    return true; 
} 

@Override 
public void onSideNavigationItemClick(int itemId) { 


    switch (itemId) { 
     case R.id.side_navigation_menu_item1: 
      invokeActivity(getString(R.string.title1), R.drawable.ic_android1);  
      page = "index.html"; 
      break; 

     case R.id.side_navigation_menu_item2: 
      invokeActivity(getString(R.string.title2), R.drawable.ic_android2); 
      page = "test.html"; 
      break; 

     case R.id.side_navigation_menu_item3: 
      invokeActivity(getString(R.string.title3), R.drawable.ic_android3); 
      break; 

     case R.id.side_navigation_menu_item4: 
      invokeActivity(getString(R.string.title4), R.drawable.ic_android4); 
      break; 

     case R.id.side_navigation_menu_item5: 
      invokeActivity(getString(R.string.title5), R.drawable.ic_android5); 
      break; 

     default: 
      return; 
    } 
    finish(); 
} 
+0

您是否在运行过程中获得了logcat中的相关错误/警告?你没有在JS中指定域名,所以它会被解析成当前的主机,它可能是'localhost',或者一个特定的IP地址,我不确定它总是被安全处理(相同的原点)。另外我不明白你在Java中使用cookie的操作 - 它们是否应该对JavaScript部分有一些影响? – Stan

+0

我假设你已经检查了这篇文章:http://blog.winfieldpeterson.com/2013/01/17/cookies-in-hybrid-android-apps/和这个问题:http://stackoverflow.com/问题/ 1652850/android-webview-cookie-problem。你是否试图解决它?结果是什么? –

+0

最近的Android版本需要cookie值中设置的过期日期!否则它不会被CookieManager接受。 – sk2212

回答

3

超过一个月的研究后,我已经得出结论,在设定的Android版本大于2.3的一个cookie无法在网页视图文件所在的本地主机(从资产文件夹中直接读取)。

我经历了许多替代使用cookie存储,包括使用HTML的本地存储。我认为,这里的所有问题都是安全问题。如果我要在本地主机上创建cookie或本地存储,则其他应用程序可以访问该存储,这是一个主要的安全威胁。

我最终的问题是试图弥合与Java的双向通信的网页流量,并且还具有过程中存储数据的地方。我的解决办法就是把JavascriptInterface的优势

的想法如下:

  • 解析数据的功能在javascript
  • 的Javascript例如调用一个特殊的JavaScript函数“使用setData(myDataName,MYDATA的)”
  • Java有监听到这一点,并会采取通过javscript设置参数,也可以返回值的JavaScript函数。
  • 将数据解析为Java后,java将其存储在资产中的一系列文件中。
  • 当javascript函数“的getData(myDataName)”之称,该数据是由Java中的同一种方法的返回。

我发现这非常有用:Android JavascriptInterface DocumentationHandling JavascriptInterface in Android 2.3

唯一的主要“挂起等”都配备了什么,我发现在的Android 2.3(固定在3.0),从而有效地打破了JavascriptInterface一个fairly serious bug。上面的第二个链接描述了我找到的最佳解决方法。

下面是如何获得双向通信工作的例子(这是非常混乱的,但它的工作原理):

定制webinterface类:

public class webViewInterface { 
    //@JavascriptInterface //for SDK 12+ 
    public String showToast(String myText) { 
     Toast.makeText(context, myText, Toast.LENGTH_LONG).show(); 
     return myText; 
    } 
    } 

主要类: 保护无效的onCreate (Bundle savedInstanceState){ super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main); 

engine = (WebView) findViewById(R.id.web_engine); 

context = this; 
WebSettings webSettings = engine.getSettings(); 
webSettings.setJavaScriptEnabled(true); 

engine.setWebViewClient(new WebViewClient()); 
engine.loadUrl("file:///android_asset/" + page); 

boolean javascriptInterfaceBroken = false; 
try { 
     if (Build.VERSION.RELEASE.startsWith("2.3")) { 
     javascriptInterfaceBroken = true; 
     } 
    } catch (Exception e) { 

    // Ignore, and assume user javascript interface is working correctly. 
    } 

// Add javascript interface only if it's not broken 
    // @TODO: write the workaround for < 2.3 devices 
if (!javascriptInterfaceBroken) { 
     engine.addJavascriptInterface(new webViewInterface(), "MainActivityInterface"); 
} 

//disables text selection 
engine.setOnLongClickListener(new View.OnLongClickListener() { 
    public boolean onLongClick(View v) { 
     return true; 
    } 
});  
} 

HTML:

<html> 
<head> 
</head> 
<body> 

<input type="button" value="Say hello" onClick="showAndroidToast();" /> 

<script type="text/javascript"> 
    function showAndroidToast() { 
     document.write(MainActivityInterface.showToast("eureka!")); 
    } 
</script> 

</body> 
</html> 
1

您需要设置cookie的有效期,否则该cookie不会被接受。

事情是这样的:

document.setCookie = function(sName,sValue) 
{ 
    var oDate = new Date(); 
    oDate.setYear(oDate.getFullYear()+1); 
    var sCookie = sName.escape() + '=' + escape(sValue) + ';expires=' + oDate.toGMTString() + ';path=/'; 
    document.cookie= sCookie; 
} 

祝你好运,希望工程!

+0

嗨,我真的很感谢你的意见。我试图实现,并没有为我工作。但在我发布这篇文章后的调查中,我发现问题是我无法在从/ assets目录加载HTML文件时设置cookie - 当从Web上加载相同的文件时,Cookie设置正常。你知道这是为什么吗?我真的很感谢你的帮助! – kirgy

+0

我不知道这是为什么,也许尝试重新加载,看看是否可行。 – 2013-10-18 01:57:56

+1

我认为这是因为webview中的安全原因,cookie设置为针对本地主机。解决方法是将cookie设置为远程主机,从而丢失本地托管文件,或将cookie全部取消。我做了以后,并使用JavaScript接口来解决存储数据的问题。 – kirgy

1

我遇到了同样的问题,因为kirgy。我试图在显示本地内容的Droid webview中使用cookie(使用JavaScript)。它不会存储数据。尽管如果从同一webview中的远程URL运行相同的代码工作完美。在Java中启用cookie(使用Android CookieManger)不起作用。

幸运的是,我使用自定义JavaScript函数来封装我所有的cookie需求在本地web应用程序中。使用webview JavaScript界面​​,在Droid上运行时,我只是通过在Java中处理它来以虚拟方式管理我的Cookie。

为了我的需要,我只需要会话存储,因此只需将cookie数据放入java HashMap。我不担心到期日期或长期存储。如果你需要你的“虚拟饼干”来实现这些细节,一旦数据通过js接口进入和从java进入并不难。

+0

我以前是“user3220983” – BuvinJ

3

我遇到了同样的限制。我从资产fodler加载本地html文件。在2.2模拟器上,Cookie被存储并可以通过JavaScript获取。但是,在我的4.1设备上测试时,本地页面设置的Cookie不会持续。

根据@ kirgy's和@ user3220983的建议,我使用了Javascript界面​​。

webview.addJavascriptInterface(new JavaScriptInterface(this), "Android"); 

public class JavaScriptInterface {   
    ... 

    @JavascriptInterface 
    public void setCookie(String name, String value) { 
     Editor editor = activity.settings.edit();  
     editor.putString(name, value); 
     editor.commit(); 
    } 

    @JavascriptInterface 
    public String getCookie(String name) { 
     return activity.settings.getString(name, ""); 
    } 

    @JavascriptInterface 
    public void deleteCookie(String name) { 
     Editor editor = activity.settings.edit();  
     editor.remove(name); 
     editor.commit(); 
    } 

注:这些功能不提供完整的cookie功能(过期,路径),我张贴这是一个很好的起点的想法。