不可或缺 Windows Native (25) - C++: windows app native, android app native, ios app native
作者:webabcd
介绍
不可或缺 Windows Native 之 C++
- windows app native
- android app native
- ios app native
示例
一、演示 windows app native 开发
1、native 层
CppCx.h
#pragma once #include <string> using namespace std; namespace NativeDll { class CppCx { public: string Hello(string name); }; }
CppCx.cpp
/* * 演示 C#, C++/CX, C/C++ 间的通信 * * 本例是 C/C++ 部分 */ #include "pch.h" #include "CppCx.h" #include "DemoCx.h" using namespace NativeDll; string CppCx::Hello(string name) { // C/C++ 通过 C++/CX 调用 C# if (DemoCx::GlobalCallback != nullptr) DemoCx::GlobalCallback->Cx2Cs("c/c++ to c++/cx to cs"); return "hello: " + name; }
2、C++/CX 层
DemoCx.h
#pragma once #include "ICallback.h" using namespace Platform; namespace NativeDll { // ref class 可被输出到元数据(winmd - Windows Metadata),以便其他托管程序调用 public ref class DemoCx sealed { public: // 用“^”标记的,系统会负责他们的引用计数,当引用计数为 0 时,它们会被销毁 String^ HelloCx(String^ name); String^ HelloCpp(String^ name); // 由 C# 调用,用于设置 ICallback 对象 void SetCallback(ICallback^ callback); // 由 C++/CX 调用,用于通过 ICallback 向 C# 发送数据 property static ICallback^ GlobalCallback; }; }
DemoCx.cpp
/* * 演示 C#, C++/CX, C/C++ 间的通信 * * 本例是 C++/CX 部分 * * 为了支持 Windows Runtime Component 这种方式,所以引入 Microsoft created the Visual C++ component extensions (C++/CX),可以将其看作是连接“调用者”和“C/C++”之间的桥梁,元数据是 windows metadata (.winmd) files * 为了让“调用者”调用 Windows Runtime Component,所以 C++/CX 会有自己的一些数据类型,比如字符串是 Platform::String^ 类型的,这样才能让“调用者”调用 * 关于 C++/CX 的相关知识请参见:https://msdn.microsoft.com/en-us/library/hh755822.aspx */ #include "pch.h" #include "DemoCx.h" #include "CppCx.h" #include "cppHelper.h" using namespace NativeDll; String^ DemoCx::HelloCx(String^ name) { // 如果 C# 端设置了 ICallback 对象,则可以在 C++/Cx 端向 C# 端发送数据 if (GlobalCallback != nullptr) GlobalCallback->Cx2Cs("c++/cx to cs"); return "hello: " + name; } // 由 C# 调用,用于设置 ICallback 对象 void DemoCx::SetCallback(ICallback^ callback) { GlobalCallback = callback; } String^ DemoCx::HelloCpp(String^ name) { // C++/CX 与 C/C++ 通信时,如果要传递字符串,则要对字符串做转换 string cppName = ws2s_3(std::wstring(name->Data())); // C++/CX 调用 C/C++ CppCx cppCx; string cppResult = cppCx.Hello(cppName); String^ cxResult = ref new Platform::String(s2ws_3(cppResult).c_str()); return cxResult; }
3、托管代码层
Cx.xaml
<Page x:Class="NativeDemo.Demo.Cx" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:NativeDemo.Demo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" TextAlignment="Left" FontSize="24.667" TextWrapping="Wrap" /> </StackPanel> </Grid> </Page>
Cx.xaml.cs
/* * 演示 C#, C++/CX, C/C++ 间的通信 * * 本例是 C# 部分 * * * C# 与 C++/CX 间通信;C++/CX 与 C/C++ 间通信;C# 通过 C++/CX 与 C/C++ 间通信 */ using System; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace NativeDemo.Demo { public sealed partial class Cx : Page { public Cx() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { NativeDll.DemoCx demoCx = new NativeDll.DemoCx(); MyCallback myCallback = new MyCallback(); myCallback.MessageReceived += myCallback_MessageReceived; demoCx.SetCallback(myCallback); // C# 调用 C++/CX lblMsg.Text += demoCx.HelloCx("cs to c++/cx"); lblMsg.Text += Environment.NewLine; // C# 通过 C++/CX 调用 C/C++ lblMsg.Text += demoCx.HelloCpp("cs to c++/cx to c/c++"); lblMsg.Text += Environment.NewLine; } async void myCallback_MessageReceived(object sender, MessageEventArgs e) { MyCallback myCallback = (MyCallback)sender; await lblMsg.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { lblMsg.Text += e.Message; lblMsg.Text += Environment.NewLine; }); } } // 实现 C++/CX 中的 ICallback 接口 public class MyCallback : NativeDll.ICallback { // 收到 C++/CX 直接发送过来的数据,或者 C/C++ 通过 C++/CX 发送过来的数据 public void Cx2Cs(string message) { OnMessageReceived(new MessageEventArgs { Message = message }); } public event EventHandler<MessageEventArgs> MessageReceived; protected virtual void OnMessageReceived(MessageEventArgs e) { EventHandler<MessageEventArgs> handler = MessageReceived; if (handler != null) handler(this, e); } } public class MessageEventArgs : EventArgs { public string Message { get; set; } } }
二、演示 android app native 开发
1、native 层(C 语言)
cHello.h
#ifndef _MYHEAD_CHELLO_ #define _MYHEAD_CHELLO_ #ifdef __cplusplus extern "C" { #endif char *hello(const char *name); #ifdef __cplusplus } #endif #endif
cHello.c
#include "cHello.h" #include <stdlib.h> char *str_concat2(const char *, const char *); char *hello(const char *name) { return str_concat2("hello: ", name); } char *str_concat2(const char *str1, const char *str2) { char *result; result = (char *)malloc(strlen(str1) + strlen(str2) + 1); if (!result) { exit(EXIT_FAILURE); } strncpy(result, str1, strlen(str1) + 1); strncat(result, str2, strlen(str1) + strlen(str2) + 1); return result; }
2、native 层(C++)
CppHello.h
#ifndef _MYHEAD_CPPHELLO_ #define _MYHEAD_CPPHELLO_ #include <string> using namespace std; namespace MyNs { class CppHello { public: string Hello(string name); }; } #endif
CppHello.cpp
#include "CppHello.h" using namespace MyNs; string CppHello::Hello(string name) { return "hello: " + name; }
3、jni 层
jniDemo.h
#include <jni.h> #ifndef _Included_com_cnblogs_webabcd_jniDemo #define _Included_com_cnblogs_webabcd_jniDemo #ifdef __cplusplus extern "C" { #endif // 注意函数名的命名规则 JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniCpp(JNIEnv *env, jobject obj, jstring name); JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniC(JNIEnv *env, jobject obj, jstring name); #ifdef __cplusplus } #endif #endif
jniDemo.cpp
/* * jni(Java Native Interface) - 详细文档参见 http://docs.oracle.com/javase/7/docs/technotes/guides/jni/ * ndk(Native Development Kit) - 下载 ndk 后,其目录内有详细的文档 * cygwin - 在 windows 平台上运行的类 UNIX 模拟环境,可以调用 ndk 编译 so * * * 为了使 jni 能支持 c++ 需要这么做: * 1、将本文件的后缀名从 .c 修改为 .cpp(c++ 文件的扩展名可以通过 Android.mk 的 LOCAL_CPP_EXTENSION 指定) * 2、按本例的方式配置 Application.mk 文件(如果只想支持 c 语言的话,则可以不要此文件) */ #include "jniDemo.h" #include "CppHello.h" #include "cHello.h" void jni2java(JNIEnv *); // 对应 MainActivity 类中的 public native String helloJniCpp(String name); 注意函数的命名规则 JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniCpp(JNIEnv *env, jobject obj, jstring name) { jni2java(env); MyNs::CppHello cppHello; const char *charName = env->GetStringUTFChars(name, 0); // jstring to char std::string stringName(charName); // jstring to string std::string stringResult = cppHello.Hello(stringName); const char *charResult = stringResult.data(); // string to char jstring jstringResult = env->NewStringUTF(charResult); // char to jstring return jstringResult; /* * 调用 jni 函数时注意(对于 C 和 CPP 来说,JNIEnv 的含义不同,具体请查看文档): * 1、C 的用法示例:jstring jstringResult = (*env)->NewStringUTF(env, charResult); // char to jstring * 2、CPP 的用法示例:jstring jstringResult = env->NewStringUTF(charResult); // char to jstring */ } // 对应 MainActivity 类中的 public native String helloJniC(String name); 注意函数的命名规则 JNIEXPORT jstring JNICALL Java_com_example_androidnative_MainActivity_helloJniC(JNIEnv *env, jobject obj, jstring name) { jni2java(env); const char *charName = env->GetStringUTFChars(name, 0); // jstring to char char *charResult = hello(charName); jstring jstringResult = env->NewStringUTF(charResult); // char to jstring free(charResult); return jstringResult; } // 调用 MainActivity 类中的 public static void helloJava(String message); 函数 void jni2java(JNIEnv *env) { const char *className = "com/example/androidnative/MainActivity"; // 注意类名规则 jclass cla = env->FindClass(className); // 第三个参数中:(Ljava/lang/String;)代表 java 中的被调用的函数的参数是 String 类型;V 代表 java 中的被调用的函数的返回值是 void 类型 jmethodID method = env->GetStaticMethodID(cla, "helloJava", "(Ljava/lang/String;)V"); jstring result = env->NewStringUTF("jni to java"); env->CallStaticVoidMethod(cla, method, result); }
编译相关
Application.mk
APP_STL := stlport_static #以静态链接的方式使用stlport版本的STL APP_CPPFLAGS := -fexceptions -frtti #允许异常功能,及运行时类型识别 APP_CPPFLAGS +=-std=c++11 #允许使用c++11的函数等功能 APP_CPPFLAGS +=-fpermissive #此项有效时表示宽松的编译形式,比如没有用到的代码中有错误也可以通过编
Android.mk
LOCAL_PATH := $(call my-dir) #模块1 include $(CLEAR_VARS) #清除 LOCAL_MODULE, LOCAL_SRC_FILES 之类的变量 LOCAL_CPP_EXTENSION := .cpp # C++ 文件的扩展名 LOCAL_MODULE := jniDemo # 模块名。如果模块名为“abc”,则此模块将会生成“libabc.so”文件。 LOCAL_SRC_FILES := jniDemo.cpp CppHello.cpp cHello.c # 需要编译的源文件 include $(BUILD_SHARED_LIBRARY) # 编译当前模块 #模块2
4、托管代码层
MainActivity.java
/* * 演示 java 如何通过 jni 与 C/C++ 互相通信 */ package com.example.androidnative; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { private static TextView txtMsg; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtMsg = (TextView) this.findViewById(R.id.txtMsg); // 加载 so System.loadLibrary("jniDemo"); // java 调用 jni, c String resultC = helloJniC("java to jni to c"); txtMsg.append(resultC); txtMsg.append(" "); // java 调用 jni, c++ String resultCpp = helloJniCpp("java to jni to c++"); txtMsg.append(resultCpp); txtMsg.append(" "); } // native function(对应的 jni 函数参见 jniDemo.cpp) public native String helloJniC(String name); public native String helloJniCpp(String name); // jni 调用 java public static void helloJava(String message) { txtMsg.append(message); txtMsg.append(" "); } }
三、演示 ios app native 开发(无论是 oc 还是 swift 都是 native 开发,本例演示 oc, c, c++ 混编)
ViewController.h
// // ViewController.h // IosNative // // Created by wanglei on 4/24/15. // Copyright (c) 2015 webabcd. All rights reserved. // #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
ViewController.mm
/* * 演示 objective-c 如何与 C/C++ 互相通信 * * objective-c 是面向对象的 c 语言,本身就是 Native 的,完全兼容 c 语言,可以与 C/C++ 混编 * * 注:为了支持 C++ 需要把本文件的后缀名由 .m 修改为 .mm */ #import "ViewController.h" #include <string> @interface ViewController () @end @implementation ViewController char *hello(const char *); class CppHello; - (void)viewDidLoad { [super viewDidLoad]; [self helloC:@"oc to c"]; [self helloCpp:@"oc to c++"]; } // oc 调用 c - (void)helloC:(NSString *)name { UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, 200, 20)]; [self.view addSubview:label]; const char *charName = [name UTF8String]; // nsstring to char char *charResult = hello(charName); NSString *nsstringResult = [[NSString alloc] initWithCString:charResult encoding:NSUTF8StringEncoding]; // char to nsstring free(charResult); label.text = nsstringResult; } // oc 调用 c++ - (void)helloCpp:(NSString *)name { UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 200, 20)]; [self.view addSubview:label]; string stringName = [name UTF8String]; // nsstring to string MyNs::CppHello cppHello; string stringResult = cppHello.Hello(stringName); NSString *nsstringResult = [[NSString alloc] initWithCString:stringResult.c_str() encoding:NSUTF8StringEncoding]; // string to nsstring label.text = nsstringResult; } char *hello(const char *name) { // c 调用 oc(别忘了 #import <Foundation/Foundation.h>,本例中不用是因为 UIViewController 已经导入这个头文件了) NSLog(@"c to oc"); char *s = "hello: "; char *result; result = (char *)malloc(strlen(s) + strlen(name) + 1); if (!result) exit(EXIT_FAILURE); strncpy(result, s, strlen(s) + 1); strncat(result, name, strlen(s) + strlen(name) + 1); return result; } using namespace std; namespace MyNs { class CppHello { public: string Hello(string name); }; } string MyNs::CppHello::Hello(string name) { // c++ 调用 oc(别忘了 #import <Foundation/Foundation.h>,本例中不用是因为 UIViewController 已经导入这个头文件了) NSLog(@"c++ to oc"); return "hello: " + name; } @end
OK
[源码下载]