Можно ли поставить Java-приложение из Google Play Market на Windows?
Неожиданно пришла в голову странная мысль. Если на Google Play Market есть Java-приложение, то по идее, оно должно работать везде, где есть Java-машина. В том числе оно должно работать и на Windows.
Вопрос:
Можно ли поставить Java-приложение из Google Play Market на Windows?
Ответы (3 шт):
Нет, к сожалению, так работать не будет. Приложения изначально написаны под Android. Мало того, что интерфейс не адаптивен по OS Windows, так еще они просто не запустятся. Если вам уж очень нужно, то можно запустить через эмулятор (Bluestacks, Nox...).
Да, импортирование возможно, как и компиляция, за счёт java-машины. Но, сама-то программа написана под Android. Если вы уверены в своей правоте, в чём смысл вопроса, если вы сейчас мне на него ответили?
Update: Ответ StateItPrimitive дает значительно более глубокое с технической точки зрение понимание различий между Java и Android приложениями.
Нет, поставить Java-приложение из Google Play Market на Windows нельзя.
Автор вопроса совершенно правильно указал, что Java-приложения 100% переносимы между устройствами, однако приложения под Android из Play Market'а Java-приложениями считаться (в общем случае) не могут.
Для корректной работы Android приложения помимо виртуальной машины Java необходимо так же специфическое окружение операционной системы, фалы стандартных ресурсов и многое другое.
Это достаточно просто понять, если взглянуть на исходный код Java-приложения и Android-приложения. В Android-приложении Вы не найдете функцию main(), запускающую, собственно, приложение. Android-приложения запускаются не пользователем, а операционной системой, которая полностью контролирует жизненный цикл программы. Вне ОС Android написанные под нее приложения запустить не получится.
Однако, хотя автор вопроса имел ввиду не совсем это, если Вы хотите установить приложение на Windows с целью тестирования (или игр), то можно попросту использовать эмулятор.
P.s. Некая аналогия. Можно сказать, что приложения Play Market'е - это куски приложений. Чтобы такой кусок стал полноценным Java приложением, его необходимо дополнить определенным Java-кодом и ресурсами. Эта часть хранится в ОС Android (причем своя для разных версий и производителей устройства).
P.P.s Нашел объяснение, где слова подобраны лучше javarush. Цитата оттуда
Как я уже упоминал, в операционной системе Android для разработки приложений используется Java. Вы можете написать код приложения для Android с помощью предоставляемого Google API Java, который затем будет скомпилирован в файлы классов. На этом схожесть заканчивается. Android не использует виртуальную машину Java (JVM) для выполнения файлов классов, вместо этого, в нём используется виртуальная машина Dalvik, не являющаяся истинной JVM и не работающая с Java-байткодом. Для выполнения на виртуальных машинах Dalvik, файлы классов компилируются в формат DEX (Dalvik EXecutable – исполняемые файлы Dalvik). После преобразования в формат DEX, файлы классов вместе с другими ресурсами объединяются в пакеты Android (APK) для распространения и инсталляции на различных устройствах.
Статья уже сильно устарела, но главную мысль вполне передает.
Дисклеймер:
Я не являюсь экспертом в разработке Android, т.к. практически все время уделяю web-приложениям, тем не менее, тема оказалась для меня весьма интересной, поэтому попробую её раскрыть подробно (настолько, насколько это было бы интересно лично мне).
Java как язык программирования:
Java является языком программирования, т.е. описанием некоторого контракта (описания и связанного с ним поведения (например, недопустимость недопустимость undefined behavior)), который можно реализовать различными способами.
Например, никто не запрещает написать свою собственную среду исполнения поверх языка Java, т.е. среду исполнения, которая была бы Java-like, которая в качестве исходного кода использовала бы возможности (features) из языка Java какой-то определенной версии. В таком случае, перед вами бы встала непростая задача: поддержка совместимости со всевозможными (хотя бы самыми популярными) архитектурами процессоров и API операционных систем.
Причем, в частном случае, ваша собственная реализация даже не обязана быть JVM-like: она не обязана компилироваться в bytecode (см. ниже), вместо этого выполнять компиляцию исходного кода сразу в машинный код (native machine code), т.е. использовать более традиционный подход, который используется во многих других языках (таких как Go, C, C++, Swift, ...).
Для того, чтобы осознать все особенности и сложности (из-за которых системы реализованы несколько по-разному) нужно разобраться в принципах работы приложений на Android и вне его:
Классическая реализация JVM-like:
Жизненный цикл типичного классического (не Android) Java-приложения можно описать следующим образом:
source code -(compiler)-> bytecode -(JVM)-> native machine code.
Конкретизируем на определенном примере:
.java -(javac)-> .class -(HotSpot)-> native machine code:
javaв качестве исходного кода;javacв качестве компилятора исходного кода, написанного на Java, вbytecode;HotSpot(от Oracle) в качестве JVM, т.е. среды исполнения/интерпретации bytecode команд в команды машинного кода для конкретного CPU, т.е. определенного процессора, ядра которого исполняют машинный код (native machine code) вашего приложения;machine code- набор инструкций для определенной архитектуры процессоров и API операционной системы.
Разберем детальней:
source code(исходный код) - код вашей программы на JVM-based языке программирования:.java-файл - код на Java;.kt-файл - код на Kotlin;.clj-файл - код на Clojure;.scala-файл - код на Scala;.groovy-файл - код на Groovy;- и так далее...
compiler- компилятор исходного кода вbytecode(выбор компилятора однозначно зависит от языка программирования, использованному в исходном коде):bytecode- набор инструкций, исполняемых JVM:- существует всего 256 инструкций (см. Chapter 7), из которых в текущий момент реально используется лишь около 200;
.class-файл - набор инструкций, являющийся результатом компиляции исходного кода в bytecode;- если рассматривать на примере Java-кода, то каждый Java-класс попадает в отдельный
.class-файл, вне зависимости от того, являлся ли онTop Level Classили нет;
- если рассматривать на примере Java-кода, то каждый Java-класс попадает в отдельный
native machine code(машинный код) - набор инструкций (для определенной архитектуры процессоров и API ОС), исполняемый процессором:результат интерпретации bytecode в инструкции конкретной архитектуры процессоров;
- в общем случае, в рамках одной JVM требуется вести поддержку команд всех используемых для исполнения кода CPU.
Реализации JVM:
Вы уже могли догадаться, что реализаций JVM множество, и каждая из них самостоятельно отвечает за трансляцию стандартного для всех bytecode в специфичный для каждой архитектуры процессоров native machine code.
Для наглядности приведу вам примерный список наиболее популярных реализаций JVM:
HotSpot VM(от Oracle);OpenJVM;MSJVM/Microsoft VM;IBM JVM;JRockit;Dalvik(от Android, Inc.):- позднее, Android, Inc. (вместе с их ранней версией продукта Android) была куплена Google;
Android Runtime, он же,ART(от Google):- разработка от Google, которая создана на замену доставшегося при покупке Android
DalvikVM (Dalvikбыла заменена наARTначиная с Android 5.0 "Lollipop");
- разработка от Google, которая создана на замену доставшегося при покупке Android
- ...
В рамках каждой из этих реализаций спецификаций JVM самостоятельно решаются следующие вопросы:
- поддержка API различных операционных систем:
- предоставляются отдельные дистрибутивы для каждой из поддерживаемых ОС (это можно увидеть на примере JVM от Oracle);
- поддержка команд различных архитектур процессоров (CISC, RISC, MISC):
- в рамках каждой из архитектур процессоров поддержка команд различных линеек процессоров.
Packaging (упаковка) классических JVM-like приложений:
С точки зрения упаковки (packaging) классических JVM-like приложений существует несколько вариантов:
- JAR:
- не исполняемый (использоваться в качестве подключаемой библиотеки);
- исполняемый;
- имеет класс, содержащий метод
public static void main(String[] args), указание на который описано вMETA-INF/MANIFEST.MF; - с появлением SpringBoot, MicroProfile.IO и подобных им решений исполняемые JAR вытесняют остальные варианты packaging'а (за исключением не исполняемых JAR-библиотек, конечно же);
- имеет класс, содержащий метод
- EJB JAR - является стандартом архивации EJB модулей (в виде JAR файлов) в рамках Jakarta EE (бывшего Java EE):
- описание EJB модуля добавляется в
META-INF/ejb-jar.xml, либо посредством аннотаций (посредством Java SPI (Service Provider Interfaces), в частности, Java Annotation Processing API);
- описание EJB модуля добавляется в
- WAR (Web application ARchive):
- является стандартом архивации веб модулей в рамках Jakarta EE (бывшего Java EE);
- является модулем, загружаемым в Web Container в рамках Application Server'а (сервера приложений);
- может содержать несколько EJB JAR;
- описание веб модуля добавляется в
WEB-INF/web.xml, либо посредством аннотаций (посредством Java SPI (Service Provider Interfaces), в частности, Java Annotation Processing API);
- EAR (Enterprise application ARchive):
- является стандартом архивации enterprise приложений в рамках Jakarta EE (бывшего Java EE);
- состоит из нескольких WAR.
Android реализация JVM-like:
При разработке приложений под Android ребята из Android, Inc. (создатели Android, позднее купленные Google) предпочли использовать уже довольно популярный язык программирования Java, реализовав его возможности (features) из определенной версии (например, java 6) и периодически обновляя их, тем или иным образом (например, до java 8). Подробнее смотрите здесь, здесь и здесь.
Таким образом, они не стали изобретать язык программирования с нуля (как это сделал Apple в случае языка Swift), но зато они сильно изменили среду его исполнения.
Теперь жизненный цикл кода стал несколько варьироваться в зависимости от конкретной реализации. Рассмотрим пример с использованием стандартных инструментов сборки:
source code -(classic JVM compiler)-> bytecode -(dexer)-> dex -(Android JVM)-> machine code.
Конкретизируем на определенном примере: java -(javac)-> .class -(dexer)-> classes.dex -(Dalvik / ART)-> machine code:
javaв качестве исходного кода;javacв качестве классического компилятора исходного кода в классический JVMbytecode;.class- файлы, хранящие в себе команды классического формата JVM bytecode;dexerв качестве утилиты для конвертации классического JVM bytecode в проприетарный формат bytecode -dex(Dalvik executable);classes.dex(Dalvik executable) - файл (обычно один), хранящий в себе команды проприетарного формата bytecode для JVM Dalvik и ART;Android Runtime(ART) иDalvik(от Google) в качестве JVM, т.е. среды исполнения/интерпретации команд dex (проприетарного bytecode) в machine code (подробнее см. здесь):Dalvikиспользует парадигмуJIT(Just-In-Time), т.е. компиляцию в машинный код во время исполнения программы;Dalvikбыл заменен наAndroid Runtimeначиная с Android 5.0 "Lollipop";
Android Runtime(ART) использует парадигмуAOT(Ahead-Of-Time), т.е. компиляцию в машинный код перед исполнением программы (в случае Andoid - во время установки.apkиз play market):Android Runtimeиспользует файлыdex(Dalvik executable), поэтому является обратно совместимым сDalvikна уровне проприетарного bytecode приложений в рамках Android;
machine code- набор инструкций для определенной архитектуры процессоров.
Примечание: стоит понимать, что, например, при использовании инструментария Jack tools процесс сборки выглядел бы несколько иначе: .java -> .jack -> .dex, но в рамках данного ответа, пожалуй, не будем отклоняться от рассмотрения стандартных средств, поэтому опустим его использование.
Жизненный цикл приложения в Android:
С точки зрения упаковки (packaging) и установки (installing) приложения перед исполнением в Android JVM имеются ряд отличий от классического JVM-like варианта. Процесс можно описать следующим образом:
сборка исходников:
.java-(javac)->.class-(dexer)->classes.dex;
сборка
apk(Android package):classes.dex+resources files-(aapt)->.apk;
подпись
apk:.apk-(jarsigner + zipalign)->signed .apk;
установка
apk:Dalvik:
classes.dex-(dexopt)->odex(optimized dex) или жеquickened dex:- оптимизированный вариант файла dex: оптимизирует коды операций, подстановка адреса/индекса вызываемого метода из таблицы vtable и так далее;
Android Runtime (ART):
classes.dex-(dex2oat)->OAT version of classes.dex(dex+native machine code), т.е. выполняется конвертация проприетарного формата bytecode в машинный код, специфичный для процессора (CPU), который установлен на device, на который производится установка. Результат кладется вELF(Executable and Linkable Format) shared object;
запуск приложения:
- при нажатии на приложение служебный процесс (
ActivityManagerService) записывает команду старта приложения в socket входящих команд. - linux процесс
Zygote, в рамках которого загружена Android JVM (Dalvik / ART), прослушивает этот socket и выполняетfork()своего процесса при поступлении команды на старт приложения;- в рамках процесса
Zygoteпредварительно загружены базовые Android библиотеки из соображений оптимизации, поэтому дочерний процесс получит предварительно разогретую виртуальную машину: - создание процесса с нуля, загрузка в него всех необходимых библиотек и прогрев JVM занял бы куда больше времени;
- иными словами, каждое приложение в рамках Android запускается в собственном экземпляре JVM и в рамках дочернего процесса относительно
Zygote(название процесса, как бы, намекает); - Android работает поверх облегченной ОС Linux, поэтому процесс Zygote стартует за счет скриптов инициализации ОС (init.rc, подробнее см. здесь);
- в рамках процесса
- загрузка в дочерний
forkedпроцесс ELF shared object (см. пункт выше) соответствующего запускаемого Android приложения.
- при нажатии на приложение служебный процесс (
Подробнее см. здесь, здесь и здесь.
Заключение:
Я очень надеюсь, что изучив принцип работы классических JVM совместимых и Android приложений вы уже сами пришли примерно к следующему выводу (весь ответ вел к тому, чтобы вы осознали это самостоятельно):
- создание приложений на основании концепций с использованием некоторого абстрактного/промежуточного кода (bytecode), само по себе, дает вам лишь возможность заложиться на будущее для реализации интерпретации этого bytecode под соответствующую архитектуру CPU и API ОС;
.apkфайлы Android содержат уже совсем не классический JVM bytecode, который умеют интерпретировать стандартные JVM (по типу HotSpot), а некоторый проприетарный для Android специфичных JVM (Dalvik и ART) -classes.dex;- точка входа в программу:
- классические исполняемые JVM приложения (JAR'ники) имеют явную точку входа:
- создается класс, содержащий метод
public static void main(String[] args), указание на который должно быть описано вMETA-INF/MANIFEST.MF;
- создается класс, содержащий метод
- приложения под Android не имеют явной точки входа:
- они описывают собственный файл
AndroidManifest.xml, за счет которого "подписываются" на определенные события (например, наonCreateвActivity), т.е. система является event driven;
- они описывают собственный файл
- классические исполняемые JVM приложения (JAR'ники) имеют явную точку входа:
- теоретически, исходный код вашей программы можно попробовать написать таким образом, что вы на основании его соберете, как исполняемый jar-файл (см. выше), так и Andorid
.apk(см. выше), но это будут два независимых друг от друга файла, имеющих различное содержимое:- например, посредством подготовки конфигурации для двух разных сборок в рамках одного проекта (общие, переиспользуемые файлы исходного кода и ресурсов не разделять, конечно же):
- разграничить файлов ресурсов для Android и Classic JVM;
- разграничить некоторые исходные файлы:
- файл с точкой входа для classic JVM и конфигурацией для него в
META-INF/MANIFEST.MF; - файлы с подпиской на события (events) от ядра Android и конфигурацией для них в
AndroidManifest.xml; - создать два разных профиля в системе сборки (например, в maven или gradle), каждый из которых при сборке будет использовать не только общие ресурсы, но и специально выделенные для него (см. пункты выше);
- например, посредством подготовки конфигурации для двух разных сборок в рамках одного проекта (общие, переиспользуемые файлы исходного кода и ресурсов не разделять, конечно же):
- теоретически, вы можете запустить ваше Andorid приложение на иной ОС, если вы полностью имитируете окружение системы Andorid, т.е. если вы будете самостоятельно реализовывать event driven движок, имитирующий поведение ОС Android, а также обрабатывать соответствующие команды от Andorid приложения, использующие Android API, т.е. транслировать команды Andorid API на API ОС, в рамках которой запущено приложение (Linux, MacOS, Windows), либо посредством использования средств виртуализации системы:
- также остается ряд вопросов касательно поддержки VM
DalvikиAndroid Runtimeтрансляции bytecode команд в команды машинного кода (native machine code) не только ряда линеек мобильных процессоров (Qualcomm Snapdragon, Huawei Kirin, Samsung Exynos, т.е. процессоров, базирующиеся на архитектуре ARM, т.е. RISC), но и десктопных (Intel, AMD, т.е. процессоров, базирующихся на архитектуре x86 и x64, т.е. CISC):- данные различия в используемых архитектурах процессоров возникают из-за различий, которые были заложены на этапе проектирования решений:
- Classic JVM базируется на концепции stack machines (stack-based architecture);
- Android JVM базируется на концепции register machine (register-based architecture);
- пожалуй, данную проблему проще всего решить посредством применения средств виртуализации;
- подробнее касательно различий в архитектурах процессоров можно почитать здесь;
- как вы понимаете, это довольно трудоемкая задача, в результате решения которой, окажется, что вы написали свой собственный эмулятор для Andorid на вашу ОС.
- также остается ряд вопросов касательно поддержки VM
Краткое описание сравнения Java и Android API можно найти здесь.