类Browser.java是整个应用的Application.其代码例如以下:

public class Browser extends Application {

    @Override
public void onCreate() {
super.onCreate(); // create CookieSyncManager with current Context
CookieSyncManager.createInstance(this);
BrowserSettings.initialize(getApplicationContext());
Preloader.initialize(getApplicationContext());
} }

在Browser的创建方法onCreate方法中进行了三个操作。

1、创建了一个CookieSyncManager单例对象。CookieSyncManager用于管理存储在本地数据库的cookies。

2、初始化BrowserSettings。BrowserSettings是浏览器配置的管理单例类。

3、初始化Preloader。Preloader是处理预载入请求的单例类。

关于CookieSyncManager

我们来看看CookieSyncManager类及createInstance方法,

public final class CookieSyncManager extends WebSyncManager {

    private static CookieSyncManager sRef;

    private CookieSyncManager(Context context) {
super(context, "CookieSyncManager");
}
//获取CookieSyncManager 对象
public static synchronized CookieSyncManager getInstance() {
return sRef;
}
//创建CookieSyncManager 对象
public static synchronized CookieSyncManager createInstance(
Context context) {
if (sRef == null) {
sRef = new CookieSyncManager(context);
}
return sRef;
} protected void syncFromRamToFlash() {
CookieManager manager = CookieManager.getInstance();
if (!manager.acceptCookie()) {
return;
}
manager.flushCookieStore();
}
}

CookieSyncManager是一个final类,这里用到了单例模式。没什么好讲的。CookieSyncManager继承自WebSyncManager 类,syncFromRamToFlash是个什么方法,后面将会介绍。再来看看WebSyncManager类。

abstract class WebSyncManager implements Runnable {
// 同步消息的消息码
private static final int SYNC_MESSAGE = 101;
// 以毫秒为单位的同步消息(即时)的时延
private static int SYNC_NOW_INTERVAL = 100; // 100 毫秒
// 以毫秒为单位的同步消息(稍后)的时延
private static int SYNC_LATER_INTERVAL = 5 * 60 * 1000; // 5分钟
// 同步线程
private Thread mSyncThread;
// 线程名
private String mThreadName;
// 同步线程的处理Handler
protected Handler mHandler;
// 持久存储的数据库
protected WebViewDatabase mDataBase;
// 调用開始同步和停止同步的參考次数
private int mStartSyncRefCount; private class SyncHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == SYNC_MESSAGE) {
syncFromRamToFlash();
// 发送延时消息来请求稍后的同步,时间间隔5分钟
Message newmsg = obtainMessage(SYNC_MESSAGE);
sendMessageDelayed(newmsg, SYNC_LATER_INTERVAL);
}
}
} protected WebSyncManager(Context context, String name) {
mThreadName = name;
if (context != null) {
mDataBase = WebViewDatabase.getInstance(context);
mSyncThread = new Thread(this);
mSyncThread.setName(mThreadName);
mSyncThread.start();
} else {
//exception
}
} protected Object clone() throws CloneNotSupportedException {
//throw exception
} public void run() {
Looper.prepare(); // 为同步handler准备Looper对象
mHandler = new SyncHandler();
onSyncInit();
// 在onSyncInit() 完毕之后减少优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Message msg = mHandler.obtainMessage(SYNC_MESSAGE);
mHandler.sendMessageDelayed(msg, SYNC_LATER_INTERVAL); Looper.loop();
} public void sync() {
//...
mHandler.removeMessages(SYNC_MESSAGE);
Message msg = mHandler.obtainMessage(SYNC_MESSAGE);
mHandler.sendMessageDelayed(msg, SYNC_NOW_INTERVAL);
} public void resetSync() {
//...
mHandler.removeMessages(SYNC_MESSAGE);
Message msg = mHandler.obtainMessage(SYNC_MESSAGE);
mHandler.sendMessageDelayed(msg, SYNC_LATER_INTERVAL);
} public void startSync() {
//...
if (++mStartSyncRefCount == 1) {
Message msg = mHandler.obtainMessage(SYNC_MESSAGE);
mHandler.sendMessageDelayed(msg, SYNC_LATER_INTERVAL);
}
} public void stopSync() {
//...
if (--mStartSyncRefCount == 0) {
mHandler.removeMessages(SYNC_MESSAGE);
}
} protected void onSyncInit() {
} abstract void syncFromRamToFlash();
}

由此可见。WebSyncManager是实现了Runnable 接口的抽象类,当中的抽象方法就是上面提到的syncFromRamToFlash法,CookieSyncManager是WebSyncManager 的实现类并覆写了该方法。WebSyncManager中默认的同步时间间隔是5分钟,也就是Cookies的同步周期,WebSyncManager在创建的时候就会启动自身的线程。并依照同步周期来对cookies做同步。同步的详细实现即是syncFromRamToFlash()方法,WebSyncManager类中有下面几个重要的方法:

resetSync() 又一次同步,即清除消息队列的同步消息。又一次发送延时5分钟的延时同步消息。

startSync() 開始同步,即參考次数为0时,发送延时5分钟的延时同步消息,次数加1.

stopSync() 停止同步。即清除消息队列的同步消息

sync() 马上同步。即清除消息队列的同步消息,又一次发送延时100毫秒的延时同步消息。

我们来看看CookieSyncManager中覆写WebSyncManager 中的syncFromRamToFlash方法,这种方法也就是cookies同步的方法。同步数据从RAM到FLASH。

protected void syncFromRamToFlash() {
CookieManager manager = CookieManager.getInstance();
if (!manager.acceptCookie()) {
return;
}
manager.flushCookieStore();
}

进行了三步操作:

1、通过getInstance()获取CookieManager 对象。

2、推断CookieManager 对象能否够接受cookies。不能则返回。

3、调用CookieManager 对象的flushCookieStore()方法来实现同步。

先来看看getInstance()方法。

public static synchronized CookieManager getInstance() {
return WebViewFactory.getProvider().getCookieManager();
}

看看WebViewFactory的getProvider()方法

static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
if (sProviderInstance != null) return sProviderInstance;
//....
if (sProviderInstance == null) {
sProviderInstance = getFactoryByName(DEFAULT_WEBVIEW_FACTORY,
WebViewFactory.class.getClassLoader());
if (sProviderInstance == null) {
sProviderInstance = new WebViewClassic.Factory();
}
}
return sProviderInstance;
}
}

是通过getFactoryByName获取的,DEFAULT_WEBVIEW_FACTORY定义例如以下:

private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";

所以实现上是获取了WebViewClassic.Factory对象。

WebViewClassic.Factory对象的getCookieManager()例如以下:

public CookieManager getCookieManager() {
return CookieManagerClassic.getInstance();
}

总结一下:getInstance()得到了一个CookieManagerClassic对象。

class CookieManagerClassic extends CookieManager

CookieManagerClassic 继承自CookieManager,并覆写了CookieManager中的大部分方法。

并终于通过本地方法实现详细的操作:

比如步骤二中的acceptCookie()方法,

public synchronized boolean acceptCookie() {
return nativeAcceptCookie();
}
private static native boolean nativeAcceptCookie();

还有步骤三中flushCookieStore()方法。

protected void flushCookieStore() {
nativeFlushCookieStore();
}
private static native void nativeFlushCookieStore();

CookieManager中另一些经常使用的方法,比如:

removeSessionCookie()

getCookie()

removeAllCookie()

setCookie()

setAcceptCookie()

...

CookieManager中的方法大多会MustOverrideException异常。所以必须用一个类来继承它。正如上面的CookieManagerClassic 。

关于BrowserSettings

BrowserSettings是整个浏览器配置的管理类。先看initialize()方法。

public static void initialize(final Context context) {
sInstance = new BrowserSettings(context);
}
private BrowserSettings(Context context) {
mContext = context.getApplicationContext(); //获取应用的Context对象
//获取应用的SharedPreferences
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mAutofillHandler = new AutofillHandler(mContext);
mManagedSettings = new LinkedList<WeakReference<WebSettings>>();
mCustomUserAgents = new WeakHashMap<WebSettings, String>();
mAutofillHandler.asyncLoadFromDb();
BackgroundHandler.execute(mSetup);
}

initialize()实际上就是new了一个BrowserSettings对象。AutofillHandler是关于账户个人信息的,asyncLoadFromDb方法是异步来载入个人账户信息的,关于这部分后面会简单提到。

创建了LinkedListWeakHashMap中都用到了WebSettings。看看WebSettings是什么吧。

WebSettings在package android.webkit下,是一个抽象类。它用于管理WebView的配置状态。当一个WebView第一次被创建时,将会获得默认的配置集合,默认的配置将会通过调用随意的getter方法返回。通过WebView.getSettings()获取的WebSettings与WebView的生存周期结合在一起。假设一个WebView被销毁了。不论什么关于WebSettings的方法将会抛出IllegalStateException异常。

WebSettings中的方法大多会MustOverrideException异常,所以必须用一个类来继承它。framework中是用WebSettingsClassic 继承它的。

关于这点后面再讲。

public class WebSettingsClassic extends WebSettings

这个LinkedList是在方法startManagingSettings(WebSettings settings) 加入条目的,在stopManagingSettings(WebSettings settings)中删除条目,在syncManagedSettings()中同步每个WebSettings。

这个WeakHashMap 是与用户代理相关的,通过WebSettings的setUserAgentString()来为WebView设置代理。

再看这句:BackgroundHandler.execute(mSetup);

BackgroundHandler的代码例如以下:

public class BackgroundHandler {

    static HandlerThread sLooperThread;
static ExecutorService mThreadPool; static {
sLooperThread = new HandlerThread("BackgroundHandler", HandlerThread.MIN_PRIORITY);
sLooperThread.start();
mThreadPool = Executors.newCachedThreadPool();
} public static void execute(Runnable runnable) {
mThreadPool.execute(runnable);
} public static Looper getLooper() {
return sLooperThread.getLooper();
} private BackgroundHandler() {}
}

整个BackgroundHandler 能够看成两个部分。一个线程池ExecutorService 对象,一个HandlerThread 对象。

所以它的作用主要是两个:

1、利用线程池ExecutorService 对象来运行线程Runnable对象。

比如:BackgroundHandler.execute(mSetup); //mSetup是一个Runnable对象

2、利用HandlerThread 来获取Looper对象,用于创建接收在其它线程中发送的消息的Handler对象。

比如:

Handler mForegroundHandler = new Handler();
Handler mBackgroundHandler = new Handler(BackgroundHandler.getLooper()) {
@Override
public void handleMessage(Message msg) { }
};
private Runnable mCreateState = new Runnable() {
@Override
public void run() {
Message.obtain(mBackgroundHandler, what, obj).sendToTarget();
}
};

知道了BackgroundHandler ,就知道了BackgroundHandler.execute(mSetup);就是在线程池中运行了mSetup这个Runnable对象。

看看mSetup的定义:

private Runnable mSetup = new Runnable() {

        @Override
public void run() {
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
mFontSizeMult = metrics.scaledDensity / metrics.density; if (ActivityManager.staticGetMemoryClass() > 64) {
mPageCacheCapacity = 5;
}
mWebStorageSizeManager = new WebStorageSizeManager(mContext,
new WebStorageSizeManager.StatFsDiskInfo(getAppCachePath()),
new WebStorageSizeManager.WebKitAppCacheInfo(getAppCachePath()));
mPrefs.registerOnSharedPreferenceChangeListener(BrowserSettings.this);
//...
sFactoryResetUrl = mContext.getResources().getString(R.string.homepage_base);
//...
synchronized (BrowserSettings.class) {
sInitialized = true;
BrowserSettings.class.notifyAll();
}
}
};

主要做了例如以下几件事:

1、获取字体缩放因子(metrics.scaledDensity(字体缩放比例)/metrics.density(显示密度))

2、依据ActivityManager.staticGetMemoryClass()的值设置缓存页面的数量,默认是1,看看staticGetMemoryClass()

    public static int staticGetMemoryClass() {
// Really brain dead right now -- just take this from the configured
// vm heap size, and assume it is in megabytes and thus ends with "m".
String vmHeapSize = SystemProperties.get("dalvik.vm.heapgrowthlimit", "");
if (vmHeapSize != null && !"".equals(vmHeapSize)) {
return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1));
}
return staticGetLargeMemoryClass();
}

能够看出虚拟机堆的大小是从"dalvik.vm.heapgrowthlimit"读出来的,上面的程序段显示假设堆的大小大于64M,则将缓存页面的值设为5。否则默觉得1,这样能够避免OOM。

3、创建一个WebStorageSizeManager对象。用以管理缓存的磁盘空间

4、为应用的SharedPreference注冊改变的监听器,里面的配置值改变将会实现同步设置操作

 	 @Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
syncManagedSettings();
}

5、获取浏览器的主页URL的路径sFactoryResetUrl

6、通知大家初始化完成。

再来看看AutofillHandler ,Google用来实现表单自己主动填充功能的。Android浏览器的自己主动填充条目有:Full name、Company name、Address、Zip code、Country、Phone、Email等。AutoFillProfileDatabase 类是用来对这些条目进行存储的数据库操作类。

数据将被存放在autofill.db 数据库中。

使用自己主动填充功能须要注意下面几个方面:

1、由于涉及到隐私,须要在浏览器的设置中开启Form Auto-fill选项;

2、用户须要比較勤快,事先要将上面的个人信息录入;

3、须要站点的支持,这些信息怎样和表单上的字段相应,这就是RFC 3106所定义的,表单控件的命名须要遵守规范。国内的站点。包含京东、亚马逊中国、当当、淘宝等均不支持。

4、某些站点可能会试图捕获隐藏字段或难以发现的字段中的信息。因此,请勿在您不信任的站点上使用自己主动填充功能。

5、某些站点会阻止浏览器保存您输入的内容。因此,无法在这些站点上填写表单。

由上,表单自己主动填充功能基本上在国内是用不上的。

我们还是简单的看一看它的实现,前面看到它的异步载入方法asyncLoadFromDb() .

public void asyncLoadFromDb() {
new LoadFromDb().start();
}

启用了一个线程。

private class LoadFromDb extends Thread {

        @Override
public void run() {
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(mContext); // 从SharedPreferences中读出近期使用的自己主动填充条目的ID.
mAutoFillActiveProfileId = p.getInt(
PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID,
mAutoFillActiveProfileId);
//获取存储数据的数据库操作管理对象
AutoFillProfileDatabase autoFillDb = AutoFillProfileDatabase.getInstance(mContext);
Cursor c = autoFillDb.getProfile(mAutoFillActiveProfileId); if (c.getCount() > 0) {
c.moveToFirst(); String fullName = c.getString(c.getColumnIndex(
AutoFillProfileDatabase.Profiles.FULL_NAME));
//从cursor中获取全部的字段的值
...
mAutoFillProfile = new AutoFillProfile(mAutoFillActiveProfileId,
fullName, email, company, addressLine1, addressLine2, city,
state, zip, country, phone);
}
c.close();
autoFillDb.close();
mLoaded.countDown();
//假设没有值。从联系人数据库中取值
if (mAutoFillProfile == null) {
final Uri profileUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
String name = getContactField(profileUri,
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
if (name != null) {
String email = getContactField(profileUri,
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
... synchronized(AutofillHandler.this) {
if (mAutoFillProfile == null) {
setAutoFillProfile(new AutoFillProfile(1, name, email, company,
null, null, null, null, null, null, phone), null);
}
}
}
}
}

详细的操作就不用多说了,就是简单的数据库操作。我们看到一个mLoaded 。定义例如以下

private CountDownLatch mLoaded = new CountDownLatch(1);

CountDownLatch 主要用于在完毕一组正在其它线程中运行的操作之前,它同意一个或多个线程一直等待。

主要方法

public CountDownLatch(int count);

public void countDown();

public void await() throws InterruptedException

--CountDownLatch(int count)构造方法传了指定计次的数目。

--countDown方法,当前线程调用此方法,则计数减一

--await方法,调用此方法会一直堵塞当前线程,直到计时器的值为0

程序中计次数目为1。此处调用countDown()是为了减1来唤醒获取AutoFillProfile之前堵塞的线程mSetup 。

为什么是mSetup ?由于在BrowserSettings的构造方法中有例如以下的代码。

private BrowserSettings(Context context) {
...
mAutofillHandler.asyncLoadFromDb();
BackgroundHandler.execute(mSetup);
}

BackgroundHandler.execute(mSetup)是在另外的线程中运行的操作。

mSetup中注冊了SharedPreference的监听器。在onSharedPreferenceChanged中依次调用syncManagedSettings()-->syncSetting(settings)--> settings.setAutoFillProfile(getAutoFillProfile());

getAutoFillProfile()是调用mAutofillHandler 的getAutoFillProfile()

 public AutoFillProfile getAutoFillProfile() {
return mAutofillHandler.getAutoFillProfile();
}

mAutofillHandler 的getAutoFillProfile()定义例如以下:

public synchronized AutoFillProfile getAutoFillProfile() {
waitForLoad();
return mAutoFillProfile;
}

再看waitForLoad()方法:

private void waitForLoad() {
try {
mLoaded.await();
} catch (InterruptedException e) {
Log.w(LOGTAG, "...");
}
}

这里用到了CountDownLatch 的await()方法来是线程堵塞,整个这一段的逻辑就是在构造BrowserSettings是在主线程载入表单自己主动填充,同步浏览器数据的操作是另开的线程中实现的,但有一个值须要主线程的操作完毕后才干获取,没有值的时候就会堵塞在那里,主线程操作结束获得值之后就会唤醒这个另开的线程,完毕值的存储工作。我用下图来表示。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYW5kcm9pZF9oYXNlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

关于Preloader

Preloader的initialize 方法也仅仅是创建了一个Preloader对象。没有进行其它的操作。

Application是整个应用的入口,一般完毕一些初始化的操作,到这里就简单分析了一下。

android原生browser分析(一)--Application的更多相关文章

  1. [Android Pro] android 4.4 Android原生权限管理:AppOps

    reference : http://m.blog.csdn.net/blog/langzxz/45308199 reference : http://blog.csdn.net/hyhyl1990/ ...

  2. android 原生camera——设置模块修改

    , 此篇博客是记一次客户需求修改,从上周五到现在正好一周时间,期间的各种酸爽,就不说了,还是来看大家关注的技术问题吧. 首先看下以前效果和修改后的效果: 修改前:修改后: 不知道有没有看明白,我在简单 ...

  3. Android源码分析-全面理解Context

    前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...

  4. 【android原生应用】之闹钟应用搭起篇

    由于工作原因接触android开发一段时间了,对于开发有了一些了解,于是萌生了搭起android原生应用进行分析和学习的想法.先从闹钟应用开始吧. 1.首先要下载原生应用,原生应用在原生系统里面(当然 ...

  5. Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  6. Android原生跳转React不同页面(undefined is not an object)

    继续上篇文章的demo,由于现在的项目是原生的,打算用部分页面试下react native,那么问题来了:react貌似只有一个入口 index.android.js,那么在不同的原生页面需要跳转到不 ...

  7. Android原生(Native)C开发之四:SDL移植笔记

    http://www.apkbus.com/forum.php?mod=viewthread&tid=1989 SDL(Simple DirectMedia Layer)是一套开放源码的跨平台 ...

  8. Android原生和H5交互;Android和H5混合开发;WebView点击H5界面跳转到Android原生界面。

    当时业务的需求是这样的,H5有一个活动商品列表的界面,IOS和Android共用这一个界面,点击商品可以跳转到Android原生的商品详情界面并传递商品ID:  大概就是点击H5界面跳转到Androi ...

  9. Android HttpURLConnection源代码分析

    Android HttpURLConnection源代码分析 之前写过HttpURLConnection与HttpClient的差别及选择.后来又分析了Volley的源代码. 近期又遇到了问题,想在V ...

随机推荐

  1. Hibernate JPA 中配置Ehcache二级缓存

    在Hibernate3 JPA里配置了一下非分布式环境的二级缓存,效果不错.具体过程如下: 1, 需要引入的jar包 http://ehcache.org/downloads/catalog 下载的包 ...

  2. CentOS 添加常用 yum 源

    CentOS 的官方源去掉了一些与版权有关的软件,因此想要安装这些软件或者手动下载安装,或者使用其他源. 下面我推荐常用的两个源, 这两个源基本可以满足一般服务器的使用需求. 首先, 添加源之前要确定 ...

  3. hdu 4736 This Is The Job The Bear Finds(2013年成都ACM网络赛)

    // Time 1718 ms; Memory 1500 K #include<iostream> #include<cstdio> #include<cmath> ...

  4. YouTube为什么打不开?以及简便的訪问的方法/解决方式!

    在站点统计中看到好多人通过百度或者谷歌(Google)搜索引擎搜:YouTube怎么上不去,怎样不用代理server訪问YouTube,YouTube上不去的解决的方法,YouTube怎样訪问等等.事 ...

  5. Jsp分页实例---假分页

    今天总结一个JSP假分页的实例,由基本功能由js实现. 相较前一篇真分页中程序的功能,丰富了一些.具备首页尾页,和页面跳转功能. 首先还是来总结一下真假分页的优缺点和特性吧. 假分页:从数据库中取出所 ...

  6. 统一横轴墨卡托投影(UTM)

    UTM 坐标系统使用基于网格的方法表示坐标.UTM 系统将地球分为 60 个区,每一个区基于横轴墨卡托投影.画图法中的地图投影方法能够在平面中表示一个两维的曲面,比如一个标准地图.图 1 展示了一个横 ...

  7. Deamon Thread 讲解

    The daemon thread's life cycle is same with the life cycle of the application which starts this daem ...

  8. java 随机数的生成

    生成10个不小于100000的6位数 public static void main(String[] args) { Random random = new Random(); for (int i ...

  9. ADO.NET 1创建连接、执行命令

    一无参构造函数的形式: 创建连接.创建命令.执行命令: string connstr = @"server=.;database=TestDataBase;uid=sa;pwd=130988 ...

  10. Java7新特性(一)Coin

    1.语法糖 数字下划线   2.switch语句中的String   3.multicatch   4.final重抛 对比上份代码   5.try-with-resources(TWR) AutoC ...