大家查資料還是得去官網(wǎng),官網(wǎng)上還是挺全的,不要浪費(fèi)時(shí)間看別人的教程。官網(wǎng)鏈接在最底下參考鏈接里
今天要講的這個(gè)過(guò)程是一個(gè)我大概花了一周時(shí)間才解決的問(wèn)題:在windows10系統(tǒng)當(dāng)中如何把protobuf庫(kù)使用C++的寫法在Android Studio項(xiàng)目當(dāng)中進(jìn)行調(diào)用。
網(wǎng)上大部分的教程都是關(guān)于如何用ndk編譯protobuf的,或者只是單獨(dú)如何將一個(gè)普通的c++編譯成.so文件鏈接到Android Studio項(xiàng)目上的(如果這是linux系統(tǒng)就簡(jiǎn)單地多了...)。
在這里首先有一個(gè)坑要注意:ndk編譯跟cmake編譯是不兼容的
其實(shí)android studio可以直接支持protobuf的java編譯,在plugin里有protobuf-support下載然后gradle里配置一下就可以了,但是我不會(huì)寫java,而且這個(gè)插件下載要翻墻,所以就沒(méi)有選擇這個(gè)方案。
ndk編譯protobuf總共分為以下幾步:
1.通過(guò)cmake_gui生成vs2019所需文件
2.vs2019編譯protobuf生成protoc
3.使用ndk編譯protobuf生成.so文件
4.自己寫一個(gè)proto文件,然后調(diào)用protobuf包當(dāng)中的protoc來(lái)生成文件
1.通過(guò)cmake_gui生成protoc
首先下載cmake和protobuf的源碼,下載地址:
cmake:https://cmake.org/download/選binary distribution里面的msi文件,可以直接運(yùn)行的。
protobuf https://github.com/protocolbuffers/protobuf下載下來(lái)解壓
安裝好cmake之后,打開cmake_gui這個(gè)可執(zhí)行文件,然后把要源碼的地址和要輸出文件的地址選好(最好就放在protobuf這個(gè)層級(jí)底下)。

這個(gè)時(shí)候點(diǎn)configure,它會(huì)讓你選擇哪個(gè)編譯版本,這個(gè)時(shí)候依據(jù)你選擇的編譯器進(jìn)行更改,比如通用的vs,我下載的是2019版本,那就選擇vs2019,然后點(diǎn)擊確定。
這個(gè)時(shí)候一般有可能會(huì)出現(xiàn)一個(gè)問(wèn)題,因?yàn)槲覀冊(cè)谙螺d源碼的時(shí)候是不帶test項(xiàng)目的,所以會(huì)缺一些文件,但是對(duì)本體protobuf沒(méi)有影響,可以改變產(chǎn)生cmake文件的選項(xiàng)來(lái)略過(guò)這個(gè)問(wèn)題。

這樣就生成好了vs2019編譯所需的cmake文件和sln文件了。
2.vs2019編譯protobuf
首先,下載vs2019:vs2019(不太清楚需不需要翻墻,選擇community社區(qū)版下載安裝。
按照默認(rèn)配置安裝就可以了,安裝好了之后運(yùn)行,選擇打開項(xiàng)目或解決方案,點(diǎn)擊到之前生成好的文件夾當(dāng)中,選擇后綴為.sln的文件進(jìn)行打開(這代表是vs2019的一個(gè)項(xiàng)目)。

然后再點(diǎn)擊這個(gè)按鈕就可以進(jìn)行編譯了。

在編譯好了之后進(jìn)到那個(gè)文件夾當(dāng)中,進(jìn)入Debug就可以看到protoc這個(gè)文件了,這個(gè)文件我們之后會(huì)用到。

3.使用ndk編譯protobuf生成.so文件
有些人可能會(huì)覺(jué)得很奇怪,之前不是編譯過(guò)了,怎么還要編譯。在vs2019里面編譯出
來(lái)的是dll庫(kù),也是動(dòng)態(tài)鏈接庫(kù),在安卓上用不了,所以我們需要使用在android studio上的ndk插件來(lái)進(jìn)行編譯生成.so的庫(kù)。
首先我們要新建一個(gè)flutter項(xiàng)目,如果有疑問(wèn)可以看flutter中文網(wǎng)
我們將protobuf/src/下面的google復(fù)制,放在android-app-src-main-jni目錄下。

首先更改android-app-build.gradle,在android的層級(jí)下加入下面的程序。
externalNativeBuild {
ndkBuild {
// Tells Gradle to put outputs from external native
// builds in the path specified below.
path './src/main/jni/Android.mk'
buildStagingDirectory "./outputs/ndk-build"
}
}
這個(gè)代表使用原生的編譯方式,調(diào)用這個(gè)Android.mk,調(diào)用這個(gè)的同時(shí)也會(huì)調(diào)用跟他同一級(jí)目錄的Application.mk。
接下來(lái)編寫這個(gè)Android.mk和Application.mk,這些文件的詳情參考鏈接里有。
Andorid.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := protobuf
LOCAL_CFLAGS := -std=c++11 -fexceptions -frtti
LOCAL_MODULE_FILENAME := libprotobuf
LOCAL_SRC_FILES := google/protobuf/stubs/bytestream.cc \
google/protobuf/stubs/bytestream.h \
google/protobuf/stubs/common.cc \
google/protobuf/stubs/hash.h \
google/protobuf/stubs/int128.cc \
google/protobuf/stubs/int128.h \
google/protobuf/io/io_win32.cc \
google/protobuf/stubs/map_util.h \
google/protobuf/stubs/mathutil.h \
google/protobuf/stubs/status.cc \
google/protobuf/stubs/status.h \
google/protobuf/stubs/status_macros.h \
google/protobuf/stubs/statusor.cc \
google/protobuf/stubs/statusor.h \
google/protobuf/stubs/stringpiece.cc \
google/protobuf/stubs/stringpiece.h \
google/protobuf/stubs/stringprintf.cc \
google/protobuf/stubs/stringprintf.h \
google/protobuf/stubs/structurally_valid.cc \
google/protobuf/stubs/strutil.cc \
google/protobuf/stubs/time.cc \
google/protobuf/stubs/time.h \
google/protobuf/any_lite.cc \
google/protobuf/arena.cc \
google/protobuf/extension_set.cc \
google/protobuf/generated_enum_util.cc \
google/protobuf/generated_message_util.cc \
google/protobuf/generated_message_table_driven_lite.h \
google/protobuf/generated_message_table_driven_lite.cc \
google/protobuf/implicit_weak_message.cc \
google/protobuf/message_lite.cc \
google/protobuf/parse_context.cc \
google/protobuf/repeated_field.cc \
google/protobuf/wire_format_lite.cc \
google/protobuf/io/coded_stream.cc \
google/protobuf/io/strtod.cc \
google/protobuf/io/zero_copy_stream.cc \
google/protobuf/io/zero_copy_stream_impl.cc \
google/protobuf/io/zero_copy_stream_impl_lite.cc \
google/protobuf/any.pb.cc \
google/protobuf/api.pb.cc \
google/protobuf/any.cc \
google/protobuf/descriptor.cc \
google/protobuf/descriptor_database.cc \
google/protobuf/descriptor.pb.cc \
google/protobuf/duration.pb.cc \
google/protobuf/dynamic_message.cc \
google/protobuf/empty.pb.cc \
google/protobuf/extension_set_heavy.cc \
google/protobuf/field_mask.pb.cc \
google/protobuf/generated_message_reflection.cc \
google/protobuf/generated_message_table_driven_lite.h \
google/protobuf/generated_message_table_driven.cc \
google/protobuf/map_field.cc \
google/protobuf/message.cc \
google/protobuf/reflection_internal.h \
google/protobuf/reflection_ops.cc \
google/protobuf/service.cc \
google/protobuf/source_context.pb.cc \
google/protobuf/struct.pb.cc \
google/protobuf/stubs/substitute.cc \
google/protobuf/stubs/substitute.h \
google/protobuf/text_format.cc \
google/protobuf/timestamp.pb.cc \
google/protobuf/type.pb.cc \
google/protobuf/unknown_field_set.cc \
google/protobuf/wire_format.cc \
google/protobuf/wrappers.pb.cc \
google/protobuf/io/gzip_stream.cc \
google/protobuf/io/printer.cc \
google/protobuf/io/tokenizer.cc \
google/protobuf/compiler/importer.cc \
google/protobuf/compiler/parser.cc \
google/protobuf/util/delimited_message_util.cc \
google/protobuf/util/field_comparator.cc \
google/protobuf/util/field_mask_util.cc \
google/protobuf/util/internal/constants.h \
google/protobuf/util/internal/datapiece.cc \
google/protobuf/util/internal/datapiece.h \
google/protobuf/util/internal/default_value_objectwriter.cc \
google/protobuf/util/internal/default_value_objectwriter.h \
google/protobuf/util/internal/error_listener.cc \
google/protobuf/util/internal/error_listener.h \
google/protobuf/util/internal/expecting_objectwriter.h \
google/protobuf/util/internal/field_mask_utility.cc \
google/protobuf/util/internal/field_mask_utility.h \
google/protobuf/util/internal/json_escaping.cc \
google/protobuf/util/internal/json_escaping.h \
google/protobuf/util/internal/json_objectwriter.cc \
google/protobuf/util/internal/json_objectwriter.h \
google/protobuf/util/internal/json_stream_parser.cc \
google/protobuf/util/internal/json_stream_parser.h \
google/protobuf/util/internal/location_tracker.h \
google/protobuf/util/internal/mock_error_listener.h \
google/protobuf/util/internal/object_location_tracker.h \
google/protobuf/util/internal/object_source.h \
google/protobuf/util/internal/object_writer.cc \
google/protobuf/util/internal/object_writer.h \
google/protobuf/util/internal/protostream_objectsource.cc \
google/protobuf/util/internal/protostream_objectsource.h \
google/protobuf/util/internal/protostream_objectwriter.cc \
google/protobuf/util/internal/protostream_objectwriter.h \
google/protobuf/util/internal/proto_writer.cc \
google/protobuf/util/internal/proto_writer.h \
google/protobuf/util/internal/structured_objectwriter.h \
google/protobuf/util/internal/type_info.cc \
google/protobuf/util/internal/type_info.h \
google/protobuf/util/internal/type_info_test_helper.cc \
google/protobuf/util/internal/type_info_test_helper.h \
google/protobuf/util/internal/utility.cc \
google/protobuf/util/internal/utility.h \
google/protobuf/util/json_util.cc \
google/protobuf/util/message_differencer.cc \
google/protobuf/util/time_util.cc \
google/protobuf/util/type_resolver_util.cc
LOCAL_EXPORT_C_INCLUDES :=
LOCAL_EXPORT_LDLIBS :=
LOCAL_C_INCLUDES := $(LOCAL_PATH) \
$(LOCAL_PATH)/src
LOCAL_LDLIBS := -llog -lz
include $(BUILD_SHARED_LIBRARY)
#include $(BUILD_STATIC_LIBRARY)
Application.mk
APP_MODULES := protobuf
APP_PLATFORM := android-26
APP_ABI := arm64-v8a
APP_STL := c++_static
APP_OPTIM := debug
這里面主要就是編譯這個(gè)包所需要包含的源文件,我編譯的是整個(gè)protobuf,所以比較多。
具體要編譯的這些文件在protobuf/src/Makefile.am當(dāng)中,你可以根據(jù)自己源碼版本進(jìn)行更改,如果你想編譯protobuf-lite,就去找libprotobuf_lite_la_SOURCES的變量定義復(fù)制過(guò)來(lái)就可以了。
然后生成一個(gè)AVD的模擬器或者是外接一個(gè)android設(shè)備,就可以編譯了。
編譯之后,我們點(diǎn)擊停止(不點(diǎn)擊停止,build不會(huì)刷新),然后在build(注意是根目錄下的build)/app/intermediates/ndkbuild點(diǎn)到底就可以找到libprotobuf.so就說(shuō)明編譯成功了。
到這里protobuf的ndk編譯就完成了,如果想用其他方式調(diào)用這個(gè)protobuf.so也是可以直接調(diào)用的了。
結(jié)語(yǔ)
flutter其實(shí)還是一個(gè)應(yīng)用起來(lái)有一些難度的大前端框架,它的學(xué)習(xí)曲線比較陡峭,需要懂的東西也比較多。由于這個(gè)框架不是非常的穩(wěn)定,經(jīng)常在更新,網(wǎng)上很多可以搜出來(lái)的解決方法有可能已經(jīng)過(guò)時(shí)了,這個(gè)時(shí)候就需要通過(guò)一些經(jīng)驗(yàn)來(lái)自己解決問(wèn)題。
在編寫一個(gè)app的時(shí)候,往往需要前后端結(jié)合,dart雖然是一個(gè)比較通用的語(yǔ)言,但是還是不如C++在后端上的應(yīng)用廣泛,所以如何將寫好的C++庫(kù)鏈接到flutter的項(xiàng)目上確實(shí)是一個(gè)比較重要的過(guò)程。
參考資料
flutter調(diào)用C++ https://blog.csdn.net/guawazi123321/article/details/105099443
這篇文章有一些問(wèn)題,但是具有借鑒意義,它生成出來(lái)的.so文件不能被dart的dynamicLibrary調(diào)用,這個(gè)我們后面會(huì)講到。
深入解讀Cmake http://www.itdecent.cn/p/089b458ab8d5 我們其實(shí)沒(méi)有用到cmake,有興趣的可以自己看一下。
Android studio ndk編譯protobuf生成C++的.so鏈接庫(kù)https://blog.csdn.net/niuben127/article/details/78738671
Android 官網(wǎng)關(guān)于android當(dāng)中安裝ndk的介紹 https://developer.android.com/studio/projects/install-ndk
Android官網(wǎng)關(guān)于使用ndk及預(yù)編譯庫(kù)配置的介紹&Android.mk和Application.mk詳解
https://developer.android.google.cn/ndk/guides/android_mk