chromium
base
java
做者: 易旭昕 (@roger2yi)node
Chromium 提供了一个相似 WTF 的基础库,甚至包含了更多的内容。这个基础库在 Blink 以外被普遍使用(Blink 里面仍然使用的是 WTF),了解它的使用对咱们实际的代码编写是十分重要的。本文主要介绍 Chromium 基础库包括的主要内容,并详细说明一些重要类型的使用方式。若是须要了解某个特定目录或者文件的内容概要,学辉的这篇文档能够提供一个不错的全面索引,另外 Chromium 为全部的基础库类型都提供了完整的单元测试,经过阅读单元测试代码了解这些类型的使用也是很好的方式。android
Chromium 基础库包括的内容十分繁杂,我把其中的主要部分大体分为如下几类:web
容器类型数组
Chromium 的代码主要使用 STL 容器类型,好比 std::vector,std::list,另外 GCC 和 MSVC 提供的 STL 扩展容器类型 hash_map 和 hash_set 也在 Chromium 中使用,不过统一放在 base 名字空间里面,经过 base::hash_map,base_hash_set 使用。安全
在 STL 外,Chromium 基础库还提供了一些额外的容器类型好比 base::LinkedList,base::MRUCache 等。cookie
容器类型代码位于 containers 子目录下。多线程
智能指针闭包
Chromium 提供了一篇官方的文档 Smart Pointer Guidelines 讲解了在 Chromium 里面常见的几种智能指针,最多见的包括 base::scoped_ptr,base::ScopedVector,base::WeakPtr 和 base::scoped_refptr。app
智能指针代码位于 memory 子目录下。
回调函数
Chromium 基础库提供了 base::Bind 机制,能够将全局函数和成员函数,跟它的调用上下文绑定在一块儿,构成一个回调函数对象。这个回调函数对象能够被传递,被保存,被当作消息发送到线程的消息 循环里面,最后咱们能够经过这个回调函数对象的 Run 方法调用跟它关联的函数。
回调函数代码位于基础库的根目录下。
线程相关
Chromium 基础库提供了大量跟线程相关的设施,包括平台线程的封装类型 base::Thread,线程本地存储 base::ThreadLocalPointer,消息循环 base::MessageLoop,线程同步设施 base::Lock,base::WaitableEvent 等等,还有原子操做和内存屏障的支持。
线程相关的代码位于 threading,message_loop,synchronization 子目录下,原子操做和内存屏障位于根目录的 atomicops.h。
字串处理
Chromium 使用 std::string 做为字串容器,官方文档 Chromium String usage 提供了在 Chromium 里面字串使用的一些说明。另外 strings 子目录下提供了一些针对字串的辅助操做设施。
文件操做
Chromium 基础库的 base::File 提供了文件相关的操做,相关的代码位于根目录和 files 子目录下;
计时器
Chromium 基础库的 base::Timer 提供了计时器相关的操做,相关的代码位于 timer 子目录下;
日志和调试
Chromium 基础库提供了通用的日志输出和各类调试辅助等机制,相关的代码位于根目录, debug 和 profile 子目录下;
系统监控
包括系统状态监控,电池状态监控和内存监控,分别位于 system_monitor,power_monitor,和 memory 子目录下;
Android 相关
基础库的 android 子目录下是 Android 平台相关的代码,除了包括其它基础类型的 Android 适配代码外,还有一些 Android 平台特有的类型,像一些用于 JNI 支持的辅助类型。
除了上面列举的部分外,基础库还包括的一些设施有进程,内存分配器,国际化支持,随机数生成,Base64编码,Sha1编码等等,还有一些难以归类的工具类型。
base::LinkedList 是 std::list 的一个替代品,优势是当你拥有一个节点对象时,要删除这个节点只须要 O(1) 的复杂度,而且插入节点不须要新增分配内存。可以作到这一点是由于 LinkedList 要求节点类型必须以 LinkNode 做为基类,而 LinkNode 自己已经包含了指向前/后节点的指针。下面的代码演示了 LinkedList 的常见用法:
class MyNodeType : public LinkNode<MyNodeType> { ... }; LinkedList<MyNodeType> list; LinkNode<MyNodeType>* n1 = ...; LinkNode<MyNodeType>* n2 = ...; LinkNode<MyNodeType>* n3 = ...; list.Append(n1); list.Append(n3); n2->InsertBefore(n3); for (LinkNode<MyNodeType>* node = list.head(); node != list.end(); node = node->next()) { MyNodeType* value = node->value(); ... }
MRU 是 most recently used 的缩写,MRUCache 提供了一个相似 Map 的容器类型,主要的区别是能够设定容器的最大容纳个数,若是超过则自动移除最久不被使用的那个对象。
MRUCache 实际上还存在几种不一样的变种:
MRUCache 是最经常使用的,它假设自身不拥有对象,当移除对象时不执行删除操做;
OwningMRUCache 假设本身拥有对象,并要求存储对象是使用指针类型,在移除对象时会执行删除操做;
HashingMRUCache 跟 MRUCache 的区别是,它内部使用 base::hash_map 而不是 std::map 存储对象,因此也要求键值对象支持 hash 操做;
按照官方文档的说明,何时咱们应该使用什么类型的智能指针:
拥有对象的时候
使用 scoped_ptr 或者 ScopedVector,它们可使用来管理所拥有的非引用计数的堆分配对象。
不拥有对象的时候
使用 raw pointer 或者 WeakPtr。若是其它代码拥有对象,可是你须要知道这个对象是否已经被销毁,就使用 WeakPtr,当所关联的对象被销毁的时候 WeakPtr 会自动被置空。你能够经过 WeakPtr.get 方法得到关联对象的指针,若是返回值为空则说明对象已经被销毁。
使用引用计数对象的时候
使用 scoped_refptr,不过 Chromium 不鼓励使用引用计数对象,特别是在多线程场景下,引用计数对象会使对象的拥有权难以肯定和对象销毁的顺序和时机难以肯定。
base::scoped_ptr 是 Chromium 里面最经常使用的智能指针,一些常见的用法:
// We put a pointer into a smart pointer at construction time.scoped_ptr<base::Value> value(base::JSONReader::Read(data));scoped_ptr<Foo> foo_ptr(new Foo(...));// ...Or by using reset().scoped_ptr<Bar> bar_ptr; // Like "Bar* bar_ptr = NULL;".bar_ptr.reset(new Bar(...)); // Now |bar_ptr| is non-NULL and owns the object.// We can test the smart pointer directly or use get() to see the raw pointer underneath.if (!value) return false;Foo* raw_ptr = foo_ptr.get();// We can call through the smart pointer as if it were a pointer.DictionaryValue* dict = NULL;if (!value->GetAsDictionary(&dict)) return false;
当 scoped_ptr 做为函数参数使用时,这意味着函数的代码会得到参数对象的全部权,函数的调用者若是不是使用一个临时的 scoped_ptr 的话,它须要使用 Pass() 方法来放弃本身的 scoped_ptr 对对象的全部权,例程以下:
// Foo() takes ownership of |bar|.void Foo(scoped_ptr<Bar> bar);...scoped_ptr<Bar> bar_ptr(new Bar());Foo(bar_ptr.Pass()); // Pass() makes |bar_ptr| NULL.Foo(scoped_ptr<Bar>(new Bar())); // No need to use Pass() on temporaries.
若是函数返回一个 scoped_ptr,这意味着函数的调用者得到返回对象的全部权,例程以下:
// Foo takes ownership of |bar|, and the caller takes ownership of the returned// object.scoped_ptr<Bar> Foo(scoped_ptr<Bar> bar) { if (cond) { return bar.Pass(); // Transfers ownership of |bar| back to // the caller. } return scoped_ptr<Bar>(new Bar())); // No Pass() necessary on temporaries. // Note that on this codepath, |bar| gets deleted here.}
最后须要注意的是不该该在函数的参数和返回值中使用 scoped_ptr 的指针或者引用形式(scoped_ptr<>* scoped_ptr<>&
),它会模糊全部权的转移,使最终谁拥有对象的全部权难以理解。
在 STL 容器里面存储 scoped_ptr, 相似 std::vector<scoped_ptr<T> >
这样的用法可能会有问题,好比下面的代码:
std::vector<scoped_ptr<T> > vec;...// 对象的全部权会从 vec 转移到 scoped_ptr p,并随着 p 被销毁而销毁!!!scoped_ptr<T> p = vec[0];
由于上述代码的危险性,因此 Chromium 不支持经过 STL 容器存储 scoped_ptr,它提供了 base::ScopedVector 来知足大部分这种需求,ScopedVector 拥有存储在它内部的对象,并在移除对象的时候负责销毁对象,若是 ScopedVector 自己被销毁,它会销毁它所存储的全部对象。由于 ScopedVector 内部存储的是 raw pointer,就不存在像 std::vector<scoped_ptr<T> >
这样容易误用的危险性。
base::ScopedVector<T> vec;...// 经过 raw pointer p 使用对象,不会有全部权的转移T* p = vec[0];
若是须要在其它 STL 容器里面使用智能指针,但愿在容器被销毁或者移除元素时自动销毁容器存储的对象,能够考虑使用 linked_ptr。
base::WeakPtr 是所谓的弱指针,Chromium 鼓励更多使用 WeakPtr 而不是滥用须要引用计数的 scoped_refptr,由于 WeakPtr 明确不会拥有对象的全部权,也不会影响对象的销毁顺序。
base::WeakPtr 须要经过 base::WeakPtrFactory 建立,通常状况下它们使用的方式是这样的:
class Controller { public: void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } void WorkComplete(const Result& result) { ... } private: // Member variables should appear before the WeakPtrFactory, to ensure // that any WeakPtrs to Controller are invalidated before its members // variable's destructors are executed, rendering them invalid. WeakPtrFactory<Controller> weak_factory_; }; class Worker { public: static void StartNew(const WeakPtr<Controller>& controller) { Worker* worker = new Worker(controller); // Kick off asynchronous processing... } private: Worker(const WeakPtr<Controller>& controller) : controller_(controller) {} void DidCompleteAsynchronousProcessing(const Result& result) { if (controller_) controller_->WorkComplete(result); } WeakPtr<Controller> controller_; };
须要支持 WeakPtr 的类型 Controller 拥有一个 WeakPtrFactory 的成员变量,外部获取的 WeakPtr 都是经过这个 WeakPtrFactory 建立的;
当 Controller 对象被销毁时,它的 WeakPtrFactory 成员变量也会同时被销毁,WeakPtrFactory 被销毁的同时会将全部经过它建立的 WeakPtr 置空;
Controller 的 WeakPtrFactory 的成员变量通常放在最后面,这样它就是第一个被销毁的成员变量,彷佛没有太大意义,不过 Chromium 习惯使用这样的方式;
在多线程环境下使用 WeakPtr 和 WeakPtrFactory 须要注意,它们只支持这样的方式:
WeakPtrFactory 和 WeakPtr 属于建立它们的线程,也只能在建立它们的线程将 WeakPtr 置空,检查一个 WeakPtr 是否为空,和访问 WeakPtr 指向的对象;
属于线程 A 的 WeakPtr 能够传递给 线程 B,线程 B 不能直接使用这个 WeakPtr,这不是线程安全的,可是它可使用这个 WeakPtr 往线程 A 发送任务(PostTask),由于任务是在线程 A 执行的,因此任务执行代码自己可使用这个 WeakPtr;
用于支持引用计数对象的智能指针,要求对象类型继承至 RefCounted 或者 RefCountedThreadSafe,后者是线程安全的。Chromium 由于历史遗留的缘故,当前的代码中使用 scoped_refptr 的地方还比较多,可是目前官方已经不鼓励 scoped_refptr 的使用,认为它会致使对象的全部权,和销毁的顺序和时机难以肯定,并认为绝大部分状况下 scoped_refptr 均可以使用 scoped_ptr 和 WeakPtr 来取代,设计自己也不该该过多依赖多个线程共享对象这种方式。
下面是一些简单的使用例程:
class MyFoo : public RefCounted<MyFoo> { ... }; void some_function() { scoped_refptr<MyFoo> foo = new MyFoo(); foo->Method(param); // |foo| is released when this function returns } void some_other_function() { scoped_refptr<MyFoo> foo = new MyFoo(); ... foo = NULL; // explicitly releases |foo| ... if (foo) foo->Method(param); } { scoped_refptr<MyFoo> a = new MyFoo(); scoped_refptr<MyFoo> b; b.swap(a); // now, |b| references the MyFoo object, and |a| references NULL. } { scoped_refptr<MyFoo> a = new MyFoo(); scoped_refptr<MyFoo> b; b = a; // now, |a| and |b| each own a reference to the same MyFoo object. }
linked_ptr 行为上有些相似 scoped_refptr,可是不须要对象自己支持引用计数,它是经过将全部指向同一个对象的 linked_ptr 连接成一条链来实现引用计数的,当一个 linked_ptr 从另一个 linked_ptr 拷贝时,它会把自身加入这条链,而这个 linked_ptr 被销毁时,它会把自身从这条链移除,若是它是最后一个,则同时销毁指向的对象。
linked_ptr 实际上有可能比 scoped_refptr 更危险,它使得对象的持有者和销毁时机变得更不明确,同时也不是线程安全的。因此 linked_ptr 通常只是用在 STL 容器上面,容器持有这些对象,而且在容器自己被销毁时销毁对象,这样就不会产生太多混乱。
base::SupportsUserData 的实现里面使用了 linked_ptr,用来在一个 std::map 里面存储 User Data。
typedef std::map<const void*, linked_ptr<Data> > DataMap; // Externally-defined data accessible by key. DataMap user_data_;SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const { DataMap::const_iterator found = user_data_.find(key); if (found != user_data_.end()) return found->second.get(); return NULL;}void SupportsUserData::SetUserData(const void* key, Data* data) { user_data_[key] = linked_ptr<Data>(data);}
Chromium 提供了 base::Bind 和模版类型 base::Callback 对函数回调提供了支持,下面是一个简单的使用例程,将一个全局函数绑定到一个 Callback 对象,并经过 Callback.Run 调用这个函数:
int Return5() { return 5; } base::Callback<int(void)> func_cb = base::Bind(&Return5); LOG(INFO) << func_cb.Run(); // Prints 5.
若是要绑定一个类的成员函数,咱们须要为 Bind 方法提供这个类的一个实例对象,把它跟 Callback 对象绑定,为了保证这个对象在 Callback 对象被执行时仍然存活,或者 Callback 对象可以知道这个对象已经被销毁,咱们须要提供一个 scoped_refptr 或者 WeakPtr,经过 base::Unretained(ptr) 用 raw pointer 也能够,不事后果自负... 早期 Chromium 的代码使用 scoped_refptr 比较多,如今 Chromium 更倾向于使用 WeakPtr,固然使用 WeakPtr 时咱们要注意这个 Callback 只能在 WeakPtr 所属的线程中被调用,由于它是非线程安全的,下面是一个使用 scoped_refptr 的例子:
class Ref : public base::RefCountedThreadSafe<Ref> { public: int Foo() { return 3; } void PrintBye() { LOG(INFO) << "bye."; } }; scoped_refptr<Ref> ref = new Ref(); base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref); LOG(INFO) << ref_cb.Run(); // Prints out 3.
若是绑定的函数须要参数,咱们能够事先绑定全部参数对象到 Callback 里面,也能够事先不绑定参数,甚至能够事先只绑定一部分参数,事先绑定全部参数的 Callback 在 Chromium 里面称为闭包 Closure:
void MyFunc(int i, const std::string& str) {} base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc); cb.Run(23, "hello, world"); void MyFunc(int i, const std::string& str) {} base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world"); cb.Run(); base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
若是想让 Callback 对象拥有跟它绑定的类对象或者参数对象,也可使用 base::Owned 或者 base::Passed 方法,分别针对 raw pointer 和 scoped_ptr,若是是 scoped_ptr 类型参数的话,在调用时 Callback 就会将这个参数对象的全部权转移给被回调的函数,最后 Callback 对象被销毁时会自动销毁绑定的类对象和参数对象(若是还拥有这个参数对象的话):
MyClass* myclass = new MyClass; base::Bind(&MyClass::Foo, base::Owned(myclass)); void TakesOwnership(scoped_ptr<Foo> arg) {} scoped_ptr<Foo> f(new Foo); // f becomes null during the following call. base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));
总而言之,在使用 Chromium 的回调函数机制时,必定要很是清楚跟 Callback 对象绑定的类对象和参数对象的全部权和生命周期,避免在 Callback 被调用时,访问到已经被销毁的对象。
base::Thread 是 Chromium 提供的对平台线程的封装,并自带了消息循环 base::MessageLoop,若是须要一个不用消息循环的线程,能够考虑使用 base::SimpleThread。
一个继承 base::Thread 的本身的线程类,可能须要复写 Init 和 Cleanup 方法,它们在这个线程中被调用,分别位于消息循环启动和中止的时候。
class InProcessRendererThread : public base::Thread { public: ... protected: virtual void Init() override; virtual void CleanUp() override; ...};
咱们能够经过 Thread.message_loop 或者 Thread.message_loop_proxy 方法获取这个线程的消息循环,后者返回的是 MessageLoopProxy,在 Chromium 里面使用 MessageLoopProxy 比直接使用 MessageLoop 要更广泛,而且经过 scoped_refptr 的方式使用 MessageLoopProxy 比经过 raw pointer 的方式使用 MessageLoop 也更安全,经过下面的两种方式能够得到当前运行线程的 MessageLoopProxy。
MessageLoop::current()->message_loop_proxy() MessageLoopProxy::current()
MessageLoopProxy 继承了接口 SequencedTaskRunner,后者又继承了接口 TaskRunner,因此 MessageLoopProxy 实现了一系列的 PostXXXTask 的方法。一个 Task 实际上就是一个 Closure,如前所述 Closure 就是一个预先绑定了全部参数对象的 Callback 对象。 经过 MessageLoopProxy PostTask 就至关于发送一个消息给这个 MessageLoopProxy 所属的线程,这个被发送的 Callback 对象将会在 MessageLoopProxy 所属的线程执行,跟 Callback 对象绑定的函数将会被调用。
PostXXXTask 有若干变种,包括延迟的时间,是不是 Non-Nestable。延迟时间比较容易理解,不须要延迟则为 0,而 Non-Nestable 的意思是 - 如 果 Task T1 在执行过程当中 Post Task T2 到当前线程的 MessageLoop,而且 T1 接着直接调用 MessageLoop 的 Run,或者 RunLoop 的 Run 方法,至关于要求 MessageLoop 在当前消息循环中进入一个子循环,立刻执行其它等待中的任务,在这种情况下 MessageLoop 进入了 Nested 状态,若是 T2 是 Non-Nestable,Chromium 将会保证 T2 在这种状况下绝对不会被执行,若是 T2 不是 Non-Nestable,就有可能在会被执行。
下面是一个简单使用例程:
scoped_refptr<base::MessageLoopProxy> ui_loop_;base::WeakPtr<SharedRendererState> ui_thread_weak_ptr_;void SharedRendererState::PostExternalDrawConstraintsToChildCompositor( const ParentCompositorDrawConstraints& parent_draw_constraints) { if (UpdateDrawConstraints(parent_draw_constraints)) { // No need to hold the lock_ during the post task. ui_loop_->PostTask( FROM_HERE, base::Bind(&SharedRendererState::UpdateParentDrawConstraintsOnUIThread, ui_thread_weak_ptr_)); }}
若是须要任务执行后原线程得到通知,可使用 PostTaskAndReply 方法,参考下面的例程,task 执行后,reply 会在调用 PostTaskAndReplay 的原线程被调用,而且 task 和 reply 对象都保证在原线程被销毁,这样咱们能够在 task 和 reply 上绑定必需要在原线程销毁的对象。另一些须要注意的地方:
task 绑定的类对象会做为参数传递给 reply 的回调函数;
reply 绑定的类对象 DataLoder 不是线程安全的,它经过 WeakPtr 跟 reply 绑定,能够提早被销毁,reply 会被自动取消;
bool PostTaskAndReply(const tracked_objects::Location& from_here, const Closure& task, const Closure& reply); class DataBuffer : public RefCountedThreadSafe<DataBuffer> { public: // Called to add data into a buffer. void AddData(void* buf, size_t length); ... }; class DataLoader : public SupportsWeakPtr<DataLoader> { public: void GetData() { scoped_refptr<DataBuffer> buffer = new DataBuffer(); target_thread_.message_loop_proxy()->PostTaskAndReply( FROM_HERE, base::Bind(&DataBuffer::AddData, buffer), base::Bind(&DataLoader::OnDataReceived, AsWeakPtr(), buffer)); } private: void OnDataReceived(scoped_refptr<DataBuffer> buffer) { // Do something with buffer. } };
若是 PostTask 以后,咱们又但愿取消它,可使用 base::CancelableTaskTracker 来 PostTask,CancelableTaskTracker 自己不是线程安全的,它的建立,销毁,PostTask,Cancel 都必须在同一个线程。下面是一个简单的使用例子:
Thread worker_thread("worker thread"); worker_thread.Start(); CancelableTaskTracker::TaskId task_id = task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), FROM_HERE, Bind(&DoNothing), Bind(&DoNothing)); task_tracker_.TryCancel(task_id);
Chromium 提供了 ThreadLocalPointer,它是平台相关的线程本地存储机制的封装,能够存放一个 raw pointer,若是须要的是 bool 类型的变量,ThreadLocalBoolean 提供了更简单的使用方式。ThreadLocalPointer 的一个简单例程:
// My class is logically attached to a single thread. We cache a pointer // on the thread it was created on, so we can implement current(). MyClass::MyClass() { DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL); Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this); } MyClass::~MyClass() { DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL); Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL); } // Return the current MyClass associated with the calling thread, can be // NULL if there isn't a MyClass associated. MyClass* MyClass::current() { return Singleton<ThreadLocalPointer<MyClass> >::get()->Get(); }
base::Lock 是平台相关锁的封装,base::AutoLock 提供了一个自动加锁/解锁的辅助类,这部分都比较容易理解。base::ConditionVariable 是平台相关的条件量的封装,跟其它库的 Condition 类型有些不一样的是,它须要在构造时就指定对应的锁,而不是在 Wait 的时候才指定。
为了方便实现线程同步消息,Chromium 还提供了 base::WaitableEvent (若是是位于 cc 模块的代码,也可使用 cc::CompletionEvent,它是 base::WaitableEvent 的封装),WaitableEvent 构造函数的第一个参数 manual_reset 的含义是,若是它为 false,一个已经 signaled 的 WaitableEvent 在被查询 IsSignaled 后会自动恢复到 unsignaled 的状态,因此通常没有特殊须要第一个参数都应该为 true。下面是一个简单的线程同步消息处理的例子:
template <typename T>static void RunTaskWithResult(base::Callback<T(void)> task, T* result, base::WaitableEvent* completion) { *result = task.Run(); completion->Signal();} base::WaitableEvent completion(true, false); bool result = false; QueueTask( base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion)); completion.Wait();
base::WaitableEventWatcher 提供了 WaitableEvent 异步响应的使用方式,请看下面的例程,咱们能够经过 WaitableEventWatcher 监控某个 WaitableEvent,并在它被 Signal 的时候触发事先设定的回调函数,实际内部实现是当 WaitableEvent 被 Signal 时,WaitableEventWatcher 事先设定的 Callback 对象会被发送到 StartWatching 的调用线程的消息循环里面:
class MyClass { public: void DoStuffWhenSignaled(WaitableEvent *waitable_event) { watcher_.StartWatching(waitable_event, base::Bind(&MyClass::OnWaitableEventSignaled, this); } private: void OnWaitableEventSignaled(WaitableEvent* waitable_event) { // OK, time to do stuff! } base::WaitableEventWatcher watcher_; };
Chromium 主要使用 std::string 做为字串类型,std::string 的一个主要问题是它自己不包含编码信息,因此 Chromium 约定 std::string 使用 UTF-8 编码,基础库里面还提供了 base::string16,string16 使用 UTF-16 编码。
Chromium 另外还有一个 base::StringPiece 类型,它相似 WTF 里面的 CString,基本上就是 C 风格字串的一个简单封装,StringPiece 一般只是用来传递一块 string data 或者 raw data,它自己并不拥有这些数据,销毁时也不会释放数据。
下面是一些使用时的注意事项:
使用 string.empty() 作空串检查;
字串常量使用 char[] 而不是 std::string,好比 const char kFoo[] = “foo”;
在函数输入参数中使用 std::string,最好使用引用常量的方式避免拷贝;
在循环的 inner loop 里面,通常应该避免临时 std::string 对象建立;
Chromium 提供的一些字串处理的辅助方法,好比字串格式化,分割,数值字串类型转换,比较,替换等等,位于 strings 子目录下,都比较简单,这里就再也不详细说明了。
base::PathService 提供了一种设定和获取一些预约义用途目录的机制,在 Android 上,咱们须要的目录定义在 base_path_android.h 和 ui_base_path.h 里面,另外 PathService.java 提供了在 Java 端设定路径的功能。
enum { PATH_ANDROID_START = 300, DIR_ANDROID_APP_DATA, // Directory where to put Android app's data. DIR_ANDROID_EXTERNAL_STORAGE, // Android external storage directory. PATH_ANDROID_END};
PathService.override(PathService.DIR_MODULE, "/system/lib/"); final int DIR_RESOURCE_PAKS_ANDROID = 3003; PathService.override(DIR_RESOURCE_PAKS_ANDROID, "/system/framework/webview/paks");
base::File 提供了平台相关的文件对象的封装,能够经过它对文件和目录进行操做,包括建立,读写文件等等。base::FilePath 提供了一个文件或者目录路径的封装。
base::FileProxy 提供了一种异步文件操做的方法,你能够为 FileProxy 设置一个 TaskRunner,好比某个线程的 MessageLoopProxy,而后在 FileProxy 上执行的操做实际上都是由这个 TaskRunner 所属的线程异步执行,FileProxy 提供的方法跟 File 基本一致,通常后面会增长一个用于响应操做结果的 Callback 对象,这个 Callback 对象会在原调用线程执行。FileProxy 有一个限制是不能同时 Proxy 多个操做,只有完成一个操做后才能执行下一个操做。
下面是一个简单的例程,咱们在另一个 file_thread_ 线程建立或者打开一个文件,当文件建立或者打开后,原线程会执行 DidCreateOrOpen 函数处理操做结果:
TaskRunner* file_task_runner() const { return file_thread_.message_loop_proxy().get(); } void DidCreateOrOpen(File::Error error) { error_ = error; MessageLoop::current()->QuitWhenIdle(); } FileProxy proxy(file_task_runner()); proxy.CreateOrOpen( test_path(), File::FLAG_CREATE | File::FLAG_READ, Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); MessageLoop::current()->Run(); EXPECT_EQ(File::FILE_OK, error_); EXPECT_TRUE(proxy.IsValid()); EXPECT_TRUE(proxy.created()); EXPECT_TRUE(PathExists(test_path()));
Chromium 还提供不少文件相关的辅助类:
base::FileEnumerator 提供了枚举某个 FilePath 下面的子文件的功能;
base::FilePathWatcher 提供了监控某个文件或者目录变化的功能;
base::ImportantFileWriter 提供了另一种文件写入方式,避免应用崩溃致使文件写入一半,数据不完整的情况,原理是先写入一个临时文件,写完后再重命名;
base::MemoryMappedFile 提供了一种将只读文件所有或者部分映射到内存,读取文件至关于内存访问,加快读取的速度的机制;
file_util.h 里面提供大量文件操做的辅助方法,好比 CreateTemporaryFile,GetFileSize 等等;
base::Timer 实际上至关于 MessageLoop::PostDelayedTask 的封装,对外提供了一次性或者不断重复的计时器功能。Timer 的构造函数里面 retain_user_task 的含义是,当 Timer 被 Stop 的时候,关联的任务是否被保留,默认值为 true,也就是保留而不置空。跟 WTF 里面的 Timer 同样,base::Timer 是有线程归属性的,它属于调用 Start 或者 Reset 方法的线程,设置的任务也在这个线程里面执行。
base::Timer timer(false, false); EXPECT_FALSE(timer.IsRunning()); timer.Start(FROM_HERE, TimeDelta::FromDays(1), base::Bind(&TimerTestCallback)); EXPECT_TRUE(timer.IsRunning()); timer.Stop(); EXPECT_FALSE(timer.IsRunning()); EXPECT_TRUE(timer.user_task().is_null());
base::ElapsedTimer 提供一个简单的方法给程序计算某些操做的耗时。
Chromeium 提供了 LOG,DLOG,VLOG 几种输出日志的方式,相似下面这样的代码:
LOG(INFO) << "Found " << num_cookies << " cookies";LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
INFO 是输出日志的级别,一共包括 INFO,WARNING,ERROR 和 FATAL 这四种,其中 FATAL 会在日志输出后自动引起一个崩溃。LOG_IF 提供了额外的条件判断,条件成立时才输出日志。DLOG 跟 LOG 的区别是 DLOG 只在 DEBUG 版本才生效,而 VLOG 跟 LOG 的区别是能够用 verbose 级别来控制是否生效,好比:
VLOG(1) << "I'm printed when you run the program with --v=1 or more";VLOG(2) << "I'm printed when you run the program with --v=2 or more";
当启动开关 --v=1 时 VLOG 1 以上的级别生效。
Chromium 提供了强大的 Tracing 机制,在 Android 上也对接了 Android Systrace 机制,因此对咱们来讲,最简单的方式就打开 Chromium Tracing,而后经过 Android Systrace 捕捉跟踪的输出。
在 TestShell 里面,咱们能够经过设置 BrowserActivity.ENABLE_ATRACE 开启 Chromium Tracing,或者经过菜单开启,而后调用 Android systrace 命令捕捉便可。
若是要增长跟踪的方法,最简单的方式是使用以下代码:
TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware");
更复杂的跟踪方式能够参考 trace_event.h 里面的说明文档。
base::debug::StackTrace 提供了调用堆栈打印的功能,StackTrace 会在被构造的时候存储当前的调用堆栈数据,而后能够经过它直接打印到控制台或者获取相应的文本,在 Android 上是经过 logcat 输出 error 日志。通常来讲 StackTrace 能够做为函数的临时变量输出当前函数的调用堆栈,也能够做为对象的成员变量记录对象建立时的调用堆栈。StackTrace 输出的是地址信息,还须要使用符号表和对应的工具翻译成可读的函数名字。
下面是一个简单的使用例程和输出的结果:
void AwContents::Destroy(JNIEnv* env, jobject obj) { base::debug::StackTrace().Print(); ...}#00 0x751c38a1 /data/app-lib/com.uc.webkit.test-1/libwebviewuc.so+0x001f08a1#01 0x4153f30f /system/lib/libdvm.so+0x0001d30f
Chromium 提供了 MemoryPressureListener 接口,在 Android 上实际对接了 ComponentCallbacks2 的 onTrimMemory 和 onLowMemory(MemoryPressureListener.java)。
使用方式以下:
void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) { ... } // Start listening. MemoryPressureListener* my_listener = new MemoryPressureListener(base::Bind(&OnMemoryPressure)); ... // Stop listening. delete my_listener;
建立 MemoryPressureListener 对象,并传入一个 Callback 对象,启动监听;
销毁 MemoryPressureListener 中止监听;
Callback 对象会在建立 MemoryPressureListener 的线程被调用,调用是异步的,经过线程消息,即便这个线程就是 Android 的 UI 线程;
MemoryPressureLevel 包括 MEMORY_PRESSURE_MODERATE 和 MEMORY_PRESSURE_CRITICAL,跟 Android ComponentCallbacks2.onTrimMemory 和 onLowMemory 的对应关系以下面代码所示;
// Modules are advised to free buffers that are cheap to re-allocate and not// immediately needed.DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_MODERATE, 0)// At this level, modules are advised to free all possible memory.// The alternative is to be killed by the system, which means all memory will// have to be re-created, plus the cost of a cold start.DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_CRITICAL, 2)
public void onTrimMemory(int level) { maybeNotifyMemoryPresure(level);}public void onLowMemory() { nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);}public static void maybeNotifyMemoryPresure(int level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL); } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND || level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) { // Don't notifiy on TRIM_MEMORY_UI_HIDDEN, since this class only // dispatches actionable memory pressure signals to native. nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_MODERATE); }}
base::BuildInfo 基本上等同于 Android 的 android.os.Build 的 Native 版本,能够经过它得到一些 Build 相关的信息,好比系统版本号等。
path_utils.h 提供一些辅助函数,用于获取 Android 系统或者应用相关的特定目录,好比 GetDataDirectory 返回当前应用的 Data 目录。
jni_string.h 提供了一些跟字串相关的辅助函数,用于 Java String 和 Native String 之间的转换,好比 ConvertJavaStringToUTF8,ConvertUTF8ToJavaString 等。
jni_array.h 提供了一些跟数组相关的辅助函数,好比 ToJavaXXXArray 将一个 Native 数组转换成一个 Java 数组对象,转换过程当中原始的数据会被拷贝。
base::android::ScopedJavaLocalRef 和 base::android::ScopedJavaGlobalRef 提供了在 Native 端持有一个 Java 对象,并在 Scoped 对象被销毁时自动解除该 Java 对象引用的机制,有点相似是针对 Java 对象的 scoped_refptr。
ScopedJavaLocalRef 对应 JNI 的 LocalRef,做为栈对象在函数内部使用,通常用于在函数结束时自动解除关联的 Java 对象的引用,或者做为函数的返回值传递 Java 对象的引用给它的调用者:
bool GetDatabaseDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDatabaseDirectory(env, GetApplicationContext()); FilePath data_path(ConvertJavaStringToUTF8(path)); *result = data_path; return true;}
ScopedJavaGlobalRef 对应 JNI 的 GlobalRef,通常做为类的成员变量,或者在须要超过某个函数的调用生命周期去持有一个 Java 对象的情况下使用。
下面的例子演示了一个异步回调的处理,咱们须要一个 ScopedJavaGlobalRef 保证这个关联的 Java 对象在回调函数被真正执行时任然存活而不会被销毁,base::Owened 将 j_callback 的拥有权转移给 base::Bind 建立的 Callback 对象。
void GenerateMHTMLCallback(ScopedJavaGlobalRef<jobject>* callback, const base::FilePath& path, int64 size) { JNIEnv* env = AttachCurrentThread(); // Android files are UTF8, so the path conversion below is safe. Java_AwContents_generateMHTMLCallback( env, ConvertUTF8ToJavaString(env, path.AsUTF8Unsafe()).obj(), size, callback->obj());}} // namespacevoid AwContents::GenerateMHTML(JNIEnv* env, jobject obj, jstring jpath, jobject callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>(); j_callback->Reset(env, callback); base::FilePath target_path(ConvertJavaStringToUTF8(env, jpath)); web_contents_->GenerateMHTML( target_path, base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback), target_path));}
base::JavaObjectWeakGlobalRef 用于持有一个 Java 对象的弱引用,对应 JNI 的 WeakGlobalRef,当须要使用这个 Java 对象时能够经过 JavaObjectWeakGlobalRef.get 返回一个 ScopedJavaLocalRef。
这一节的内容包括一些比较零散,没法归类的工具类型。
base::SupportsUserData 是用来给一个对象增长 UserData 支持的辅助类,User Data,也叫 Client Data,通常使用 Key-Value 的方式存储,是这个对象的使用者将本身或者其它的使用者须要用到的一些数据附加在这个对象上面的一种机制,对象自己只是做为这些数据的一个载体。
须要承载 User Data 的类,须要继承 SupportsUserData,好比 content::WebContents,而须要做为 User Data 存储的类型,须要继承 SupportsUserData::Data,使用 void* 指针作 key。SupportsUserData 是非线程安全的,若是跨线程使用,须要使用者本身保证线程安全。
LazyInstance 提供了一种延迟建立全局静态对象的方式,它的优势是:
它预先在程序的静态内存区分配了对象的内存,当对象建立时就不须要在堆上分配内存,加快了对象建立的速度和减小堆内存碎片;
它的对象建立是线程安全的,不用担忧多线程竞争的情况;
它延迟对象的建立到第一次使用的时候,避免在程序启动时建立,减小了启动的时间开销;
总的来讲 LazyInstance 就像是函数内部的静态对象的线程安全版本,下面是使用的例程:
static LazyInstance<MyClass> my_instance = LAZY_INSTANCE_INITIALIZER; void SomeMethod() { my_instance.Get().SomeMethod(); // MyClass::SomeMethod() MyClass* ptr = my_instance.Pointer(); ptr->DoDoDo(); // MyClass::DoDoDo }
若是明确不须要销毁对象,不须要调用析构函数,可使用 Leaky 类型定义(实际上在 CAW 上,用不用 Leaky 都同样,参看下面的 Singleton):
base::LazyInstance<GlobalTileManager>::Leaky g_tile_manager = LAZY_INSTANCE_INITIALIZER;
通常方便本身的类型实现单例模式的辅助类,使用的例程以下:
// In your header: template <typename T> struct DefaultSingletonTraits; class FooClass { public: static FooClass* GetInstance(); void Bar() { ... } private: FooClass() { ... } friend struct DefaultSingletonTraits<FooClass>; DISALLOW_COPY_AND_ASSIGN(FooClass); };// In your source file: FooClass* FooClass::GetInstance() { return Singleton<FooClass>::get(); }// And to call methods on FooClass: FooClass::GetInstance()->Bar();
须要注意的是:
Singleton::get() 的调用方法必须命名为 GetInstance;
GetInstance 不能是 inline 的,也就是说它的实现不能放在头文件里面;
Singleton::get() 有必定的时间开销,避免在循环的 inner loop 里面每次都调用;
对于 CAW 来讲,使用 Singleton 的类型的析构函数是不会被自动调用的,对于 Chrome for Android 来讲,在子进程退出时,使用 Singleton 的类型的析构函数在进程退出时被自动被调用,另外 LazyInstance 的情况也同样;
总的来讲,Chromium 并不鼓励使用单例模式,因此能不用仍是不用。
base::AutoReset 是一个很简单的辅助类,它通常做为栈对象使用,用途是构造时保存变量原有的值并设置新的值,当生命周期结束,析构的时候恢复变量原有的值。
{ base::AutoReset<bool> frame_resetter(&viewport_clip_valid_for_dcheck_, true); layer_tree_host_->SetNeedsRedrawRect(clip_); layer_tree_host_->Composite(gfx::FrameTime::Now()); }
base::ObserverList 是帮助实现观察者模式的一个辅助类,顾名思义,它提供了一个观察者列表容器。除此之外,使用 ObserverList 而不是直接使用 std::vector 或者 std::list 的缘由还在于 ObserverList 提供了一个特定版本的迭代器实现,在迭代的过程当中从容器中删除本身或者其它的 Observer 是安全的,迭代器的 GetNext 方法会自动检查容器是否被修改过,正确返回修改事后的容器的下一个元素。
通常的使用方式以下:
class MyWidget { public: ... class Observer { public: virtual void OnFoo(MyWidget* w) = 0; virtual void OnBar(MyWidget* w, int x, int y) = 0; }; void AddObserver(Observer* obs) { observer_list_.AddObserver(obs); } void RemoveObserver(Observer* obs) { observer_list_.RemoveObserver(obs); } void NotifyFoo() { FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this)); } void NotifyBar(int x, int y) { FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y)); } private: ObserverList<Observer> observer_list_; };
base::ObserverListThreadSafe 至关于 base::ObserverList 的线程安全版本,经过 ObserverListThreadSafe.Notify 能够调用注册的 Observer 的某一个指定的方法,而且这个方法是在这个 Observer 所属的线程上被调用, 所谓 Observer 所属的线程就是指将 Observer 加入到 ObserverListThreadSafe 里面的那个调用线程。为了作到上述这一点,ObserverListThreadSafe 是经过 PostTask 到线程的消息循环来实现的,这也意味着跟 ObserverList 不一样的是,Notify 和 Callback 被调用是异步的,而 ObserverList 是同步的,MemoryPressureListener 的内部实现就使用了 ObserverListThreadSafe。
MemoryPressureListener::MemoryPressureListener( const MemoryPressureListener::MemoryPressureCallback& callback) : callback_(callback) { g_observers.Get().AddObserver(this);}MemoryPressureListener::~MemoryPressureListener() { g_observers.Get().RemoveObserver(this);}void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) { callback_.Run(memory_pressure_level);}// staticvoid MemoryPressureListener::NotifyMemoryPressure( MemoryPressureLevel memory_pressure_level) { TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure", "level", memory_pressure_level); g_observers.Get().Notify(&MemoryPressureListener::Notify, memory_pressure_level);}