Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C++ based mobile application

C++ based mobile application

Slides for a talk given during code::dive 2017.

Avatar for Michał Kowalczyk

Michał Kowalczyk

November 15, 2017

More Decks by Michał Kowalczyk

Other Decks in Programming

Transcript

  1. Mobile app Mobile device Android / iOS Android Wear /

    watchOS Android TV / tvOS Android Auto…
  2. Why C++ 75% of top 50 Android apps use it

    Portability Reducing code duplicity Extra libraries (TensorFlow Mobile) Performance Reducing battery usage Obfuscation
  3. Why not C++ Complex technological stack Cost of JNI calls

    Not whole API available Increasing Android apk size
  4. Bridging code - handwritten Small projects Specialised needs Our project

    in TomTom Nearly 35 000 lines of C++ Nearly 10 000 lines of Java
  5. SWIG input - C++ 1 #include <string> 2 3 class

    BusinessLogic 4 { 5 public: 6 std::string helloWorld(); 7 }; 8 9 std::string BusinessLogic::helloWorld() 10 { 11 return "Hello world!"; 12 }
  6. SWIG input - interface definition 1 #ifdef SWIG 2 %module

    business_logic 3 %include "std_string.i" 4 %{ 5 #include "BusinessLogic.hpp" 6 %} 7 #endif 8 9 %include "BusinessLogic.hpp"
  7. SWIG output - C++ 1 SWIGEXPORT jstring JNICALL Java_business_1logicJNI_BusinessLogic_1helloWorld(JNIEnv *jenv,

    2 jclass jcls, 3 jlong jarg1, 4 jobject jarg1_) { 5 jstring jresult = 0 ; 6 BusinessLogic *arg1 = (BusinessLogic *) 0 ; 7 std::string result; 8 9 (void)jenv; 10 (void)jcls; 11 (void)jarg1_; 12 arg1 = *(BusinessLogic **)&jarg1; 13 result = (arg1)->helloWorld(); 14 jresult = jenv->NewStringUTF((&result)->c_str()); 15 return jresult; 16 }
  8. SWIG output - Java 1 public class BusinessLogic { 2

    // ... 3 4 protected void finalize() { 5 delete(); 6 } 7 8 public synchronized void delete() { 9 if (swigCPtr != 0) { 10 if (swigCMemOwn) { 11 swigCMemOwn = false; 12 business_logicJNI.delete_BusinessLogic(swigCPtr); 13 } 14 swigCPtr = 0; 15 } 16 } 17 18 public String helloWorld() { 19 return business_logicJNI.BusinessLogic_helloWorld(swigCPtr, this); 20 } 21 }
  9. Use SWIG generated bridging code 1 public class JavaApp {

    2 public static void main(String argv[]) { 3 System.loadLibrary("business_logic"); 4 final BusinessLogic logic = new BusinessLogic(); 5 System.out.println(logic.helloWorld()); 6 } 7 }
  10. Bridging code generation - Djinni 3 years old Java /

    Objective-C / Python IDL centric
  11. Djinni input - interface definition 1 BusinessLogic = interface +c

    { 2 static create(): BusinessLogic; 3 helloWorld(): string; 4 }
  12. Djinni output - C++ 1 #include <memory> 2 #include <string>

    3 4 class BusinessLogic { 5 public: 6 virtual ~BusinessLogic() {} 7 8 static std::shared_ptr<BusinessLogic> create(); 9 10 virtual std::string helloWorld() = 0; 11 };
  13. Android version API version Reached users Release date 2.3.3-2.3.7 10

    100,0% 2010 4.0.3-4.0.4 15 99,4% 2011 4.1.x 16 98,8% 2012 4.2.x 17 96,5% 2012 4.3 18 93,2% 2012 4.4 19 92,2% 2013 5.0 21 77,7% 2014 5.1 22 71,0% 2014 6.0 23 50,0% 2015 7.0 24 18,0% 2016 7.1 25 2,2% 2016 8.0 26 0,2% 2017
  14. Android manifest file 1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package="pl.ekk.mkk.androidproject"> 3 <application

    4 android:allowBackup="true" 5 android:icon="@mipmap/ic_launcher" 6 android:label="@string/app_name" 7 android:roundIcon="@mipmap/ic_launcher_round" 8 android:supportsRtl="true" 9 android:theme="@style/AppTheme"> 10 <activity android:name=".MainActivity"> 11 <intent-filter> 12 <action android:name="android.intent.action.MAIN"/> 13 <category android:name="android.intent.category.LAUNCHER"/> 14 </intent-filter> 15 </activity> 16 </application> 17 </manifest>
  15. Activity 1 public class MainActivity extends AppCompatActivity { 2 static

    { 3 System.loadLibrary("native-lib"); 4 } 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 setContentView(R.layout.activity_main); 10 11 TextView tv = (TextView) findViewById(R.id.sample_text); 12 tv.setText(stringFromJNI()); 13 } 14 15 public native String stringFromJNI(); 16 }
  16. Native code 1 #include <jni.h> 2 #include <string> 3 4

    extern "C" 5 JNIEXPORT jstring 6 7 JNICALL 8 Java_pl_ekk_mkk_androidproject_MainActivity_stringFromJNI( 9 JNIEnv *env, 10 jobject /* this */) 11 { 12 std::string hello = "Hello from C++"; 13 return env->NewStringUTF(hello.c_str()); 14 }
  17. CMake 1 add_library( native-lib 2 SHARED 3 src/main/cpp/native-lib.cpp ) 4

    5 find_library( log-lib 6 log ) 7 8 target_link_libraries( native-lib 9 ${log-lib} )
  18. ViewController.h 1 #import <UIKit/UIKit.h> 2 3 @interface ViewController : UIViewController

    4 5 @property (weak, nonatomic) IBOutlet UILabel *label; 6 7 @end
  19. ViewController.m 1 #import "ViewController.h" 2 #import "ObjCppWrapper.h" 3 4 @interface

    ViewController () 5 6 @end 7 8 @implementation ViewController 9 10 - (void)viewDidLoad { 11 [super viewDidLoad]; 12 13 ObjCppWrapper* wrapper = [[ObjCppWrapper alloc] init]; 14 _label.text = [wrapper stringFromCpp]; 15 } 16 17 @end
  20. ObjCppWrapper.mm 1 #import <Foundation/Foundation.h> 2 #import "ObjCppWrapper.h" 3 #include "NativeLib.hpp"

    4 5 @implementation ObjCppWrapper { 6 } 7 8 - (nonnull NSString *)stringFromCpp { 9 NativeLib lib; 10 const std::string string = lib.stringFromCpp(); 11 return [[NSString alloc] initWithBytes:string.data() 12 length:static_cast<NSUInteger>(string.size()) 13 encoding:NSUTF8StringEncoding]; 14 } 15 16 @end
  21. NativeLib 1 #include <string> 2 3 class NativeLib 4 {

    5 public: 6 std::string stringFromCpp(); 7 }; 8 9 std::string NativeLib::stringFromCpp() 10 { 11 return "Hello from C++"; 12 }
  22. Advice Find Android / iOS developers Consult C++ boundaries /

    use djinni Understand what you copy-paste from stackoverflow CrystaxNDK Don’t handwrite bridging code Limit ABIs while development; use ccache Use platform specific tools for platform agnostic code Use logging