2016-08-12 13 views
11

Przetestowuję nowy budynek C/C++ w Android Studio przez CMake poprzez stabilną gradle (http://tools.android.com/tech-docs/external-c-builds).Skompiluj i użyj binariów wykonywalnych zależnych od ABI w systemie Android z Androidem Studio 2.2 i CMake

W mojej aplikacji, zrootowane urządzenie musi używać pliku binarnego zależnego od ABI, który kompiluję w Android Studio.

Kiedy próbuję skompilować biblioteki standardowej z

add_library(mylib SHARED mylib.c) 

pobiera automatycznie skompilowany i kopiowane wewnątrz lib/[ABI] folderze APK (np /lib/armeabi/mylib.so), ale jeśli skompilować wykonywalny plik binarny z:

add_executable(mybinary mybinary.cpp) 

binarne są corectly generowanego wewnątrz folderu produkcji:

app/build/intermediates/cmake/debug/lib/armeabi/mybinary 
app/build/intermediates/cmake/debug/lib/x86_64/mybinary 
... 

, ale nie wydają się być kopiowane w dowolnym miejscu w apk.

Jaki jest właściwy sposób na zaspokojenie tej potrzeby? Czy zadanie stopniowania jest do zrobienia?

build.gradle:

apply plugin: 'com.android.application' 

android { 
    compileSdkVersion 24 
    buildToolsVersion "24.0.1" 
    defaultConfig { 
     applicationId "com.my.app" 
     minSdkVersion 10 
     targetSdkVersion 24 
     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 " 
       arguments "-DANDROID_TOOLCHAIN=clang", "-DANDROID_PLATFORM=android-21" 
       cFlags "-DTEST_C_FLAG1", "-DTEST_C_FLAG2" 
       cppFlags "-DTEST_CPP_FLAG2", "-DTEST_CPP_FLAG2" 
       abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a' 
      } 
     } 
    } 
} 

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    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:24.1.1' 
    compile 'com.android.support:design:24.1.1' 
    compile 'com.android.support:recyclerview-v7:24.1.1' 
    compile 'eu.chainfire:libsuperuser:1.0.0.201607041850' 
} 

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1) 

set(CMAKE_VERBOSE_MAKEFILE on) 

add_executable(mybinary ${CMAKE_CURRENT_SOURCE_DIR}/mybinary.cpp) 
target_link_libraries(mybinary libcustom) 
target_include_directories (mybinary PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 

mybinary.cpp

#include <stdlib.h> 
#include <string> 
#include <iostream> 

using namespace std; 

int main(int argc, char *argv[]) { 

    string hello = "Hello from C++"; 
    cout << "Message from native code: " << hello << "\n"; 

    return EXIT_SUCCESS; 
} 

Jak aplikacja powinna współdziałać z mybinary:

import eu.chainfire.libsuperuser.Shell; 
... 
Shell.SU.run("/path/to/mybinary"); 
+0

[Budowanie plików wykonywalnych dla powłoki Androida] (http://stackoverflow.com/questions/35231168/building-executables-for-android- shell/35275134#35275134) – Onik

Odpowiedz

12

Ok, znalazłem rozwiązanie, które wydaje się dość wygodne, ale prawdopodobnie istnieją bardziej odpowiednie sposoby;

CMakeLists.txt jest domyślnie umieszczony wewnątrz myAppProject/app tak Dodałem tę linię do CMakeLists.txt:

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

kompletne 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) 

kompletny aplikacji /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}) 

kompletne 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}) 

Robi tak każdy wykonywalny binarny jest kompilowany bezpośrednio do folderu aktywów, wewnątrz podkatalogu, którego nazwa jest celem ABI, np:

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

W celu skorzystania z właściwego pliku binarnego wewnątrz aplikacja, prawidłowa binarny powinien być wybrany:

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"); 

Następnie binarne powinny być skopiowane od folderze aktywów o prawidłowe wykonanie uprawnienia:

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); 

To wszystko!

UPDATE: Plik gradle.build:

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

To jest świetne. po co w ten sposób przeszkadzać? z shared lib, mechanizm jest już po prostu użyć loadlibrary(), aby go załadować i używać. – Gerry

+0

Wygląda na to, że nie działa w przypadku plików binarnych. – ADD

+0

Nie otrzymuję żadnych wbudowanych plików w folderze zasobów, tylko utworzone puste katalogi –

1
  1. Bądź wyjście plików wykonywalnych gdzie plugin Android Gradle spodziewa biblioteki:

    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")

  2. Fool wtyczki do myślenia, plik wykonywalny jest obiekt wspólny:

    add_executable(i_am_an_executable.so main.c)

  3. C heck 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. Uzyskaj i uruchom plik wykonywalny; znajduje się w context.getApplicationInfo().nativeLibraryDir.

Minusem jest to, że nie można ustawić android:extractNativeLibs do false — ja nie znam żadnego sposobu, aby uzyskać dostęp lib/ w APK od wewnątrz aplikacji.

Powiązane problemy