在上一篇文章移动端音视频跨平台开发技术概论中,我们分析了跨平台开发的总体架构。今天我们实际动手,写一个helloworld项目,这个项目很简单,就是做一个简单的日志库,最终我们希望能在Android和ios手机上打印出一行hello world日志。
项目名字就叫simplest_crossplatfrom_helloworld,简称sch,对应下文代码中的变量、方法名称。
一、编写打印日志的核心代码流程在Android平台上,我们利用jni的log.h进行日志的输出,在ios平台上,则直接利用printf函数,利用一个预编译宏SCH_PLATFORM_ANDROID来区分当前是否在Android平台上,相应地选择是否要包含jni的头文件,以及是否要调用__android_log_print来输出日志,所以最终的cpp代码如下
#include#define LOG_TAG "SCH" #ifdef SCH_PLATFORM_ANDROID #include "android/log.h" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); #endif void sch_log(const char* filename, int line, const char* format, ...) { char buf[1024]; memset(buf, 0, 1024); va_list ap; va_start(ap, format); vsnprintf(buf, 1024, format, ap); va_end(ap); std::string log_string; log_string += "[" + std::string(filename) + "(" + std::to_string(line) + ")] "; log_string += buf; #ifdef SCH_PLATFORM_ANDROID LOGI("%s", log_string.c_str()); #else printf("[%s] %s", LOG_TAG, log_string.c_str()); #endif fflush(stdout); fflush(stderr); }
基于上面的实现,我们定义对应的头文件如下
#ifndef SIMPLEST_CROSSPLATFORM_HELLOWORLD_LOGGER_HPP
#define SIMPLEST_CROSSPLATFORM_HELLOWORLD_LOGGER_HPP
#ifdef __cplusplus
extern "C" {
#endif
#include
#define filename(x) strrchr(x,'/') ? strrchr(x,'/') + 1 : x
void sch_log(const char* filename, int line, const char* format, ...);
#define SCH_LOGI(format, ...) sch_log(filename(__FILE__), __LINE__, format, ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif //SIMPLEST_CROSSPLATFORM_HELLOWORLD_LOGGER_HPP
这样的话,调用者就只需要使用SCH_LOGI这个宏,即可输出日志。
未来我们还可以丰富日志级别,定义SCH_LOGE、SCH_LOGD等等。
二、编写编译工具链根据上一篇文章的架构,我们还需要一套能够为双平台生成静态库、动态库的编译工具链,这里我们选择cmake来构建编译链。
在下面的CMakeList中,我们做了以下几件事
- 根据编译目标的不同,分别添加了SCH_PLATFORM_ANDROID和SCH_PLATFORM_IOS的宏定义对于两个平台,我们都编译名为libschdemo.a的静态库导出了Logger.hpp头文件
cmake_minimum_required(VERSION 3.7.1 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 11)
project(schdemo C CXX)
message(${PROJECT_SOURCE_DIR})
# used for local debug only
#set(CMAKE_SYSTEM_NAME "Android")
#set(TARGET_ABI "arm64-v8a")
#add_definitions(-D __ANDROID__)
##set(CMAKE_SYSTEM_NAME "iOS")
##set(TARGET_ABI "arm64")
##add_definitions(-D __APPLE__)
#set(jni_location "${ANDROID_NDK_HOME}/sysroot/usr/include")
#message(${jni_location})
#include_directories(${jni_location})
if (CMAKE_SYSTEM_NAME MATCHES "Android")
message("building for Android")
add_definitions(-D SCH_PLATFORM_ANDROID)
find_library(android_log_lib log)
elseif (CMAKE_SYSTEM_NAME MATCHES "iOS")
message("building for iOS, arch ${TARGET_ABI}")
add_definitions(-D SCH_PLATFORM_IOS)
endif()
if(CMAKE_ANDROID_NDK)
list(APPEND PLATFORM_LIBS c++abi)
else()
list(APPEND PLATFORM_LIBS ${CMAKE_CXX_IMPLICIT_link_LIBRARIES})
endif()
add_library(schdemo
STATIC
Logger.cpp
Logger.hpp)
set(header_files
Logger.hpp)
if (CMAKE_SYSTEM_NAME MATCHES "Android")
target_link_libraries(schdemo
android
${android_log_lib}
${PLATFORM_LIBS})
elseif (CMAKE_SYSTEM_NAME MATCHES "iOS")
target_link_libraries(schdemo
${PLATFORM_LIBS})
endif()
install(FILES ${header_files} DESTINATION include)
install(TARGETS schdemo
LIBRARY DESTINATION lib # 动态库安装路径
ARCHIVE DESTINATION lib # 静态库安装路径
RUNTIME DESTINATION bin # 可执行文件安装路径
)
有了CMakeList之后,我们在分别编写两个平台对应的编译脚本,Android平台我们就用ndk提供的交叉编译工具链,ios平台我们利用https://github.com/leetal/ios-cmake现成的交叉编译工具链。
Android平台编译脚本basepath=$(cd `dirname $0`; pwd)
echo "${basepath}"
target="arm64-v8a"
if [ $# -gt 0 ];then
target=$1
echo "build target ${target}"
fi
if [ -d ./install/android/"${target}" ];then
rm -rf install/android/"${target}"
fi
if [ -d ./android_build ];then
rm -rf android_build
fi
mkdir android_build
cd android_build
${ANDROID_SDK_HOME}/cmake/3.10.2.4988404/bin/cmake ../sch/
-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake
-DANDROID_ABI="$target"
-DANDROID_PLATFORM=21
-DTARGET_ABI=${target}
-DCMAKE_INSTALL_PREFIX=${basepath}/install/android/"$target"
make -j4
make install
ios平台编译脚本
这里的third_pary/ios-cmake路径就对应前面说的https://github.com/leetal/ios-cmake项目
basepath=$(cd `dirname $0`; pwd)
echo "${basepath}"
target="arm64"
if [ $# -gt 0 ];then
target=$1
echo "build target ${target}"
fi
platform="OS64"
if [ "${target}" = "i386" ];then
platform="SIMULATOR"
elif [ "${target}" = "x86_64" ];then
platform="SIMULATOR64"
elif [ "${target}" = "armv7" ];then
platform="OS"
fi
if [ -d ./install/ios/"${target}" ];then
rm -rf install/ios/"${target}"
fi
if [ -d ./ios_build ];then
rm -rf ios_build
fi
mkdir ios_build
cd ios_build
cmake ../sch/
-DCMAKE_TOOLCHAIN_FILE="${basepath}"/third_party/ios-cmake/ios.toolchain.cmake
-DPLATFORM=${platform}
-DARCHS=${target}
-DTARGET_ABI=${target}
-DENABLE_BITCODE=TRUE
-DCMAKE_INSTALL_PREFIX="${basepath}"/install/ios/"${target}"
make -j4
make install
双端编译脚本
有了各个平台自己的脚本后,为了方便编译,我们再写一个统一编译脚本,其中编译了Android arm64和armv7架构的静态库,编译了ios四个架构的库并利用lipo工具进行了大包,如下
./build_sch_android.sh
./build_sch_android.sh armeabi-v7a
./build_sch_ios.sh
./build_sch_ios.sh armv7
./build_sch_ios.sh i386
./build_sch_ios.sh x86_64
xcrun lipo -create install/ios/arm64/lib/libschdemo.a
install/ios/armv7/lib/libschdemo.a
install/ios/i386/lib/libschdemo.a
install/ios/x86_64/lib/libschdemo.a -output install/ios/libschdemo_uni.a
到这里,我们的编译工具链就打造完毕了。
三、编写应用层API对于Android平台,我们可以封装一个Java日志工具类,通过jni调用前面编译出来的libschdemo.a,来进行日志的打印,设计Java工具类接口如下
public class LogUtil {
static {
System.loadLibrary("c++_shared");
System.loadLibrary("logJNI");
}
public static void i(String msg) {
nativelog(msg);
}
private static native void nativelog(String msg);
}
其中nativelog对应的jni代码如下,非常简单
#include#include "Logger.hpp" extern "C" JNIEXPORT void JNICALL Java_com_example_zhanghui_schdemo_LogUtil_nativelog(JNIEnv *env, jclass clazz, jstring msg) { const char* msg_str = env->GetStringUTFChars(msg, 0); SCH_LOGI("%s", msg_str); env->ReleaseStringUTFChars(msg, msg_str); }
对于ios平台,因为当前这个hello world的case比较简单,我们选择直接引用libschdemo库然后进行调用,示例如下
#import "AppDelegate.h"
#include "Logger.hpp"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
SCH_LOGI("%sn", "Simplest Crossplatfrom Hello World!");
return YES;
}
当然,更详细的demo使用代码就不在这里展示了,比较简单。
至此,跨平台Hello World项目就全部编写完毕,最终我们运行Android和ios平台的demo项目,都能看到如下的日志打印
03-20 20:28:26.661 22755 22755 I SCH : [log_jni.cpp(13)]Simplest CrossPlatform Hello World!
项目完整代码扫描下方二维码,回复“跨平台”获取。
欢迎关注我的公众号灰度五十,分享各类音视频、移动开发知识,以及名企内推信息~



