Подтвердить что ты не робот

Gradle сборка android для разных процессорных архитектур

Я хочу построить 4 отдельных apks для 4 различных архитектур процессоров Android (armeabi armeabi-v7a x86 mips), используя Gradle.

У меня есть собственные библиотеки OpenCV, построенные для 4 архитектур процессора в папке libs.

libs
    -armeabi
    -armeabi-v7a
    -x86
    -mips

Я хочу, чтобы каждый apk содержал только библиотеку OpenCV, соответствующую правильной архитектуре процессора.

Текущая сборка script выглядит следующим образом:

apply plugin: 'android'

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(':workspace:OpenCV4Android:sdk:java')
}

android {
    compileSdkVersion 11
    buildToolsVersion "18.1.0"

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')

        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')

        flavorGroups "abi", "version"
        productFlavors {
            x86 {
                flavorGroup "abi"
            }
            arm {
                flavorGroup "abi"
            }
            mips {
                flavorGroup "abi"
            }
        }

    }
}

Может кто-нибудь помочь мне решить эту проблему?

Приветствия,

4b9b3361

Ответ 1

Как и Android Gradle Плагин версии 13, теперь вы можете создать отдельный APK, используя новый механизм "split". Вы можете прочитать об этом здесь.

Стандартная файловая структура для размещения ваших файлов .so:

src
-main
  -jniLibs
    -armeabi
      -arm.so
    -armeabi-v7a
      -armv7.so
    -x86
      -x86.so
    -mips
      -mips.so

Обратите внимание, что имя файла .so не имеет значения, если оно имеет расширение .so.

Затем в файле сборки Gradle:

android {
...
splits {
abi {
  enable true
  reset()
  include 'x86', 'armeabi-v7a', 'mips', 'armeabi'
  universalApk false
  }
 }
}

и

// map for the version code
ext.versionCodes = ['armeabi-v7a':1, mips:2, x86:3]

import com.android.build.OutputFile

android.applicationVariants.all { variant ->
    // assign different version code for each output
    variant.outputs.each { output ->
        output.versionCodeOverride =
            project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000000 + android.defaultConfig.versionCode
    }
}

Обратите внимание, что коды версий выше в ext.versionCodes в значительной степени неактуальны, это здесь, чтобы добавить уникальное смещение для каждого типа ABI, поэтому коды версий не сталкиваются.

Ответ 2

Разделенное решение ABI APK для gradle является самым простым, что я нашел до сих пор. @withoutclass имеет хорошую запись здесь: fooobar.com/questions/201925/... Мне пришлось ссылаться на документацию Android, так как это новая функция, которая все еще может измениться: http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits

Тем не менее, мне пришлось отказаться от этой простой реализации, так как мне нужно было поддерживать как сборку с живыми строками, так и архитектуру. Вы можете столкнуться с этой же проблемой, если поддерживаете как хранилище Google Play (которое поддерживает APK с архитектурой), так и App Store Amazon (который поддерживает только APK файлы).

Возможно, это будет возможно с помощью split APK, если вы можете добавить компонент вкуса, но на данный момент split + flavor еще не поддерживается: < а2 >

В итоге я использовал abiFilter, см. пример кода ниже:

android {
    flavorDimensions "abi"

    productFlavors {
        fat {
            flavorDimension "abi"
            ndk {
                abiFilters "x86", "armeabi-v7a", "armeabi"
                versionCode = 0;
            }
        }
        arm {
            flavorDimension "abi"
            ndk {
                abiFilter "armeabi"
                versionCode = 1;
            }
        }
        armv7a {
            flavorDimension "abi"
            ndk {
                abiFilter "armeabi-v7a"
                versionCode = 3;
            }
        }
        x86 {
            flavorDimension "abi"
            ndk {
                abiFilter "x86"
                versionCode = 6;
            }
        }
    }
}

// Each APK needs a different version code when submitted to Google,
// bump the versionCode we set in defaultConfig
android.applicationVariants.all { variant ->
    // Ugly hard coded flavorDimensions position
    // If you have more than one flavorDimension, make sure to target the position for "abi"
    def abiVersion = variant.productFlavors.get(0).versionCode

    variant.mergedFlavor.versionCode = abiVersion * 1000 + android.defaultConfig.versionCode
}

Обновление Использование universalApk, установленного в true, разрешает это решение, просто добавляет время для сборки каждого apk.

android {
    // Rest of Gradle file
        splits {
            abi {
            enable true
            reset()
            include 'armeabi', 'armeabi-v7a', 'x86'
            universalApk true
        }
    }
}

//Ensures architecture specific APKs have a higher version code
//(otherwise an x86 build would end up using the arm build, which x86 devices can run)
ext.versionCodes = [armeabi:1, 'armeabi-v7a':3, x86:6]

android.applicationVariants.all { variant ->
    // assign different version code for each output
    variant.outputs.each { output ->
        int abiVersionCode = project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) ?: 0
        output.versionCodeOverride = (abiVersionCode * 1000) + android.defaultConfig.versionCode
    }
}

Ответ 3

У меня нет ответа gradle, но я думаю, что теперь у меня есть общий ответ для любого инструмента для создания Android. Вот моя идея о том, как создавать отдельные файлы APK для каждой поддерживаемой архитектуры процессора:

  • Создайте свой APK с любыми инструментами, которые вы используете, включая все поддерживаемые библиотеки родного кода, например. armeabi, armeabi-v7a, x86 и mips. Я назову его "оригинальным" APK файлом.

  • Разархивируйте ваш оригинальный APK в пустую папку, используя любую утилиту zip/unzip, лучше всего используйте средства командной строки, чтобы позже вы могли ее автоматизировать с помощью оболочки script или пакетного файла.

    /li >
  • В папке с несжатым исходным APK удалите подпапку META-INF (это содержит подписи, нам нужно будет переписать APK после всех модификаций, поэтому оригинальная META-INF должен быть удален).

  • Перейдите в подпапку lib и удалите подпапки для любых архитектур процессоров, которые вы не хотите в новом файле APK. Например, оставьте только подкаталог "x86", чтобы сделать APK для процессоров Intel Atom.

  • Важно: каждый APK для другой архитектуры должен иметь другой номер "versionCode" в AndroidManifest.xml и код версии, например. armeabi-v7a должен быть немного выше, чем у armeabi (читайте инструкции Google для создания нескольких APK здесь: http://developer.android.com/google/play/publishing/multiple-apks.html). К сожалению, файл манифеста находится в скомпилированной двоичной форме внутри APK. Нам нужен специальный инструмент для модификации версии Code. См. Ниже.

  • Как только манифест изменен с новым кодом версии, а ненужные каталоги и файлы удалены, повторно запишите, подпишите и выровняйте свой меньший APK (используйте jarsigner и zipalign инструменты из Android SDK).

  • Повторите этот процесс для всех других архитектур, которые вам нужны для поддержки, создавая небольшие файлы APK со слегка отличающимися версиями (но с тем же именем версии).

Единственная нерешенная проблема - это способ изменить 'versionCode в двоичном файле манифеста. Я не мог найти решение для этого в течение долгого времени, поэтому, наконец, пришлось сесть и запустить мой собственный код, чтобы сделать это. В качестве отправной точки я взял APKExtractor от Prasanta Paul, http://code.google.com/p/apk-extractor/, написанный на Java. Я старая школа и еще более комфортно с С++, поэтому моя небольшая утилита "aminc", написанная на С++, теперь находится на GitHub по адресу:

https://github.com/gregko/aminc

Я опубликовал все решение Visual Studio 2012, но вся программа представляет собой один .cpp файл, который, вероятно, может быть скомпилирован на любой платформе. И вот пример файла Windows.bat, который я использую для разделения моего "жирного" apk с именем atVoice.apk на 4 небольших файла с именем atVoice_armeabi.apk, atVoice_armeabi-v7a.apk, atVoice_x86.apk и atVoice_mips.apk. Я фактически отправляю эти файлы в Google Play (см. Мое приложение в https://play.google.com/store/apps/details?id=com.hyperionics.avar), и все работает отлично. Также смотрите этот проект Github от Jorge Suárez de Lis, который размещает аналогичный script для Linux.

@echo off
REM    My "fat" apk is named atVoice.apk. Change below to whatever or set from %1
set apkfile=atVoice
del *.apk

REM    My tools build atVoice-release.apk in bin project sub-dir. 
REM    Copy it here for splitting.
copy ..\bin\%apkfile%-release.apk %apkfile%.apk

zip -d %apkfile%.apk META-INF/*

REM ------------------- armeabi ------------------------
unzip %apkfile%.apk AndroidManifest.xml
copy/y %apkfile%.apk %apkfile%.zip
zip -d %apkfile%.zip lib/armeabi-v7a/* lib/x86/* lib/mips/*
aminc AndroidManifest.xml 1
zip -f %apkfile%.zip
ren %apkfile%.zip %apkfile%_armeabi.apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_armeabi.apk MyKeyName
zipalign 4 %apkfile%_armeabi.apk %apkfile%_armeabi-aligned.apk
del %apkfile%_armeabi.apk
ren %apkfile%_armeabi-aligned.apk %apkfile%_armeabi.apk

REM ------------------- armeabi-v7a ---------------------
copy/y %apkfile%.apk %apkfile%.zip
zip -d %apkfile%.zip lib/armeabi/* lib/x86/* lib/mips/*
aminc AndroidManifest.xml 1
zip -f %apkfile%.zip
ren %apkfile%.zip %apkfile%_armeabi-v7a.apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_armeabi-v7a.apk MyKeyName
zipalign 4 %apkfile%_armeabi-v7a.apk %apkfile%_armeabi-v7a-aligned.apk
del %apkfile%_armeabi-v7a.apk
ren %apkfile%_armeabi-v7a-aligned.apk %apkfile%_armeabi-v7a.apk

REM ------------------- x86 ---------------------
copy/y %apkfile%.apk %apkfile%.zip
zip -d %apkfile%.zip lib/armeabi/* lib/armeabi-v7a/* lib/mips/*
aminc AndroidManifest.xml 9
zip -f %apkfile%.zip
ren %apkfile%.zip %apkfile%_x86.apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_x86.apk MyKeyName
zipalign 4 %apkfile%_x86.apk %apkfile%_x86-aligned.apk
del %apkfile%_x86.apk
ren %apkfile%_x86-aligned.apk %apkfile%_x86.apk

REM ------------------- MIPS ---------------------
copy/y %apkfile%.apk %apkfile%.zip
zip -d %apkfile%.zip lib/armeabi/* lib/armeabi-v7a/* lib/x86/*
aminc AndroidManifest.xml 10
zip -f %apkfile%.zip
ren %apkfile%.zip %apkfile%_mips.apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_mips.apk MyKeyName
zipalign 4 %apkfile%_mips.apk %apkfile%_mips-aligned.apk
del %apkfile%_mips.apk
ren %apkfile%_mips-aligned.apk %apkfile%_mips.apk


del AndroidManifest.xml
del %apkfile%.apk
:done

Грег

Ответ 4

UPDATE - со времени этой публикации в процессе сборки gradle был достигнут значительный прогресс, поэтому этот ответ может быть не рекомендованной лучшей практикой, и новые изменения могут даже остановить его. Используйте свое собственное усмотрение.

Для этого сначала необходимо поместить собственные библиотеки в следующую иерархию папок

lib
 -armeabi
  -arm.so
  -*.so

-

lib
 -x86
  -x86.so
  -*.so

затем застегните папки lib (без 's') (например, arm.zip и x86.zip) и переименуйте расширение "zip" в "jar" (например, arm.jar и x86.jar). Поместите эти банки в соответствующие папки (например, armeabi/libs и x86/libs). Теперь мы будем включать зависимости для каждого аромата. Но мы не можем использовать "файл компиляции"... ". Мы должны использовать файл" flavorCompile "..."

например.

    flavorGroups 'abi'
        productFlavors {
            arm {
                flavorGroup 'abi'
                dependencies {
                    armCompile files('arm/libs/armeabi.jar')
                }
            }
            x86 {
                flavorGroup 'abi'
                dependencies {
                    x86Compile files('x86/libs/x86.jar')
                }
            }

    }

====

Вот более сложная среда. У вас есть не только варианты архитектуры процессора, но также у вас есть библиотеки отладки (.jar,.so) для процессоров. Пример здесь имеет Debug.jar для отладки руки и NonDebug.jar для выпуска Arm; и *.so для обеих рук и X86. Такая конфигурация может быть достигнута с помощью gradle ExtraPropertiesExtension Пожалуйста, прочтите мой SO ответ здесь, fooobar.com/info/201928/..., чтобы понять, как можно структурировать папки отладки.

android {
compileSdkVersion 18
buildToolsVersion "19.0.0"

final DEBUG_ROOT = "build-types/debug"
final RELEASE_ROOT = "build-types/release"
project.ext.set("projRoot", "")
buildTypes {
    debug {
        project.projRoot = DEBUG_ROOT

        dependencies {
            debugCompile files(DEBUG_ROOT+"/libs/Debug.jar")
        }
    }

    release {
        project.projRoot = RELEASE_ROOT
        dependencies {
            releaseCompile files(RELEASE_ROOT+"/libs/NonDebug.jar")
        }
        runProguard true
        proguardFile 'proguard.cfg'
    }
}
sourceSets {

    final PROJ_ROOT = project.ext.get("projRoot")
    final BUILD_TYPE_RES = PROJ_ROOT + "/res"
    main {
        manifest.srcFile 'src/main/AndroidManifest.xml'
        java.srcDirs = ['src/main/java']
        //resources.srcDirs = ['src/main']
        //aidl.srcDirs = ['src/main']
        //renderscript.srcDirs = ['src/main']
        res.srcDirs = ['src/main/res',BUILD_TYPE_RES]
        assets.srcDirs = ['src/main/assets']
    }

    flavorGroups 'abi'
    productFlavors {
        arm {
            flavorGroup 'abi'
            final ARM_LIB_PATH = PROJ_ROOT + "/arm/libs/armeabi.jar"
            dependencies {
                armCompile files(ARM_LIB_PATH)
            }
        }
        x86 {
            flavorGroup 'abi'
            final X86_LIB_PATH = PROJ_ROOT + "/x86/libs/x86.jar"
            dependencies {
                x86Compile files(X86_LIB_PATH)
            }
        }

    }

    // Move the tests to tests/java, tests/res, etc...
    instrumentTest.setRoot('tests')

    // Move the build types to build-types/<type>
    // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
    // This moves them out of them default location under src/<type>/... which would
    // conflict with src/ being used by the main source set.
    // Adding new build types or product flavors should be accompanied
    // by a similar customization.
    debug.setRoot(DEBUG_ROOT)
    release.setRoot(RELEASE_ROOT)
}

}