Chromium提供了一种Extension机制,用来增强浏览器功能。我们可以将Extension看作是一种运行在Chromium中的应用。这种应用的开发语言是JavaScript,并且UI通过HTML描述。通过使用Chromium提供的API,Extension可以访问网络,修改浏览器行为,以及操作网页的内容等。本文接下来对Chromium的Extension机制进行简要介绍,以及制定学习计划。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

在Chrome(基于Chromium实现,以下我们将交替使用Chrome和Chromium)的地址栏中输入"chrome://extensions",就可以看到当前安装的所有Extension,如图1所示:

图1 Chrome中的Extension列表

图1显示了两个Extension。一个是基于Browser Action实现的,另一个是基于Page Action实现的。它们在地址栏的右边分别对应有一个Button。点击这两个Button,可以弹出一个窗口。这一点后面我们会看到。

接下来,我们就通过上述两个Extension例子,对Chromium的Extension机制进行介绍。

每一个Extension都包含有一个清单文件manifest.json,类似于Android应用程序的AndroidManifest.xml文件。前者是json格式的,后者是xml格式的。清单文件描述了Extension的内容。以图1所示的Browser action example为例,它的manifest.json如下所示:

{"manifest_version": 2,"name": "Browser action example","description": "This extension show a image and changes a web page's background","version": "1.0","browser_action": {"default_icon": "icon.png","default_popup": "popup.html"},"content_scripts": [{"matches": ["http://*/*"],"js": ["content.js"],"run_at": "document_start","all_frames": true}]
}

前面几行是一些描述性信息。后面的browser_action和content_scripts指定了一个Browser Action和一个Content Script。

Browser Action对在浏览器中加载的所有网页都生效。后面我们可以看到,Extension还有一种Page Action,它针对特定的网页生效。一个Extension最多可以有一个Browser Action或者Page Action。不管是Browser Action,还是Page Action,都可以指定一个icon文件和一个popup文件。前者是一个图片,后者是一个html文件。指定的icon将会以Button的形式展现在地址栏的右边。当这个Button被点击的时候,就会弹出一个窗口,窗口会加载在清单文件中指定的popup.html文件。注意,这里指定的文件路径都是相对路径,相对Extension的根目录的。

上述popup.html的内容如下所示:

<html><head><title>Getting Started Extension's Popup</title><style>body {font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;font-size: 100%;}#status {/* avoid an excessively wide status text */white-space: pre;text-overflow: ellipsis;overflow: hidden;max-width: 400px;}</style><!--- JavaScript and HTML must be in separate files: see our Content Security- Policy documentation[1] for details and explanation.-- [1]: https://developer.chrome.com/extensions/contentSecurityPolicy--><script src="popup.js"></script></head><body><div id="status"></div><img id="image-result" hidden></body>
</html>

这个html文件的内容很简单,由以下内容组成:

1. 一个popup.js脚本

2. 一个div标签

3. 一个img标签

其中,div标签用来显示状态信息,img标签用来显示图片。它们的内容都是popup.js指定的,如下所示:

function getImageUrl(callback, errorCallback) {callback("https://images-cn-8.ssl-images-amazon.com/images/I/61vnPRDVoeL.jpg", 200, 250);
}function renderStatus(statusText) {document.getElementById('status').textContent = statusText;
}document.addEventListener('DOMContentLoaded', function() {getImageUrl(function(imageUrl, width, height) {var imageResult = document.getElementById('image-result');imageResult.width = width;imageResult.height = height;imageResult.src = imageUrl;imageResult.hidden = false;}, function(errorMessage) {renderStatus('Cannot display image. ' + errorMessage);});
});

这个popup.js所做的事情非常简单,就是为popup.html中的img标签指定一个src。安装了这个Entension之后,就可以在浏览器地址栏的右边出现一个Button。点击这个Button,就可以弹出一个窗口,它的内容如图2所示:

图2 Browser Action的popup.html

Browser action example的清单文件还指定了一个Content Script,即content.js。这个content.js通过matches字段指定对任何网页生效。这里所说的生效,是指将content.js注入到网页中去执行的。

上述content.js的内容如下所示:

document.body.style.backgroundColor="red"

它做了一件非常简单的事情,就是将网页的背景设置为红色。这件事情虽然非常简单,但是它告诉了我们,Extension通过Content Script可以操作在Chromium中加载的任何一个网页的内容!

我们再来看另一个Page action example。它的清单文件如下所示:

{"manifest_version": 2,"name": "Page action example","description": "This extension show a image and changes a web page's background","version": "1.0","background": {"scripts": ["background.js"]},"page_action": {"default_icon": "icon.png", "default_popup": "popup.html" },"permissions": ["tabs"],"content_scripts": [{"matches": ["https://fast.com/"],"js": ["content.js"],"run_at": "document_start","all_frames": true}]
}

这个清单文件没有指定Browser Action,但是指定了Page Action,以及Content Script和Background。Content Script和Background描述的都是一个JavaScript脚本,前者称为content.js,后者称为background.js。

Page Action与Browser Action类似,它也对应有一个icon和一个popup。不过,Page Action是有状态的,分为显示和隐藏两种。为显示状态时,它在地址栏右边的Button变亮,并且可以点击。为隐藏状态时,它在地址栏右边的Button变灰,并且不可以点击。

我们可以通过Chromium提供的Page Action API(chrome.pageAction.show和chrome.pageAction.hide)显示或者隐藏Page Action。一般就是根据当前打开的网页决定要显示还是隐藏一个Page Action。这个判断逻辑一般就是实现在background.js中,如下所示:

chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { if (tab.url.indexOf("fast.com") >= 0) {chrome.pageAction.show(tabId);  }var views = chrome.extension.getViews({type: "tab"});if (views.length > 0) {console.log(views[0].whoiam);} else {console.log("No tab");}
});  chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {sendResponse({counter: request.counter + 1 }); }
);var whoiam = "background.html"

这个background.js通过chrome.tabs.onUpdated.addListener这个API指定了一个Listener。每当我们切换到浏览器中的Tab时,与这个Listener绑定的函数就会被调用。这个函数是一个匿名函数,它做了两件事情。

第一件事情是判断当前激活的Tab打开的网页的URL是否包含了"fast.com"关键字。如果包含了,那么就通过chrome.pageAction.show这个API将清单文件中指定的Page Action显示出来。

第二件事情是调用chrome.extension.getViews这个API检查当前的Extension是否有页面在浏览器的Tab中打开。如果有打开,那么就会获得这些页面的window对象。一旦获得了一个页面window对象,我们就可以访问它的成员。例如,定义在页面中的函数和变量。这实际上是提供了一种方式,使得Extension的不同页面可以相互通信。这里我们就是访问了一个whoiam变量,并且将它的内容打印出来。

关于Extension页面,及其通信方式,我们接下来还会进一步解释。

上述background.js还通过chrome.runtime.onMessage.addListener这个API定义了一个Listener以及一个whoiam变量。其中,Listener用来接收其它页面给它发送的消息,变量whoiam可以被其它页面直接访问。

当Page Action通过chrome.pageAction.show这个API被设置为显示状态时,我们就可以点击它在地址栏右边的按钮,弹出一个窗口。这个窗口在清单文件中指定加载的网页为popup.html,它的内容如下所示:

<html><head><title>Getting Started Extension's Popup</title><style>body {font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;font-size: 100%;}#status {/* avoid an excessively wide status text */white-space: pre;text-overflow: ellipsis;overflow: hidden;max-width: 400px;}</style><!--- JavaScript and HTML must be in separate files: see our Content Security- Policy documentation[1] for details and explanation.-- [1]: https://developer.chrome.com/extensions/contentSecurityPolicy--><script src="popup.js"></script></head><body><table align='center'>  <tr>  <td><button id="testRequest">send 0 to tab page</button></td>  <td id="resultsRequest"><font color="gray">response: null</font></td>  </tr>   </table>  <div id="status"></div><img id="image-result" hidden></body>
</html>

与前面Browser action example中的popup.html一样,这个popup.html也包含了一个popup.js,一个div标签和一个img标签。不过,这个popup.html还多了一个table标签。这个table包含了一行两列。其中一列用来显示一个Button,另一列用来显示文本。

上述popup.html显示出来的效果如图3所示:

图3 Page Action的popup.html

这个popup.html显示的图片是通过它包含的popup.js指定的,如下所示:

function getImageUrl(callback, errorCallback) {callback("http://avatar.csdn.net/5/6/E/1_luoshengyang.jpg", 200, 200);
}function renderStatus(statusText) {document.getElementById('status').textContent = statusText;
}var counter = 0;function testRequest() {  chrome.tabs.getSelected(null, function(tab) {   chrome.tabs.sendRequest(tab.id, {counter: counter}, function handler(response) {  counter = response.counter;document.querySelector('#resultsRequest').innerHTML = "<font color='gray'> response: " + counter + "</font>";document.querySelector('#testRequest').innerText = "send " + (counter -1) + " to tab page";});  });
}  document.addEventListener('DOMContentLoaded', function() {getImageUrl(function(imageUrl, width, height) {var imageResult = document.getElementById('image-result');imageResult.width = width;imageResult.height = height;imageResult.src = imageUrl;imageResult.hidden = false;console.log(chrome.extension.getBackgroundPage().whoiam);}, function(errorMessage) {renderStatus('Cannot display image. ' + errorMessage);});document.querySelector('#testRequest').addEventListener(  'click', testRequest);
});var whoiam = "popup.html"

这个popup.js是在页面的文档加载完成时指定显示的图片的。同时,它还会在页面的文档加载完成时,做另外两件事情。

第一件事情是调用chrome.extension.getBackgroundPage这个API获得当前Extension的Background页面的window对象,并且通过这个window对象访问定义在Background页面中的whoiam变量。前面我们在background.js定义了一个whoiam变量,因此这里就可以对它进行访问。

第二件事情为popup.html页面中显示为"send to tab page"的Button指定一个Listener。这个Listener用来监听Button的Click事件。一旦监听到Click事件发生,函数testRequest就会被调用。

函数testRequest首先通过chrome.tabs.getSelected这个API获得当前激活的Tab,接着又通过chrome.tabs.sendRequest这个API向获得的当前激活的Tab发送一个消息。消息是json格式的,传递一个由变量counter描述的计数给接收者。接收者接收到这个消息之后, 会将它封装的计数取出来,并且增加1,然后再将结果返回来。返回来的结果保存在变量counter的同时,也会显示在popup.html页面中id为resultsRequests的td标签中。

这样,通过不断地点击popup.html页面中显示为"send to tab page"的Button,就可以不断地与当前激活的Tab通信。后面我们可以看到,实际上是与在Tab中加载的Content Script通信的。

从popup.js的内容还可以看到,它定义了一个变量whoiam。这个变量可以被其它Extension页面直接访问。

前面提到,Page action example的清单文件还指定了一个Content Script,即content.js。这个content.js通过matches字段指定仅对URL为“https://fast.com/”的页面生效,也就是它只会注入到URL为“https://fast.com/”的页面中去。注入的内容如下所示:

document.body.innerHTML = "<table align='center'>\<tr>\<td><button id='testRequest'>send 0 to background page</button></td>\<td id='resultsRequest'>response: null</td>\</tr>\</table>" +document.body.innerHTML;chrome.extension.onRequest.addListener(  function(request, sender, sendResponse) {  sendResponse({counter: request.counter + 1 });  }
);var counter = 0;function testRequest() {  chrome.runtime.sendMessage({counter: counter}, function(response) {counter = response.counter;document.querySelector('#resultsRequest').innerText = "response: " + counter;document.querySelector('#testRequest').innerText = "send " + (counter -1) + " to background page";});
}document.querySelector('#testRequest').addEventListener(  'click', testRequest);

它注入了一个table。这个table包含一行两列。其中一个列包含了一个显示为“send 0 to background page”的Button,另外一个列用来显示文本。

注入的效果如图4所示:

图4 Content Script

当我们点击显示为“send 0 to background page”的Button时,定义在content.js中的函数testRequest就会被调用。函数testRequest用来向当前的Extension的Background页面发送消息。这个消息传递一个由变量counter描述的计数给Background页面。

Background页面在background.js中通过chrome.runtime.onMessage.addListener这个API定义了一个Listener。这个Listener用来接收上述由content.js发送过来的消息。接收到该消息后,background.js会将其封装的计数取出,并且增加1,然后将结果返回给content.js显示。

此外,content.js还通过chrome.extension.onRequest.addListener这个API定义了一个Listener。这个Listener用来监听其它页面发送过来的消息。也就是前面提到的从popup.html中发送过来的消息。

分析到这里,我们小结一下,Page action example这个Extension所包含的内容:

1. popup.html:显示在一个弹出窗口中。

2. background.html: 这个页面是由Chromium根据background.js生成出来的,没有显示在窗口中,因此称为Background页面。

3. content.js:一个注入在宿主页面中的JavaScript。

其中,前两个属于页面,后面一个属于脚本。事实上,一个Extension可以同时拥有若干个页面。这些页面分为五种类型为:background、popup、tab、infobar、notification。它们分别代表在不同窗口打开的页面。其中,前面两种我们已经描述过了,后面三种也比较容易理解:

1. tab:像正常网页一样在浏览器的Tab中打开的页面。

2. infobar: 在浏览器顶部信息栏显示的信息页面。

3. notification:在浏览器底部显示的通知页面。

每一个页面都有一个URL。URL的规范为:chrome-extension://[extension-id]/path。其中,协议部分为chrome-extension,extension-id是Chromium为Extension分配的ID,path是页面的路径。

从图1可以看到,Chrome为Page action example分配的Extension ID为“abcemahgedfccgcmlkaeiabpjjjhhmoc”。按照上述规范,图3显示的popup页面的URL就为“chrome-extension://abcemahgedfccgcmlkaeiabpjjjhhmoc/popup.html”。这个URL可以直接输入到Chrome的地址栏中去,使得popup.html也可以显示在一个Tab中。

Page action example还包含了另外一个tab.html文件,它的内容如下所示:

<html><head><title>Getting Started Extension's Tab</title></head><body><table align='center' height="100%"><tr><td><img src="sample.png" /></td></tr></table></body>
</html>

这是一个简单的html页来,用来显示打包在Extension中的一个图片sample.png。

我们可以在浏览器的地址栏中输入“chrome-extension://abcemahgedfccgcmlkaeiabpjjjhhmoc/tab.html”访问这个页面,效果如图5所示:

图5 Open extension page in tab

这样,我们就可以归纳出一个Extension是由Extension Page和Content Script构成的。

Extension Page与普通的网页一样,具有一个URL。常规的Extension Page有popup.html、background.html。其中,popup.html显示在一个弹出窗口中,background.html运行在后台,没有显示在窗口中。给出一个Extension Page的URL,我们也可以在浏览器的Tab中打开它。

Content Script是一个JavaScript脚本,它会注入到宿主网页去执行,从页可以访问它的DOM Tree。这里说的宿主网页,就是在浏览器中加载的网页。这些网页是由网站提供的,不属于Extension的一部分。

Extension Page之间,以及Extension Page与Content Script之间,是可以相互通信的。正是因为它们可以相互通信,才能形成一个整体,共同去完成一个Extension的功能。其中,background.js所在的background.html扮演着一个中心角色。它在Extension加载的时候就会默默加载,并且一直运行在后台。其它的Extension Page或者Content Script都是按需加载的。因此,我们就可以将Extension的状态维护在background.html中,其它的Extension Page或者Content Script需要的时候,可以与它进行通信。

那么,Extension Page之间,以及Extension Page与Content Script之间,是如何通信的呢?我们通过图6说明,如下所示:

图6 Extension Page与Content Script之间的通信方式

同一个Extension的Extension Page都是运行在同一个进程中的。这个进程称为Extension进程,这实际上也是一个Render进程。在同一个进程打开的网页可以在JavaScript中直接获得对方的window对象。有了一个网页的window对象,就可以调它里面定义的函数或者变量,相当于就是可以与它进行通信。

Chromium的Extension机制提供了两个API:chrome.extension.getViews和chrome.extension.getBackgroundPage。其中,通过前者可以获得Background Page之外的Extension Page的window对象,而通过后者可以获得的Background Page的window对象。chrome.extension.getViews这个API在调用的时候,还可以指定一个type参数,用来指定要获取哪一种类型的Extension Page的window对象。如果没有指定,则获取Background Page之外的所有Extension Page的window对象。

Extension的Content Script由于是注入在其它网页中运行,因此它们不能与Extension Page进行直接通信,而是要进行跨进程通信。又由于Content Script和Extension Page是相互不知道对方的,因此它们在进行跨进程通信的时候,需要有一个桥梁。这个桥梁就是Browser进程。

Chromium的Extension机制提供了两个API:chrome.tabs.sendRequest和chome.extension.onRequest,用来从Extension Page向Content Script发送消息。同样,Chromium的Extension机制也为从Content Script向Extension Page发送消息提供了两个API:chrome.runtime.sendMessage和chrome.runtime.onMessage。它们的实现原理是一样的。当chrome.tabs.sendRequest或者chrome.runtime.sendMessage被调用的时候,它们会向Browser进程发送一个类型为ExtensionHostMsg_PostMessage的IPC消息。Browser进程接收到这个消息之后,又会向目标进程发送一个类型为ExtensionMsg_DeliverMessage的IPC消息。目标进程接收到这个消息之后,再通过JavaScript引擎分发给chome.extension.onRequest或者chrome.runtime.onMessage处理。

了解了Extension Page之间,以及Extension Page与Content Script之间的通信机制之后,我们再来看Extension Page和Content Script的加载过程。

Chromium的Browser进程在启动的时候,会创建一个Startup Task。这个Startup Task会初始化一个Extension Service,如图7所示:

图7  Extension加载过程

Extension Service在初始化的过程中,会通过一个Installed Loader加载当前用户安装的所有设置为Enabled的Extension。这些Extension形成一个列表,保存在一个Extension Registry中。以后通过这个Extension Registry,就可以获得当前启用的所有Extension的信息。

如果一个Extension指定了Background Page,那么Browser进程在初始化好浏览器窗口之后,还会自动加载它指定的Background Page,如图8所示:

图8 Background Page和Popup Page的加载过程

Browser进程初始化好浏览器窗口之后,会发送一个OnBrowserWindowReady通知。这个通知会触发Browser进程创建一个ExtensionHost对象。这个ExtensionHost对象又会通过WebContents类的静态成员函数Create加载指定的Background Page。WebContents类是Chromium的Content层向外提供的一个API。通过这个API,就可以使用Chromium来加载一个指定的网页了。

注意,Background Page会加载在一个Extension进程中。如果这个Extension进程还没有创建,那么WebContents类的静态成员函数Create会先创建它。以后Browser进程如果要与这个Background Page进行通信,那么就会通过上述创建的ExtensionHost对象进行。从这里我们也可以看到,每一个Background Page在Browser进程中都有一个对应的ExtensionHost对象,就类似于普通的网页在Browser进程中都有一个对应的RenderProcessHostImpl对象一样。这一点可以参考前面Chromium多进程架构简要介绍和学习计划这个系列的文章。这是很好理解的,因为Extension进程本质上也是一个Render进程。

其它类型的Extension Page,它们则是按需加载的。例如,对于Popup Page来说,当用户点击了它在地址栏右边对应的Button的时候,Browser进程才会加载它们。它们的加载过程与上述的Background Page是类似的,即先创建一个ExtensionHost对象,然后再通过WebContents类的静态成员函数Create进行加载。

最后,我们再来看Content Script的加载过程,如图9所示:

图9 Content Script的加载过程

Content Script的加载过程由三个流程组成。

第一个流程发生在Extension加载过程中,也就是图7所示的Extension加载流程。Browser进程在启动的时候,会创建一个UserScriptMaster对象,用来监听所有的Extension的加载事件。如果当前被加载的Extension指定了Content Script,那么指定的Content Script的内容就会保存在上述UserScriptMaster对象中。

第二个流程发生在Content Script的宿主网页所对应的Render进程的启动过程中。当这个Render进程启动完成时,Browser进程会获得一个OnProcessLaunched通知。这个通知直接分发给代表该Render进程的一个RenderProcessHostImpl对象处理。这个RenderProcessHostImpl对象首先会到上述的UserScriptMaster对象中收集要在宿主网页中加载的Content Script,然后再通过一个类型为ExtensionMsg_UpdateUserScript的IPC消息将这些Content Script发送给宿主网页所在的Render进程。这个Render进程会通过一个Dispatcher对象接收该IPC消息,并且将发送过来的Content Script保存在一个UserScriptSlave对象中。

第三个流程发生在Content Script的宿主网页的加载过程中。Content Script可以在Extension的清单文件中指定加载时机。有三个时机可以指定:document_start、document_end和document_idle,分别表示在宿主网页的Document对象开始创建、结束创建以及空闲时加载。接下来,我们假设Content Script指定了在document_start时加载。

从前面Chromium网页DOM Tree创建过程分析一文可以知道,WebKit在加载的一个网页的时候,首先会为它创建一个Document对象。在创建这个Document对象的时候,WebKit会通过Chromium中的Content层,也就是调用一个RenderFrameImpl对象的成员函数didCreateDocumentElement。这个RenderFrameImpl对象是在Content层中描述的一个在当前Render进程中加载的网页的。

RenderFrameImpl类的成员函数didCreateDocumentElement在执行的过程中,会到上述的UserScriptSlave对象收集要在当前加载的网页中加载的Content Script。这些Content Script又会进一步交给JavaScript引擎在一个Isolated World中执行。Content Script在Isolated World中执行,意味着它们是被隔离执行的,也就是它们不能访问在宿主网页中定义的JavaScript函数和变量。

RenderFrameImpl类的成员函数didCreateDocumentElement是如何将Content Script交给JavaScript引擎执行的呢?从前面Chromium网页Frame Tree创建过程分析一文可以知道,Chromium的Content层的每一个RenderFrameImpl对象在WebKit中都对应有一个WebLocalFrameImpl对象,并且该WebLocalFrameImpl对象会保存在它对应的RenderFrameImpl对象的内部。这个WebLocalFrameImpl对象可以看作是WebKit向Chromium的Content层提供的一个API接口。通过调用这个WebLocalFrameImpl对象的成员函数executeScriptInIsolatedWorld,就可以将指定的Content Script交给JavaScript引擎执行了。

这样,我们就通过两个Extension例子,对Extension机制涉及到的基本概念进行了介绍。为了更进一步理解Extension机制,接下来我们将结合源码,按照以下四个情景对Extension机制进行分析:

1. Extension的加载过程;

2. Extension Page的加载过程;

3. Content Script的加载过程;

4. Extension Page之间,以及Extension Page与Content Script之间的通信过程。

注意,这些文章的侧重点是分析Extension机制的实现,而不是Extension的开发。Extension的开发,可以参考官方文档:http://code.google.com/chrome/extensions/getstarted.html。

另外,除了Extension机制,Chromium还提供了Plugin机制,用来增强浏览器的功能。Extension和Plugin是两种不同的机制。从狭义上讲,Plugin仅仅是用来增加网页的功能,而Extension不仅能用来增加网页的功能,也能增强浏览器本身功能。而且,两者的开发方式(开发语言和API接口)也是完全不一样的。为了更好地理解两者的区别,后面我们将会用另外一个系列的文章分析Chromium的Plugin机制。

敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。

Chromium扩展(Extension)机制简要介绍和学习计划相关推荐

  1. Chromium视频标签video简要介绍和学习计划

    随着互联网的发展,在网页上观看视频变得越来越流行,尤其是泛娱乐(手机直播)大行其道的今天.在HTML5之前,在网页上播放视频需要插件支持,例如Flash插件.有了HTML5之后,标签<video ...

  2. 老郭的《Dalvik虚拟机垃圾收集机制简要介绍和学习计划》

    伴随着"Dalvik is dead,long live Dalvik"这行AOSP代码提交日志,在Android5.0中,ART运行时取代了Dalvik虚拟机.虽然Dalvik虚 ...

  3. Chromium插件(Plugin)机制简要介绍和学习计划

    在Chromium中,除了可以使用Extension增强浏览器功能,还可以使用Plugin.两者最大区别是前者用JS开发,后者用C/C++开发.这意味着Plugin以Native Code运行,在性能 ...

  4. Android WebView简要介绍和学习计划

    我们通常会在App的UI中嵌入WebView,用来实现某些功能的动态更新.在4.4版本之前,Android WebView基于WebKit实现.不过,在4.4版本之后,Android WebView就 ...

  5. Android进程间通信(IPC)机制Binder简要介绍和学习计划

    在Android系统中,每一个应用程序都是由一些Activity和Service组成的,这些Activity和Service有可能运行在同一个进程中,也有可能运行在不同的进程中.那么,不在同一个进程的 ...

  6. Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划

    前面我们从Android应用程序与SurfaceFlinger服务的关系出发,从侧面简单学习了SurfaceFlinger服务.有了这些预备知识之后,我们就可以从正面来分析SurfaceFlinger ...

  7. Android应用程序组件Content Provider简要介绍和学习计划

    在Android系统中,Content Provider作为应用程序四大组件之一,它起到在应用程序之间共享数据的作用,同时,它还是标准的数据访问接口.前面的一系列文章已经分析过Android应用程序的 ...

  8. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划

    在Android系统中,提供了独特的匿名共享内存子系统Ashmem(Anonymous Shared Memory),它以驱动程序的形式实现在内核空间中.它有两个特点,一是能够辅助内存管理系统来有效地 ...

  9. Android窗口管理服务WindowManagerService的简要介绍和学习计划

    在前一个系列文章中,我们从个体的角度来分析了Android应用程序窗口的实现框架.事实上,如果我们从整体的角度来看,Android应用程序窗口的实现要更复杂,因为它们的类型和作用不同,且会相互影响.在 ...

  10. Chromium多进程架构简要介绍和学习计划

    Chromium以多进程架构著称,它主要包含四类进程,分别是Browser进程.Render进程.GPU进程和Plugin进程.之所以要将Render进程.GPU进程和Plugin进程独立出来,是为了 ...

最新文章

  1. 全球数字孪生市场大预测:2025 年的 358 亿美元,年复合增长率(CAGR)高达 37.8%...
  2. 计算机专业的金书,《计算机专业英语》书评,金书网
  3. 【NIO】之IO和NIO的区别
  4. Linux的实际操作:Linux的分区
  5. (组合数学笔记)Pólya计数理论_Part.9_Pólya定理的推广——De Bruijn定理
  6. @Valid注解的使用(转)
  7. 据说IE7.0不支持跨域名脚本,那网页计数器不是要失效啦?
  8. Atitit  undac网络设备管理法案 (路由器 交换机等)    法案编号USRr101510
  9. 多路IO复用与异步IO
  10. iPhone应用炫酷的下拉更新效果
  11. MessageBox用法详解(c++)
  12. bp神经网络的训练过程,一文搞定bp神经网络
  13. 支持向量机SVM思维导图
  14. 华为设备VRRP配置命令
  15. windows 配置中科大的 Rust 下载云,提高下载速度
  16. Java高级类特性(二)
  17. CS1.6 网络参数 设置
  18. siki学院的飞机大作战UE4.26代码
  19. linux man命令如何翻页,如何在Linux中使用man帮助使用man命令
  20. 你能编写CMS79F623的EEPROM写入数据20到地址1,并且给出具体代码

热门文章

  1. 查询ES(ElasticSearch)版本
  2. 计算机病毒级防范措施总结,计算机病毒及防范措施
  3. 怎么把cad的图导入ps_CAD图如何导入Photoshop的方法
  4. 如何使用YouTube视频管理器
  5. 来聊聊我的阿里云P7面试经历
  6. 概率论的学习和整理--番外4:学习期望之前,先学习平均数(包括算术平均数,几何平均数,调和平均数等),众数,中位数等概念差别。
  7. 东北大学OJ-1212: 实验3-4 :scanf、printf测试
  8. android开机画面在uboot里吗,iTOP-6818开发板-Android5.1修改uboot和内核开机LOGO
  9. 硕泰克SL-67fv1支持PIII800EB吗?
  10. 支付宝支付接口的使用