Android webview cookie返回null

时间:2013-10-07 21:54:59

标签: java javascript android cookies webview

我正在尝试在android中的webview中设置和检索cookie。我尝试了很多cookie管理器脚本来尝试让它工作。我启用了JavaScript。

在Samsung S3和Samsung Galaxy Tab 10.1上运行应用程序时,似乎根本没有设置cookie(android 4.1)。但是,当在三星Galaxy ace,HTC Desire Z和Android模拟器上运行软件时,cookie被设置并读取完全正常。

工作时,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();
}

4 个答案:

答案 0 :(得分:3)

经过一个多月的研究,我得出的结论是,在webview中设置一个大于2.3的cookie的cookie是不可能的,因为webview中的文件位于localhost上(直接从assets文件夹中读取)。

我经历了许多使用cookie存储的替代方案,包括使用HTML的本地存储。我被引导相信,这里的整个问题是安全问题。如果我要在localhost上创建cookie或localstorage,那么其他应用程序可以访问该存储,因此是一个主要的安全威胁。

我的最终问题是尝试通过与java的双向通信来桥接webview,并且还有一个在此过程中存储数据的地方。我的解决方案是利用 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中的fairly serious bug(在3.0中修复),它有效地打破了JavascriptInterface。上面的第二个链接描述了我找到的最佳解决方法。

这是一个如何使双向通信工作的例子(它非常混乱,但它有效):

自定义网络接口类:

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

主要课程:     protected void 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 :(得分:3)

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

根据@ kirgy和@ 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功能(到期,路径),我将此作为一个良好起点的想法发布。

答案 2 :(得分: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;
}
祝你好运,希望它有效!

答案 3 :(得分:1)

我遇到了与kirgy相同的问题。我试图在显示本地内容的Droid webview中使用cookie(使用javascript)。它不会存储数据。如果从同一webview中的远程URL运行,相同的代码工作得很好。在java中启用cookie(使用Android CookieManger)无效。

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

根据我的需要,我只需要会话存储,所以只需将cookie数据放入java HashMap即可。我并不担心失效日期或长期存储。如果您需要&#34;虚拟cookie&#34;为了实现这些细节,一旦数据通过js接口进出java,就不难理解。