6

我试图简单地在android的webview中设置和检索一个cookie。我已经尝试了许多 cookie 管理器脚本来尝试让它工作。我启用了 JavaScript。

在三星 S3 和三星 Galaxy Tab 10.1 上运行应用程序时,cookie 似乎根本没有设置(android 4.1)。然而,在三星 Galaxy ace、HTC Desire Z 和安卓模拟器上运行该软件时,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

4 回答 4

4

经过一个多月的研究,我得出结论,在文件位于本地主机上的 web 视图中,无法在大于 2.3 的 android 版本中设置 cookie(直接从 assets 文件夹中读取)。

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

我的最终问题是试图通过与 java 的双向通信来桥接 webview,并且在此过程中还有一个存储数据的地方。我的解决方案是利用JavascriptInterface

思路如下:

  • 解析数据以在 javascript 中运行
  • Javascript 调用一个特殊的 javascript 函数,例如“setData(myDataName, myData)”
  • Java 有一个监听这个的函数,它将接受 javscript 设置的参数,也可以将值返回给 javascript。
  • 一旦数据被解析为 Java,java 将其存储在 assets 中的一系列文件中。
  • 当调用 javascript 函数“getData(myDataName)”时,数据由 Java 以同类方法返回。

我发现这非常有用:Android JavascriptInterface文档Android 2.3 中的处理 JavascriptInterface

我发现的唯一主要“挂断”是 Android 2.3 中的一个相当严重的错误(在 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>
于 2013-10-19T22:29:12.483 回答
3

我遇到了同样的限制。我正在从资产文件夹加载本地 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 功能(过期、路径),我将其发布为一个好的起点。

于 2014-07-22T20:59:11.643 回答
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;
}

祝你好运,希望它有效!

于 2013-10-17T12:02:55.603 回答
1

我遇到了和kirgy一样的问题。我试图在显示本地内容的 Droid webview 中使用 cookie(使用 javascript)。它不会存储数据。但是,如果从同一 web 视图中的远程 url 运行相同的代码,则可以完美运行。在 java 中启用 cookie(使用 Android CookieManger)没有效果。

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

对于我的需要,我只需要会话存储,所以只需将 cookie 数据放在 java HashMap 中。我不担心过期日期或长期存储。如果您需要您的“虚拟 cookie”来实现这些细节,那么一旦数据通过 js 接口传入和传出 java,应该不难弄清楚。

于 2014-07-01T17:49:11.720 回答