Android中CookieManager的底层实现
前几天,项目组有个技术问题,想用本地加载html, js写cookie的方法,绕过去。
想法是没有问题的, 但是测试的时候发现, 每次重新打开App, 都取不到以前Cookie里面设的值。
后来去App/data下面找WebView.db,打开来一看,cookie情报根本没有存。
原因是js写cookie的时候,没有指明expire, WebKit默认把它当成临时cookie, webview终了之后就丢失了。
这个问题本身不复杂,不过趁机我看了一把Andorid底层读取Cookie的source root:
以下source root
1. 取Cookie的API
- CookieManager.getInstance().getCookie(url);
CookieManager.getInstance().getCookie(url);
2.CookieManager的getInstance()
- /**
- * Gets the singleton CookieManager instance. If this method is used
- * before the application instantiates a {@link WebView} instance,
- * {@link CookieSyncManager#createInstance(Context)} must be called
- * first.
- *
- * @return the singleton CookieManager instance
- */
- publicstaticsynchronized CookieManager getInstance() {
- return WebViewFactory.getProvider().getCookieManager();
- }
/** * Gets the singleton CookieManager instance. If this method is used * before the application instantiates a {@link WebView} instance, * {@link CookieSyncManager#createInstance(Context)} must be called * first. * * @return the singleton CookieManager instance */ public static synchronized CookieManager getInstance() { return WebViewFactory.getProvider().getCookieManager(); }3.WebViewFactory是个工厂模式,额
- staticsynchronized WebViewFactoryProvider getProvider() {
- // For now the main purpose of this function (and the factory abstraction) is to keep
- // us honest and minimize usage of WebViewClassic internals when binding the proxy.
- if (sProviderInstance != null) return sProviderInstance;
- sProviderInstance = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY);
- if (sProviderInstance == null) {
- if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage");
- sProviderInstance = new WebViewClassic.Factory();
- }
- return sProviderInstance;
- }
static synchronized WebViewFactoryProvider getProvider() { // For now the main purpose of this function (and the factory abstraction) is to keep // us honest and minimize usage of WebViewClassic internals when binding the proxy. if (sProviderInstance != null) return sProviderInstance; sProviderInstance = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY); if (sProviderInstance == null) { if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage"); sProviderInstance = new WebViewClassic.Factory(); } return sProviderInstance; }4.层层嵌套,最后生成的instance是CookieManagerClassic
- @Override
- public CookieManager getCookieManager() {
- return CookieManagerClassic.getInstance();
- }
@Override public CookieManager getCookieManager() { return CookieManagerClassic.getInstance(); }5. getCookie方法 实现在CookieManagerClassic上
- @Override
- public String getCookie(String url) {
- return getCookie(url, false);
- }
- @Override
- public String getCookie(String url, boolean privateBrowsing) {
- WebAddress uri;
- try {
- uri = new WebAddress(url);
- } catch (ParseException ex) {
- Log.e(LOGTAG, "Bad address: " + url);
- returnnull;
- }
- return nativeGetCookie(uri.toString(), privateBrowsing);
- }
@Override public String getCookie(String url) { return getCookie(url, false); } @Override public String getCookie(String url, boolean privateBrowsing) { WebAddress uri; try { uri = new WebAddress(url); } catch (ParseException ex) { Log.e(LOGTAG, "Bad address: " + url); return null; } return nativeGetCookie(uri.toString(), privateBrowsing); }6. nativeGetCookie定义在CookieManager.cpp里, 恩。。。。JNI。。。
- static jstring getCookie(JNIEnv* env, jobject, jstring url, jboolean privateBrowsing)
- {
- GURL gurl(jstringToStdString(env, url));
- CookieOptions options;
- options.set_include_httponly();
- std::string cookies = WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options);
- return stdStringToJstring(env, cookies);
- }
static jstring getCookie(JNIEnv* env, jobject, jstring url, jboolean privateBrowsing) { GURL gurl(jstringToStdString(env, url)); CookieOptions options; options.set_include_httponly(); std::string cookies = WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options); return stdStringToJstring(env, cookies); }7. WebCookieJar.cpp中,定义了从哪里去取得Cookie情报
- WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing)
- {
- MutexLocker lock(instanceMutex);
- if (!isFirstInstanceCreated && fileSchemeCookiesEnabled)
- net::CookieMonster::EnableFileScheme();
- isFirstInstanceCreated = true;
- scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing);
- if (!instancePtr->get())
- *instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing));
- return instancePtr->get();
- }
WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing) { MutexLocker lock(instanceMutex); if (!isFirstInstanceCreated && fileSchemeCookiesEnabled) net::CookieMonster::EnableFileScheme(); isFirstInstanceCreated = true; scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing); if (!instancePtr->get()) *instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing)); return instancePtr->get(); }
- static std::string databaseDirectory(bool isPrivateBrowsing)
- {
- staticconstchar* const kDatabaseFilename = "/webviewCookiesChromium.db";
- staticconstchar* const kDatabaseFilenamePrivateBrowsing = "/webviewCookiesChromiumPrivate.db";
- std::string databaseFilePath = databaseDirectory();
- databaseFilePath.append(isPrivateBrowsing ? kDatabaseFilenamePrivateBrowsing : kDatabaseFilename);
- return databaseFilePath;
- }
static std::string databaseDirectory(bool isPrivateBrowsing) { static const char* const kDatabaseFilename = "/webviewCookiesChromium.db"; static const char* const kDatabaseFilenamePrivateBrowsing = "/webviewCookiesChromiumPrivate.db"; std::string databaseFilePath = databaseDirectory(); databaseFilePath.append(isPrivateBrowsing ? kDatabaseFilenamePrivateBrowsing : kDatabaseFilename); return databaseFilePath; }※Android3.0以上是webviewCookiesChromium.db, 以下是WebView.db文件
8.最后读取Cookie情报发生在cookie_monster.cc。 额,最后调到C++, 去读.db文件,无敌了
- std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
- const CookieOptions& options) {
- base::AutoLock autolock(lock_);
- InitIfNecessary();
- if (!HasCookieableScheme(url)) {
- return std::string();
- }
- TimeTicks start_time(TimeTicks::Now());
- // Get the cookies for this host and its domain(s).
- std::vector<CanonicalCookie*> cookies;
- FindCookiesForHostAndDomain(url, options, true, &cookies);
- std::sort(cookies.begin(), cookies.end(), CookieSorter);
- std::string cookie_line;
- for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin();
- it != cookies.end(); ++it) {
- if (it != cookies.begin())
- cookie_line += "; ";
- // In Mozilla if you set a cookie like AAAA, it will have an empty token
- // and a value of AAAA. When it sends the cookie back, it will send AAAA,
- // so we need to avoid sending =AAAA for a blank token value.
- if (!(*it)->Name().empty())
- cookie_line += (*it)->Name() + "=";
- cookie_line += (*it)->Value();
- }
- histogram_time_get_->AddTime(TimeTicks::Now() - start_time);
- VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line;
- return cookie_line;
- }
std::string CookieMonster::GetCookiesWithOptions(const GURL& url, const CookieOptions& options) { base::AutoLock autolock(lock_); InitIfNecessary(); if (!HasCookieableScheme(url)) { return std::string(); } TimeTicks start_time(TimeTicks::Now()); // Get the cookies for this host and its domain(s). std::vector<CanonicalCookie*> cookies; FindCookiesForHostAndDomain(url, options, true, &cookies); std::sort(cookies.begin(), cookies.end(), CookieSorter); std::string cookie_line; for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); it != cookies.end(); ++it) { if (it != cookies.begin()) cookie_line += "; "; // In Mozilla if you set a cookie like AAAA, it will have an empty token // and a value of AAAA. When it sends the cookie back, it will send AAAA, // so we need to avoid sending =AAAA for a blank token value. if (!(*it)->Name().empty()) cookie_line += (*it)->Name() + "="; cookie_line += (*it)->Value(); } histogram_time_get_->AddTime(TimeTicks::Now() - start_time); VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line; return cookie_line; }#以上#
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述