移动端跨平台开发一个跨平台的helloworld
Posted zhanghui_cuc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动端跨平台开发一个跨平台的helloworld相关的知识,希望对你有一定的参考价值。
在上一篇文章移动端音视频跨平台开发技术概论中,我们分析了跨平台开发的总体架构。今天我们实际动手,写一个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 <string>
#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 <string.h>
#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 <jni.h>
#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("%s\\n", "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!
项目完整代码扫描下方二维码,回复“跨平台”获取。
欢迎关注我的公众号灰度五十,分享各类音视频、移动开发知识,以及名企内推信息~
以上是关于移动端跨平台开发一个跨平台的helloworld的主要内容,如果未能解决你的问题,请参考以下文章