Compile and use ABI-dependent executable binaries in Android with Android Studio 2.2 and CMake

后端 未结 2 1984
梦如初夏
梦如初夏 2020-12-13 15:28

I\'m testing out the new Android Studio C/C++ building via CMake through stable gradle (http://tools.android.com/tech-docs/external-c-builds).

In my app, an already

相关标签:
2条回答
  • 2020-12-13 16:00
    1. Make the executable files output to where the Android Gradle plugin expects libraries:

      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")

    2. Fool the plugin into thinking your executable is a shared object:

      add_executable(i_am_an_executable.so main.c)

    3. Check the APK:

      $ 7z l build/outputs/apk/app-debug.apk lib/                                                                                                                                    [2:08:56]
         Date      Time    Attr         Size   Compressed  Name
      ------------------- ----- ------------ ------------  ------------------------
                          .....         9684         4889  lib/armeabi/i_am_an_executable.so
                          .....         6048         1710  lib/arm64-v8a/i_am_an_executable.so
                          .....         9688         4692  lib/armeabi-v7a/i_am_an_executable.so
                          .....         5484         1715  lib/x86/i_am_an_executable.so
                          .....         6160         1694  lib/x86_64/i_am_an_executable.so
      
    4. Access and run your executable; it is located in context.getApplicationInfo().nativeLibraryDir.

    The downside to this is that you cannot set android:extractNativeLibs to false — I don't know of any way to access lib/ in an APK from within the app.

    0 讨论(0)
  • 2020-12-13 16:19

    Ok, I've found a solution that seems to by quite comfortable but probably there are more proper ways out there;

    CMakeLists.txt is by default placed inside myAppProject/app so I've added this line to CMakeLists.txt:

    set(EXECUTABLE_OUTPUT_PATH      "${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets/${ANDROID_ABI}")
    

    complete app/CMakeLists.txt:

    cmake_minimum_required(VERSION 3.4.1)
    
    set(CMAKE_VERBOSE_MAKEFILE on)
    
    # set binary output folder to Android assets folder
    set(EXECUTABLE_OUTPUT_PATH      "${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets/${ANDROID_ABI}")
    
    add_subdirectory (src/main/cpp/mylib)
    add_subdirectory (src/main/cpp/mybinary)
    

    complete app/src/main/cpp/mybinary/CMakeLists.txt:

    add_executable(mybinary ${CMAKE_CURRENT_SOURCE_DIR}/mybinary.cpp)
    # mybinary, in this example, has mylib as dependency
    target_link_libraries( mybinary mylib)
    target_include_directories (mybinary PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
    

    complete app/src/main/cpp/mylib/CMakeLists.txt:

    add_library( # Sets the name of the library.
                 mylib
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 # Associated headers in the same location as their source
                 # file are automatically included.
                 ${CMAKE_CURRENT_SOURCE_DIR}/mylib.cpp )
    
    target_include_directories (mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
    

    Doing so, any executable binary is compiled directly into assets folder, inside a subfolder whose name is the target ABI, eg:

    assets/armeabi/mybinary
    assets/x86_64/mybinary
    ... 
    

    In order to use the proper binary inside the App, the correct binary should be selected:

    String abi;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        abi = Build.SUPPORTED_ABIS[0];
    } else {
        //noinspection deprecation
        abi = Build.CPU_ABI;
    }
    String folder;
    if (abi.contains("armeabi-v7a")) {
        folder = "armeabi-v7a";
    } else if (abi.contains("x86_64")) {
        folder = "x86_64";
    } else if (abi.contains("x86")) {
        folder = "x86";
    } else if (abi.contains("armeabi")) {
        folder = "armeabi";
    }
    ...
    AssetManager assetManager = getAssets();
    InputStream in = assetManager.open(folder+"/" + "mybinary");
    

    Then, the binary should be copied away from assets folder with the correct execute permissions:

    OutputStream out = context.openFileOutput("mybinary", MODE_PRIVATE);
    long size = 0;
    int nRead;
    while ((nRead = in.read(buff)) != -1) {
        out.write(buff, 0, nRead);
        size += nRead;
    }
    out.flush();
    Log.d(TAG, "Copy success: " +  " + size + " bytes");
    File execFile = new File(context.getFilesDir()+"/mybinary");
    execFile.setExecutable(true);
    

    That's all!

    UPDATE: gradle.build file:

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 25
        buildToolsVersion "25"
        defaultConfig {
            applicationId "com.myapp.example"
            minSdkVersion 10
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    cppFlags ""
                }
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
    
        defaultConfig {
            externalNativeBuild {
                cmake {
                    targets "mylib", "mybinary"
                    arguments "-DANDROID_TOOLCHAIN=clang"
                    cFlags "-DTEST_C_FLAG1", "-DTEST_C_FLAG2"
                    cppFlags "-DTEST_CPP_FLAG2", "-DTEST_CPP_FLAG2"
                    abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
                }
            }
        }
    }
    
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:25.0.0'
        compile 'com.android.support:design:25.0.0'
        compile 'com.android.support:recyclerview-v7:25.0.0'
        compile 'com.android.support:cardview-v7:25.0.0'
        compile 'eu.chainfire:libsuperuser:1.0.0.201607041850'
    }
    
    0 讨论(0)
提交回复
热议问题