懒人chromium net android移植指南
Chromium瀏覽器的網絡庫是一個功能非常強大的網絡庫,它支持的網絡協議非常多,除了常見的HTTP/1.1,它還支持HTTP/2,QUIC等比較新的協議。這里我們嘗試將Chromium net網絡庫移植到Android平臺,在我們的Android應用中跑起來。
移植Chromium net網絡庫有兩種方式,一是將Chromium net網絡庫及其依賴的所有其它庫編譯為動態鏈接庫,將這些so導入我們的Android應用,然后提取Chromium net網絡庫導出的頭文件并導入我們的Android應用,我們自己編寫JNI代碼進而讓Chromium net跑起來;二是利用Cronet。Cronet是對Chromium net網絡庫的封裝,它為我們提供方便的Java接口。編譯Cronet時,會將Cronet的JNI代碼,Chromium net庫及其依賴的所有其它庫編譯為一個單獨的so,并將各個庫的Java接口編譯為jar包,我們可以將這些jar文件和so文件導入我們的Android應用,并調用Java接口。
這里我們會介紹這兩種方式。
編譯Chromium net模塊
下載Chromium代碼
首先要做的是下載完整的Chromium代碼,這可以參考 Chromium Android編譯指南 完成。然后執行(假設當前目錄是chromium代碼庫的根目錄)命令:
$ gclient runhooks $ cd src $ ./build/install-build-deps.sh $ ./build/install-build-deps-android.sh這些命令幫我們下載構建Chromium所需的工具鏈。
對構建進行配置
構建之前需要對構建進行配置。編輯out/Default/args.gn文件,參照 Chromium Android編譯指南 的說明,輸入如下內容:
target_os = "android" target_cpu = "arm" # (default) is_debug = false # (default)# Other args you may want to set: is_component_build = true is_clang = false symbol_level = 1 # Faster build with fewer symbols. -g1 rather than -g2 enable_incremental_javac = false # Much faster; experimental android_ndk_root = "~/dev_tools/Android/android-ndk-r10e" android_sdk_root = "~/dev_tools/Android/sdk" android_sdk_build_tools_version = "23.0.2"disable_file_support = true disable_ftp_support = true enable_websockets = falseuse_platform_icu_alternatives = true關鍵點主要有如下幾個:
-
is_component_build被置為ture。Chromium文檔 (gn args --list out/Default/輸出) 中對這個配置項的說明如下:
is_component_build Default = false//build/config/BUILDCONFIG.gn:162Component build. Setting to true compiles targets declared as "components"as shared libraries loaded dynamically. This speeds up development time.When false, components will be linked statically.將這個配置項置為true,會使以components聲明的targets被編譯為動態鏈接庫,否則它們將會被編譯為靜態庫。這里我們需要將net等模塊編譯為動態鏈接庫,因而將該配置項置為true。
-
is_debug被置為false,表示編譯非Debug版。在這種情況下,enable_incremental_javac同樣要被置為false。否則在執行gn gen out/Default生成 .ninja 文件時會報出如下的error:
ERROR at //build/config/android/config.gni:136:3: Assertion failed. assert(!(enable_incremental_javac && !is_java_debug)) ^----- See //build/config/compiler/compiler.gni:5:1: whence it was imported. import("//build/config/android/config.gni") ^----------------------------------------- See //BUILD.gn:11:1: whence it was imported. import("//build/config/compiler/compiler.gni") ^-------------------------------------------- - 配置android_ndk_root為標準NDK的路徑,即直接從Google開發者站點下載的NDK包。這個配置指示構建系統,在編譯時使用標準NDK工具鏈,而不是Chromium代碼庫中的NDK工具鏈。這主要是因為Chromium代碼庫中的NDK工具鏈與標準NDK工具鏈之間的差異,會導致我們在JNI代碼中調用Chromium net函數時,出現詭異的鏈接誒問題。
NDK版本的選擇也要注意。Chromium使用的默認NDK相關信息 (gn args --list out/Default/輸出) 如下:
可見默認的NDK版本是r10e的。因而我們也選用r10e版的標準NDK。
- is_clang選項被置為了false。Chromium文檔 (gn args --list out/Default/輸出) 中對這個配置項的說明如下:
這個配置項用于指定是否要用Clang編譯器。
-
disable_file_support、disable_ftp_support和enable_websockets分別被置為true、true和false。這幾個設置主要是為裁剪需要。我們要禁掉chromium net對這幾種協議的支持,以減小最終編譯出來的so文件的大小。
-
use_platform_icu_alternatives被置為true。這個配置也是為了減小最終的so文件的總大小。ICU相關的幾個so文件總和接近2M,通過將use_platform_icu_alternatives置為true,指示不使用Chromium代碼庫中的ICU。
保存out/Default/args.gn文件退出,執行如下命令:
$ gn gen out/Default產生ninja構建所需的 .ninja 文件。
編譯Chromium net
輸入如下命令編譯net模塊:
$ ninja -C out/Default net這個命令會編譯net模塊,及其依賴的所有模塊,包括base,crypto,boringssl,protobuf,url等??匆幌挛覀兙幾g的成果:
$ ls -alh out/Default/ | grep so$ -rwxrwxr-x 1 chrome chrome 967K 11月 11 15:34 libbase.cr.so -rwxrwxr-x 1 chrome chrome 785K 11月 11 15:34 libboringssl.cr.so -rwxrwxr-x 1 chrome chrome 50K 11月 11 15:34 libcrcrypto.cr.so -rwxrwxr-x 1 chrome chrome 3.3M 11月 11 15:36 libnet.cr.so -rwxrwxr-x 1 chrome chrome 90K 11月 11 15:34 libprefs.cr.so -rwxrwxr-x 1 chrome chrome 154K 11月 11 15:34 libprotobuf_lite.cr.so -rwxrwxr-x 1 chrome chrome 70K 11月 11 15:36 liburl.cr.so總共7個共享庫文件,總大小5.4M。
使用Chromium net
將Chromium net導入Android應用
在我們工程的app模塊的jni目錄下為chromium創建文件夾app/src/main/jni/third_party/chromium/libs和app/src/main/jni/third_party/chromium/include,分別用于存放我們編譯出來的共享庫和net等模塊導出的頭文件及這些頭文件include的其它頭文件。
這里我們將編譯出來的所有so文件拷貝到app/src/main/jni/third_party/chromium/libs/armeabi和app/src/main/jni/third_party/chromium/libs/armeabi-v7a目錄下:
cp out/Default/*.so ~/MyApplication/app/src/main/jni/third_party/chromium/libs/armeabi/ cp out/Default/*.so ~/MyApplication/app/src/main/jni/third_party/chromium/libs/armeabi-v7a/提取導出頭文件
為了使用net模塊提供的API,不可避免地要將net導出的頭文件引入我們的項目。要做到這些,需要從chromium工程提取net導出的頭文件。不像許多其它的C/C++項目,源代碼文件、私有頭文件及導出頭文件存放的位置被很好地做了區隔,chromium各個模塊的頭文件和源代碼文件都是放在一起的。這給我們提取導出頭文件的工作帶來了一點麻煩。
好在有gn工具。gn工具提供的desc命令(參考 GN的使用 - GN工具 一文)的輸出有如下這樣兩段:
$ gn desc out/Default/ net Target //net:net Type: shared_library Toolchain: //build/toolchain/android:arm ...... sources//net/base/address_family.cc//net/base/address_family.h ......public[All headers listed in the sources are public.]我們可以據此編寫腳本提取net模塊的頭文件。
我們可以為腳本傳入[chromium代碼庫的src目錄路徑],[輸出目錄的路徑],[模塊名],及[保存頭文件的目標目錄路徑]作為參數,以提取頭文件,[保存頭文件的目標目錄路徑]參數缺失時默認使用當前目錄,如:
$ cd /media/data/MyProjects/MyApplication/app/src/main/jni/third_party/chromium/include $ chromium_mod_headers_extracter.py ~/data/chromium_android/src out/Default net $ chromium_mod_headers_extracter.py ~/data/chromium_android/src out/Default base $ chromium_mod_headers_extracter.py ~/data/chromium_android/src out/Default url利用我們的腳本,提取net、base和url這三個模塊導出的頭文件。
這里一并將該腳本的完整內容貼出來供大家參考:
#!/usr/bin/env pythonimport os import shutil import sysdef print_usage_and_exit():print sys.argv[0] + " [chromium_src_root]" + "[out_dir]" + " [target_name]" + " [targetroot]"exit(1)def copy_file(src_file_path, target_file_path):if os.path.exists(target_file_path):returnif not os.path.exists(src_file_path):returntarget_dir_path = os.path.dirname(target_file_path)if not os.path.exists(target_dir_path):os.makedirs(target_dir_path)shutil.copy(src_file_path, target_dir_path)def copy_all_files(source_dir, all_files, target_dir):for one_file in all_files:source_path = source_dir + os.path.sep + one_filetarget_path = target_dir + os.path.sep + one_filecopy_file(source_path, target_path)if __name__ == "__main__":if len(sys.argv) < 4 or len(sys.argv) > 5:print_usage_and_exit()chromium_src_root = sys.argv[1]out_dir = sys.argv[2]target_name = sys.argv[3]target_root_path = "."if len(sys.argv) == 5:target_root_path = sys.argv[4]target_root_path = os.path.abspath(target_root_path)os.chdir(chromium_src_root)cmd = "gn desc " + out_dir + " " + target_nameoutputs = os.popen(cmd).readlines()source_start = Falseall_headers = []public_start = Falsepublic_headers = []for output_line in outputs:output_line = output_line.strip()if output_line.startswith("sources"):source_start = Truecontinueelif source_start and len(output_line) == 0:source_start = Falsecontinueelif source_start and output_line.endswith(".h"):output_line = output_line[1:]all_headers.append(output_line)elif output_line == "public":public_start = Truecontinueelif public_start and len(output_line) == 0:public_start = Falsecontinueelif public_start:public_headers.append(output_line)if len(public_headers) == 1:public_headers = all_headersif len(public_headers) > 1:copy_all_files(chromium_src_root, public_headers, target_dir=target_root_path)此外,前面的提取過程會遺漏一些必須的頭文件。主要是如下幾個:
base/callback_forward.h base/message_loop/timer_slack.h base/files/file.h net/cert/cert_status_flags_list.h net/cert/cert_type.h net/base/privacy_mode.h net/websockets/websocket_event_interface.h net/quic/quic_alarm_factory.h對于這些文件,我們直接從chromium的代碼庫拷貝到我們的工程中對應的位置即可。
我們還需要引入chromium的build配置頭文件build/build_config.h。直接將chromium代碼庫中的對應文件拷貝過來,放到對應的位置。
將app/src/main/jni/third_party/chromium/include/base/gtest_prod_util.h文件中對testing/gtest/include/gtest/gtest_prod.h的include注釋掉,同時修改FRIEND_TEST_ALL_PREFIXES宏的定義為:
#if 0 #define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \FRIEND_TEST(test_case_name, test_name); \FRIEND_TEST(test_case_name, DISABLED_##test_name); \FRIEND_TEST(test_case_name, FLAKY_##test_name) #else #define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) #endif這樣就可以注釋掉類定義中專門為gtest插入的代碼。
Chromium net的簡單使用
參照chromium/src/net/tools/get_server_time/get_server_time.cc的代碼,來編寫簡單的demo程序。
首先是JNI的Java層代碼:
package com.example.hanpfei0306.myapplication;public class NetUtils {static {System.loadLibrary("neteasenet");}private static native void nativeSendRequest(String url);public static void sendRequest(String url) {nativeSendRequest(url);} }然后是JNI的native實現,app/src/main/jni/src/NetJni.cpp:
// // Created by hanpfei0306 on 16-8-4. //#include <stdio.h> #include <net/base/network_delegate_impl.h>#include "jni.h"#include "base/at_exit.h" #include "base/json/json_writer.h" #include "base/message_loop/message_loop.h" #include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/values.h" #include "net/http/http_response_headers.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request.h"#include "JNIHelper.h"#define TAG "NetUtils"// Simply quits the current message loop when finished. Used to make // URLFetcher synchronous. class QuitDelegate : public net::URLFetcherDelegate { public:QuitDelegate() {}~QuitDelegate() override {}// net::URLFetcherDelegate implementation.void OnURLFetchComplete(const net::URLFetcher* source) override {LOGE("OnURLFetchComplete");base::MessageLoop::current()->QuitWhenIdle();int responseCode = source->GetResponseCode();const net::URLRequestStatus status = source->GetStatus();if (status.status() != net::URLRequestStatus::SUCCESS) {LOGW("Request failed with error code: %s", net::ErrorToString(status.error()).c_str());return;}const net::HttpResponseHeaders* const headers = source->GetResponseHeaders();if (!headers) {LOGW("Response does not have any headers");return;}size_t iter = 0;std::string header_name;std::string date_header;while (headers->EnumerateHeaderLines(&iter, &header_name, &date_header)) {LOGW("Got %s header: %s\n", header_name.c_str(), date_header.c_str());}std::string responseStr;if(!source->GetResponseAsString(&responseStr)) {LOGW("Get response as string failed!");}LOGI("Content len = %lld, response code = %d, response = %s",source->GetReceivedResponseContentLength(),source->GetResponseCode(),responseStr.c_str());}void OnURLFetchDownloadProgress(const net::URLFetcher* source,int64_t current,int64_t total) override {LOGE("OnURLFetchDownloadProgress");}void OnURLFetchUploadProgress(const net::URLFetcher* source,int64_t current,int64_t total) override {LOGE("OnURLFetchUploadProgress");}private:DISALLOW_COPY_AND_ASSIGN(QuitDelegate); };// NetLog::ThreadSafeObserver implementation that simply prints events // to the logs. class PrintingLogObserver : public net::NetLog::ThreadSafeObserver { public:PrintingLogObserver() {}~PrintingLogObserver() override {// This is guaranteed to be safe as this program is single threaded.net_log()->DeprecatedRemoveObserver(this);}// NetLog::ThreadSafeObserver implementation:void OnAddEntry(const net::NetLog::Entry& entry) override {// The log level of the entry is unknown, so just assume it maps// to VLOG(1).const char* const source_type = net::NetLog::SourceTypeToString(entry.source().type);const char* const event_type = net::NetLog::EventTypeToString(entry.type());const char* const event_phase = net::NetLog::EventPhaseToString(entry.phase());std::unique_ptr<base::Value> params(entry.ParametersToValue());std::string params_str;if (params.get()) {base::JSONWriter::Write(*params, ¶ms_str);params_str.insert(0, ": ");} #ifdef DEBUG_ALLLOGI("source_type = %s (id = %u): entry_type = %s : event_phase = %s params_str = %s",source_type, entry.source().id, event_type, event_phase, params_str.c_str()); #endif}private:DISALLOW_COPY_AND_ASSIGN(PrintingLogObserver); };// Builds a URLRequestContext assuming there's only a single loop. static std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(net::NetLog *net_log) {net::URLRequestContextBuilder builder;builder.set_net_log(net_log); //#if defined(OS_LINUX)// On Linux, use a fixed ProxyConfigService, since the default one// depends on glib.//// TODO(akalin): Remove this once http://crbug.com/146421 is fixed.builder.set_proxy_config_service(base::WrapUnique(new net::ProxyConfigServiceFixed(net::ProxyConfig()))); //#endifstd::unique_ptr<net::URLRequestContext> context(builder.Build());context->set_net_log(net_log);return context; }static void NetUtils_nativeSendRequest(JNIEnv* env, jclass, jstring javaUrl) {const char* native_url = env->GetStringUTFChars(javaUrl, NULL);LOGW("Url: %s", native_url);base::AtExitManager exit_manager;LOGW("Url: %s", native_url);GURL url(native_url);if (!url.is_valid() || (url.scheme() != "http" && url.scheme() != "https")) {LOGW("Not valid url: %s", native_url);return;}LOGW("Url: %s", native_url);base::MessageLoopForIO main_loop;QuitDelegate delegate;std::unique_ptr<net::URLFetcher> fetcher =net::URLFetcher::Create(url, net::URLFetcher::GET, &delegate);net::NetLog *net_log = nullptr; #ifdef DEBUG_ALLnet_log = new net::NetLog;PrintingLogObserver printing_log_observer;net_log->DeprecatedAddObserver(&printing_log_observer,net::NetLogCaptureMode::IncludeSocketBytes()); #endifstd::unique_ptr<net::URLRequestContext> url_request_context(BuildURLRequestContext(net_log));fetcher->SetRequestContext(// Since there's only a single thread, there's no need to worry// about when the URLRequestContext gets created.// The URLFetcher will take a reference on the object, and hence// implicitly take ownership.new net::TrivialURLRequestContextGetter(url_request_context.get(),main_loop.task_runner()));fetcher->Start();// |delegate| quits |main_loop| when the request is done.main_loop.Run();env->ReleaseStringUTFChars(javaUrl, native_url); }int jniRegisterNativeMethods(JNIEnv* env, const char *classPathName, JNINativeMethod *nativeMethods, jint nMethods) {jclass clazz;clazz = env->FindClass(classPathName);if (clazz == NULL) {return JNI_FALSE;}if (env->RegisterNatives(clazz, nativeMethods, nMethods) < 0) {return JNI_FALSE;}return JNI_TRUE; }static JNINativeMethod gNetUtilsMethods[] = {NATIVE_METHOD(NetUtils, nativeSendRequest, "(Ljava/lang/String;)V"), };void register_com_netease_volleydemo_NetUtils(JNIEnv* env) {jniRegisterNativeMethods(env, "com/example/hanpfei0306/myapplication/NetUtils",gNetUtilsMethods, NELEM(gNetUtilsMethods)); }// DalvikVM calls this on startup, so we can statically register all our native methods. jint JNI_OnLoad(JavaVM* vm, void*) {JNIEnv* env;if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {LOGE("JavaVM::GetEnv() failed");abort();}register_com_netease_volleydemo_NetUtils(env);return JNI_VERSION_1_6; }這個文件里,在nativeSendRequest()函數中調用chromium net執行網絡請求,獲取響應,并打印出響應的headers及content。
配置Gradle
要在Android Studio中使用JNI,還需要對Gralde做一些配置文。這里需要對MyApplication/build.gradle、MyApplication/gradle/wrapper/gradle-wrapper.properties,和MyApplication/app/build.gradle這幾個文件做修改。
修改MyApplication/build.gradle文件,最終的內容為:
// Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript {repositories {jcenter()}dependencies {classpath 'com.android.tools.build:gradle-experimental:0.7.0'// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files} }allprojects {repositories {jcenter()} }task clean(type: Delete) {delete rootProject.buildDir }在這個文件中配置gradle插件的版本為gradle-experimental:0.7.0。
修改MyApplication/gradle/wrapper/gradle-wrapper.properties文件,最終的內容為:
#Mon Dec 28 10:00:20 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip在這個文件中配置gradle的版本。
修改MyApplication/app/build.gradle文件,最終的內容為:
apply plugin: 'com.android.model.application'model {repositories {libs(PrebuiltLibraries) {chromium_net {headers.srcDir "src/main/jni/third_party/chromium/include"binaries.withType(SharedLibraryBinary) {sharedLibraryFile = file("src/main/jni/third_party/chromium/libs/${targetPlatform.getName()}/libnet.cr.so")}}chromium_base {headers.srcDir "src/main/jni/third_party/chromium/include"binaries.withType(SharedLibraryBinary) {sharedLibraryFile = file("src/main/jni/third_party/chromium/libs/${targetPlatform.getName()}/libbase.cr.so")}}chromium_url {headers.srcDir "src/main/jni/third_party/chromium/include"binaries.withType(SharedLibraryBinary) {sharedLibraryFile = file("src/main/jni/third_party/chromium/libs/${targetPlatform.getName()}/liburl.cr.so")}}}}android {compileSdkVersion 23buildToolsVersion "23.0.3"defaultConfig {applicationId "com.example.hanpfei0306.myapplication"minSdkVersion.apiLevel 19targetSdkVersion.apiLevel 21versionCode 1versionName "1.0"}ndk {moduleName "neteasenet"toolchain "clang"CFlags.addAll(['-I' + file('src/main/jni/third_party/chromium/include/'),])cppFlags.addAll(["-std=gnu++11", ])cppFlags.addAll(["-DV8_DEPRECATION_WARNINGS","-DENABLE_NOTIFICATIONS","-DENABLE_BROWSER_CDMS","-DENABLE_PRINTING=1","-DENABLE_BASIC_PRINTING=1","-DENABLE_SPELLCHECK=1","-DUSE_BROWSER_SPELLCHECKER=1","-DUSE_OPENSSL_CERTS=1","-DNO_TCMALLOC","-DUSE_EXTERNAL_POPUP_MENU=1","-DDISABLE_NACL","-DENABLE_SUPERVISED_USERS=1","-DCHROMIUM_BUILD","-D_FILE_OFFSET_BITS=64","-DANDROID","-DHAVE_SYS_UIO_H","-D__STDC_CONSTANT_MACROS","-D__STDC_FORMAT_MACROS","-D_FORTIFY_SOURCE=2","-DCOMPONENT_BUILD","-D__GNU_SOURCE=1","-D_DEBUG","-DDYNAMIC_ANNOTATIONS_ENABLED=1","-DWTF_USE_DYNAMIC_ANNOTATIONS=1","-DDLOPEN_KERBEROS","-DNET_IMPLEMENTATION","-DUSE_KERBEROS","-DENABLE_BUILT_IN_DNS","-DPOSIX_AVOID_MMAP","-DENABLE_WEBSOCKETS","-DGOOGLE_PROTOBUF_NO_RTTI","-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER","-DHAVE_PTHREAD","-DPROTOBUF_USE_DLLS","-DBORINGSSL_SHARED_LIBRARY","-DU_USING_ICU_NAMESPACE=0","-DU_ENABLE_DYLOAD=0",])cppFlags.addAll(['-I' + file('src/main/jni/third_party/chromium/include'), ])ldLibs.add("android")ldLibs.add("log")ldLibs.add("z")stl "c++_shared"}sources {main {java {source {srcDir "src/main/java"}}jni {source {srcDirs = ["src/main/jni",]}dependencies {library 'chromium_base' linkage 'shared'library 'chromium_url' linkage 'shared'library 'chromium_net' linkage 'shared'}}jniLibs {source {srcDirs =["src/main/jni/third_party/chromium/libs",]}}}}buildTypes {debug {ndk {abiFilters.add("armeabi")abiFilters.add("armeabi-v7a")}}release {minifyEnabled falseproguardFiles.add(file("proguard-rules.pro"))ndk {abiFilters.add("armeabi")abiFilters.add("armeabi-v7a")}}}} } dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:23.4.0' }關鍵點主要有如下這些:
- 為net、base和url這幾個模塊創建PrebuiltLibraries libs元素,并正確的設置對這些模塊的依賴。
- 配置stl為"c++_shared"。
- cppFlags的"-std=gnu++11"選項必不可少。
- buildType下的debug和release,需要給它們ndk的abiFilters添加我們想要支持的ABI,而不是留空,以防止Android Studio為我們編譯我們不打算支持的ABI的so,而出現找不到文件的問題。
- CFlags和cppFlags中除了配置頭文件搜索路徑的那兩行之外,其它的內容,主要是從chromium的構建環境中提取的。方法為:
主要是build.gradle的cppFlags添加的那些宏定義,它們來自defines。如果這些配置,在編譯chromium net so的環境,和構建我們的工程的環境之間存在差異,則很可能會導致運行期一些莫名奇妙的問題,比如意外的緩沖區溢出之類的。
Cronet移植
如我們前面提到的,Chromium已經有提供一個稱為cronet的模塊,封裝chromium net,提供Java接口。使用這個模塊將大大簡化我們的移植工作。構建cronet所需步驟主要有:
- 基于前面看到的配置文件out/Default/args.gn,編輯該文件將is_component_build配置選項置為false。
-
執行如下命令:
$ gn gen out/Default產生ninja構建所需的 .ninja 文件。
-
執行如下命令生成cronet so文件:
$ ninja -C out/Default/ cronet并將產生的libcronet.so文件導入我們的應用中。我們的應用的build.gradle將如下面這樣:
-
執行如下命令生成cronet Java層代碼的jar包:
$ ninja -C out/Default/ cronet_java這將在out/Default/lib.java/的子目錄下產生出多個jar文件,cronet_java.jar,cronet_api.jar,url_java.jar,base_java.jar,net_java.jar。將這些jar文件全部導入我們的應用中。
-
調用cronet提供的Java接口執行網絡請求:
我們有專門將Chromium net及其依賴的模塊,還有構建工具鏈,從Chromium的代碼庫中抽離出來,形成一個單獨的repo,放在git上。有興趣的朋友可以拿來玩一下。
Done。
總結
以上是生活随笔為你收集整理的懒人chromium net android移植指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: chromium net android
- 下一篇: 在Android中使用FlatBuffe