C/C++ в Android приложениях

Автор: | 09.11.2018

Тема на стадии разработки

Введение

При помощи Android SDK  можно писать приложения на Java или Kotlin. Кроме него есть еще Android  NDK — Native Development Kit. Это — сопутствующий инструмент Android SDK, который позволяет создавать  компоненты с помощью нативного кода (C/C++).

Что означает Native (родной) код? Это означает, что C/C++  код компилируется непосредственно в понятные для процессора инструкции без каких-либо промежуточных механизмов, таких как Java виртуальная машина (JVM). При этом, модель приложения для Android не меняется. Приложения по-прежнему упаковываются в файл .apk и работают внутри JVM.

Итак, Android  NDK позволяет:

  • повысить производительность ряда задач;
  • использовать библиотеки (или собственные программы), написанные на C/C ++;
  • создавать кросс-платформенные решения (Android и iOS);
  • управлять аппаратным обеспечением через низкоуровневое программирование.

В этой теме будет рассмотрено, как подключить в Android Java проект C/C ++ библиотеку под названием «SuperpoweredSDK».  Эта библиотека предоставляет набор интересных функций, таких как аудио фильтры, эффекты, возможности декодирования звука, поддержку различных аудиоформатов и многое другое.

Создание проекта с поддержкой C/C ++

Для компиляции и отладки native кода в проекте необходимы следующие компоненты:

  • NDK — набор инструментов, который позволяет использовать C/C++ код в Android.
  • CMake — инструмент внешней сборки, который работает вместе с Gradle для создания native библиотеки. Этот компонент вам не нужен, если вы планируете использовать ndk-build.
  • LLDB — debugger Android Studio для отладки native кода.

Запускаю Android Studio и  инсталлирую эти компоненты из Android SDK. Для этого из главного меню выбираю Tools -> SDK Manager, затем вкладку SDK Tools.  В окне «Default Settings» устанавливаю флажки около LLDB и NDK (CMake уже имеет статус Installed). Кнопкой OK запускаю процесс инсталляции, который длится около 1 часа.

Теперь создаю новый проект под названием superpoweredsample. Процесс создания нового проекта AS с поддержкой Native кода аналогичен созданию стандартного Android-проекта, за исключением нескольких дополнительных шагов:

  1. В окне «Create new project» устанавливаю флажок Include C++ Support. Нажимаю 2 раза кнопку Next.
  2. Выбираю Empty Activity и 2 раза Next.
  3. В разделе «Customize C++ Support» устанавливаю флажок для:
  • еще одна настройка ????
  • Exceptions Support;
  • Runtime Type Information Support.

4. Нажимаю Finish.

Создание проекта длится около 10 минут. В результате помимо обычных для проекта в Android Studio файлов будут созданы: файл native-lib.cpp  в группе cpp, а также файл CmakeLists.txt в группе External Build Files:

native-lib.cpp — исходный C/C++ файл с сообщением

"Hello from C++"

CmakeLists.txt —  это файл сценария, по которому  Gradle использует инструмент CMake для построения из native исходного кода ndk-build библиотеку.

Подключение C/C ++ библиотеки в проект

Загружаю Superpowered SDK-библиотеку Low-Latency-Android-iOS-Linux-Windows-tvOS-macOS-Interactive-Audio-Platform-master.zip и распаковываю ее содержимое, куда удобно. Папка Superpowered — это и есть сама библиотека.

Она содержит два типа файлов:

  • .a файлы — это статические библиотеки Superpowered;
  • .h файлы — это в основном интерфейсы к функциям, реализованным в статических библиотеках.

Для подключения библиотеки необходимо внести изменения в ряд файлов проекта:

  • В файле local.properties указываю место, в которое разметил библиотеку Superpowered

  • В файле build.gradle(Module:app) вношу изменения, отмеченные красным:
apply plugin: 'com.android.application'

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def superpowered_sdk_path = properties.getProperty('superpowered.dir')

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.sv.nativec"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }

        externalNativeBuild {
            cmake {
                arguments '-DANDROID_PLATFORM=android-16', '-DANDROID_TOOLCHAIN=clang', '-DANDROID_ARM_NEON=TRUE', '-DANDROID_STL=gnustl_static', "-DPATH_TO_SUPERPOWERED:STRING=${superpowered_sdk_path}"
                cFlags '-O3', '-fsigned-char'

                cppFlags "-frtti -fexceptions", '-fsigned-char', "-I${superpowered_sdk_path}"
            }
        }
    }

    dataBinding.enabled = true

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }
    
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

 

  • В файле CMakeLists вношу изменения, которые сообщают Android Studio включить предварительно скомпилированную библиотеку:

 

cmake_minimum_required(VERSION 3.4.1)

set(
   PATH_TO_SUPERPOWERED
   CACHE STRING ""
)

message(${ANDROID_ABI})

file(GLOB CPP_FILES "*.cpp")
include_directories(${PATH_TO_SUPERPOWERED})

add_library(
            native-lib
            SHARED
            src/main/cpp/native-lib.cpp
)

add_library(
    SuperpoweredExample
    SHARED
    src/main/cpp/superpoweredsample.cpp
)

add_library(
   SuperpoweredAndroidAudioIO
   SHARED
   ${CPP_FILES}
   ${PATH_TO_SUPERPOWERED}/AndroidIO/SuperpoweredAndroidAudioIO.cpp
)

target_link_libraries(
    SuperpoweredExample
    SuperpoweredAndroidAudioIO
)

target_link_libraries(
    SuperpoweredAndroidAudioIO
    log
    android
    OpenSLES
    ${PATH_TO_SUPERPOWERED}/libSuperpoweredAndroid${ANDROID_ABI}.a
)

target_link_libraries(
    native-lib
    log
    android
)

После этого можно построить  проект и увидеть файлы  native библиотеки в представлении проекта.

На этом этапе пока выдает ошибки. Слишком много различных настроек, в каждой необходимо разобраться. Подробнее по настройкам см. первоисточник

Внедрение пользовательского интерфейса и добавление кода на C ++

Теперь давайте приступим к реализации макета для приложения. Приложение состоит всего из одного действия с некоторыми базовыми элементами управления для воспроизведения звука и некоторыми ручками для аудиоэффектов. Вот полный XML-файл:

 

<?xml version="1.0" encoding="utf-8"?>

<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data class="MainActivityBinding">

        <variable
            name="playing"
            type="java.lang.Boolean"/>

    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.superpoweredsample.MainActivity">

        <ImageView
            android:id="@+id/btn_play_pause"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:src="@{playing ? @drawable/ic_pause_circle_outline_black_24dp : @drawable/ic_play_circle_outline_black_24dp}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_song"
            app:layout_constraintVertical_bias="0.8"
            tools:srcCompat="@drawable/ic_play_circle_outline_black_24dp"/>

        <TextView
            android:id="@+id/tv_artist"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Protest The Hero"
            app:layout_constraintBottom_toTopOf="@+id/tv_song"
            app:layout_constraintEnd_toEndOf="@+id/tv_song"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="@+id/tv_song"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.20999998"
            app:layout_constraintVertical_chainStyle="packed"/>

        <TextView
            android:id="@+id/tv_song"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:text="Drumhead Trial"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_artist"/>

        <SeekBar
            android:id="@+id/sb_track_progress"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="32dp"
            android:layout_marginStart="32dp"
            app:layout_constraintBottom_toTopOf="@+id/btn_play_pause"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>

        <SeekBar
            android:id="@+id/sb_pitch"
            style="@style/Widget.AppCompat.SeekBar.Discrete"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="32dp"
            android:max="25"
            android:progress="13"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_pitch"/>

        <TextView
            android:id="@+id/tv_pitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="32dp"
            android:text="Pitch"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_song"/>

        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="32dp"
            android:text="Time"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/sb_pitch"/>

        <RadioGroup
            android:id="@+id/radioGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:orientation="horizontal"
            android:padding="2dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_time_normal">

            <RadioButton
                android:id="@+id/rb_slow_down"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="12dp"/>

            <RadioButton
                android:id="@+id/rb_normal_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="12dp"
                android:layout_marginRight="12dp"/>

            <RadioButton
                android:id="@+id/rb_speed_up"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="12dp"/>
        </RadioGroup>

        <TextView
            android:id="@+id/tv_time_normal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:text="Normal"
            app:layout_constraintEnd_toStartOf="@+id/tv_time_fast"
            app:layout_constraintStart_toEndOf="@+id/tv_time_slow"
            app:layout_constraintTop_toTopOf="@+id/tv_time"/>

        <TextView
            android:id="@+id/tv_time_slow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="Half"
            app:layout_constraintBottom_toTopOf="@+id/radioGroup"
            app:layout_constraintStart_toStartOf="@+id/radioGroup"/>

        <TextView
            android:id="@+id/tv_time_fast"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="Double"
            app:layout_constraintBottom_toTopOf="@+id/radioGroup"
            app:layout_constraintEnd_toEndOf="@+id/radioGroup"/>

        <TextView
            android:id="@+id/tv_end_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="6:05"
            app:layout_constraintBottom_toTopOf="@+id/sb_track_progress"
            app:layout_constraintEnd_toEndOf="@+id/sb_track_progress"/>

        <TextView
            android:id="@+id/tv_pitch_min"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginStart="8dp"
            android:text="-12"
            app:layout_constraintBottom_toTopOf="@+id/sb_pitch"
            app:layout_constraintStart_toStartOf="@+id/sb_pitch"/>

        <TextView
            android:id="@+id/tv_pitch_max"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:text="+12"
            app:layout_constraintBottom_toBottomOf="@+id/tv_pitch_normal"
            app:layout_constraintEnd_toEndOf="@+id/sb_pitch"
            app:layout_constraintTop_toTopOf="@+id/tv_pitch_normal"/>

        <TextView
            android:id="@+id/tv_pitch_normal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:text="0"
            app:layout_constraintBottom_toBottomOf="@+id/tv_pitch_min"
            app:layout_constraintEnd_toStartOf="@+id/tv_pitch_max"
            app:layout_constraintHorizontal_bias="0.53"
            app:layout_constraintStart_toEndOf="@+id/tv_pitch_min"
            app:layout_constraintTop_toTopOf="@+id/tv_pitch_min"/>
    </android.support.constraint.ConstraintLayout>
</layout>

 

Тема еще не закончена

 

 

 

 

Полезные ссылки:

Android NDK: Using C/C++ Native Libraries to Write Android Apps

Using C++ OpenCV code with Android

Что такое Android.mk?

 

 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *