Two and a half years ago, I published an article
Writing, Collecting, and Running HelloWorld for Android in Notepad . She became very popular and gained about 80,000 views. With the advent of new tools, such as
Jack ToolChain , it became necessary to
reprint and update the article .
When I started learning Android, I wanted to
completely write and compile an Android application manually - without using an IDE. However, this task proved to be difficult and took me quite a lot of time. But as it turned out - this approach has
brought great benefits and clarified many subtleties that hide the IDE.
Using only a notebook , we will write a very small educational Android application. And then compile it, build and run on the device - and all through the command line. Interested? Then I ask.
Introduction
I was amazed at how complex and confusing the template application in Android Studio is. It is simply piled up with resources. And to a lesser extent - code and scripts. Although all it has to do is display HelloWorld! In addition, the books and manuals that I have reviewed explain how to create an IDEA or eclipse HelloWorld using dialog boxes - and further narrative is already coming from it. And what happens "under the hood" - it remains only to guess.
')
We will create our own template project, which is ideal to use for educational purposes. There will be nothing superfluous, only all the most necessary. And then we will analyze in detail how to assemble and run it on your Android device. At the end of the article there will be a link to download the archive with the final project - if you have any questions, you can check it out.
Thus, you will be 100% aware and understand the composition of your project and the process of its assembly. Although this test project is designed for training, with a little refinement it can be used as a solid foundation for your real projects.
Training
First we need to download and install command line tools. The link to download them is at the bottom of the page dedicated to Android Studio (
https://developer.android.com/studio/index.html ).
Android SDK 24 is just Android
N (
N ougat / 7). We accept the conditions, download the installer, run it. Leave everything by default. It will be installed in the
C: \ Users \ kciray \ AppData \ Local \ Android \ android-sdk directory . Remember this path, there will be our main tools.
Next, run the SDK Manager (from the
android-sdk folder) and also install the default set. It has everything you need, including the new Jack compiler. You will also need
JDK 8 .
The main requirement before reading this article is that in addition to the installed software, you should already be able to run the Helloworld that comes with Eclipse or Android Studio on your device. Those. you must have a usb driver configured, usb debugging enabled on your device, etc ... Or, an emulator is created and configured. These are very basic things, and their consideration is beyond the scope of this article - there is enough information on the net. By the way, reading a couple of chapters from books will also not be superfluous - at least to understand how the manifest works, resources, and indeed the basics of the Java language. And in this article I will describe what the books are silent about.
Writing a project
To get started, create some folder where your project will be.
Let's call it
testapp . In it, create 3 more folders -
bin ,
res and
src .
Create a blank text file in
testapp and change its name to
AndroidManifest.xml .
Add the following to it:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testapp"> <uses-sdk android:targetSdkVersion="24" /> <application android:label="TestApp"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
It's simple. We intend to make an application named
TestApp , which at startup starts the
MainActivity class. It remains only to write this small class - and the application is ready. If necessary, edit the
android: targetSdkVersion property in the
uses-sdk tag β
install the version that you have.
Next - create a simple resource - the string
Hello test app . In fact, we could do without a resource by inserting this line directly into Java code. But some assembly steps work with resources, and in order to see interesting moments we will work with them.
Let's create in the
res folder the
values folder. All resources should be divided into folders. Next, we will create an empty
strings.xml file in it, and in it we will write:
<resources> <string name="hello">Hello test app!</string> </resources>
That's all the resources we need. Simple, isn't it? Next, we will create a
com folder inside
src , an
example folder in it, then a
testapp folder even lower down the hierarchy - and then our class is
MainActivity.java . Add there code:
package com.example.testapp; import android.app.Activity; import android.app.AlertDialog; import android.os.Bundle; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button button = new Button(this); button.setText("Big button"); button.setOnClickListener(v -> { new AlertDialog.Builder(MainActivity.this) .setTitle("From lambda") .setMessage(getString(R.string.hello)) .show(); }); setContentView(button); } }
This is the simplest Activity that contains one button for the whole screen. Clicking this button brings up a dialog box that shows a row of resources. Pay attention to the lambda (construction
v -> {...} ). Jack ToolChain allows us to use many features of Java 8 under the android. You can read more on
developer.android.com and
source.android.com .
The directory structure should get this
β AndroidManifest.xml ββββbin ββββres β ββββvalues β strings.xml β ββββsrc ββββcom ββββexample ββββtestapp MainActivity.java
And this is actually all that we needed for the simplest project. For comparison -
HelloWorld from Android Studio (2.1.3) β .gitignore β build.gradle β gradle.properties β gradlew β gradlew.bat β local.properties β MyApplication2.iml β settings.gradle β ββββ.gradle β ββββ2.14.1 β ββββtaskArtifacts β cache.properties β cache.properties.lock β fileHashes.bin β fileSnapshots.bin β fileSnapshotsToTreeSnapshotsIndex.bin β taskArtifacts.bin β ββββ.idea β β .name β β compiler.xml β β encodings.xml β β gradle.xml β β misc.xml β β modules.xml β β runConfigurations.xml β β workspace.xml β β β ββββcopyright β β profiles_settings.xml β β β ββββlibraries β animated_vector_drawable_24_2_0.xml β appcompat_v7_24_2_0.xml β hamcrest_core_1_3.xml β junit_4_12.xml β support_annotations_24_2_0.xml β support_compat_24_2_0.xml β support_core_ui_24_2_0.xml β support_core_utils_24_2_0.xml β support_fragment_24_2_0.xml β support_media_compat_24_2_0.xml β support_v4_24_2_0.xml β support_vector_drawable_24_2_0.xml β ββββapp β β .gitignore β β app.iml β β build.gradle β β proguard-rules.pro β β β ββββlibs β ββββsrc β ββββandroidTest β β ββββjava β β ββββcom β β ββββexample β β ββββkciray β β ββββmyapplication β β ApplicationTest.java β β β ββββmain β β β AndroidManifest.xml β β β β β ββββjava β β β ββββcom β β β ββββexample β β β ββββkciray β β β ββββmyapplication β β β MainActivity.java β β β β β ββββres β β ββββdrawable β β ββββlayout β β β activity_main.xml β β β β β ββββmipmap-hdpi β β β ic_launcher.png β β β β β ββββmipmap-mdpi β β β ic_launcher.png β β β β β ββββmipmap-xhdpi β β β ic_launcher.png β β β β β ββββmipmap-xxhdpi β β β ic_launcher.png β β β β β ββββmipmap-xxxhdpi β β β ic_launcher.png β β β β β ββββvalues β β β colors.xml β β β dimens.xml β β β strings.xml β β β styles.xml β β β β β ββββvalues-w820dp β β dimens.xml β β β ββββtest β ββββjava β ββββcom β ββββexample β ββββkciray β ββββmyapplication β ExampleUnitTest.java β ββββbuild β ββββgenerated β mockable-android-24.jar β ββββgradle ββββwrapper gradle-wrapper.jar gradle-wrapper.properties
Looks worse than 2 years ago
Actually, automation through gradle, work with git and IDE are very important things, but at the stage of learning Android
I would really like to abstract from them .
Assembly
Now we come to the most important and difficult stage. We will work a lot with the command line, so I recommend that you write all the commands given here into one file and name it
Compile.bat . At the end of the file after the commands, you can add
pause so that the result and errors can be seen - if any.
Preparation of ways
The first thing we will do for convenience and brevity is to create special variables in which we will store paths. First of all, let's define our main directories. You need to replace the paths to the JDK and Android SDK with the ones you have.
set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_73 set ANDROID_HOME=C:\Users\kciray\AppData\Local\Android\android-sdk set DEV_HOME=%CD%
Next - the path directly to the programs. I recommend that you browse the catalogs of your SDK and make sure that everything is in place. Also correct the versions that are present in the paths.
set JACK_JAR="%ANDROID_HOME%\build-tools\24.0.2\jack.jar" set AAPT_PATH="%ANDROID_HOME%\build-tools\24.0.2\aapt.exe" set ANDROID_JAR="%ANDROID_HOME%\platforms\android-24\android.jar" set ADB="%ANDROID_HOME%\platform-tools\adb.exe" set JAVAVM="%JAVA_HOME%\bin\java.exe"
By the way, in older versions, the aapt utility was in platform-tools - and I do not rule out that it and / or others can slip away somewhere else. So be careful. If you check everything right now, then the rest of the article should go smoothly.
And yet - in a couple of variables let's score our packages and classes. If you go to change them - you do not have to run on the code - all the settings at the beginning.
set PACKAGE_PATH=com/example/testapp set PACKAGE=com.example.testapp set MAIN_CLASS=MainActivity
Preparing to compile
To begin, I will ask - have you ever wondered how the mysterious class
R works? Actually he confused me at first because of his supernatural capabilities. How can one at a compilation stage access the XML files in other directories through the class fields? I assumed that the precompiler was operating there - it turned out that way.
Actually, there is a special utility AAPT - it goes through the directories of your resources and creates the very
R.java . It turns out that everything is very simple - it's just a class, which includes other static nested classes with integer constants. And that's it! It looks like this:
R.java package com.example.testapp; public final class R { public static final class attr { } public static final class string { public static final int hello=0x7f020000; } }
Now let's create it with you. To do this, use the following commands:
call %AAPT_PATH% package -f -m -S %DEV_HOME%\res -J %DEV_HOME%\src -M %DEV_HOME%\AndroidManifest.xml -I %ANDROID_JAR%
Let's figure out what's what. AAPT - Android Asset Packaging Tool - literally "packer android property". Its options are:
- package - says that we just need to pack resources (and not add or remove)
- -f - rewriting existing R.java , if any
- -m - place R.java in the proper packages, and not in the root specified in the -J path
- -S - after this option, we specify the directory with resources
- -J - after this option, we specify where to save the resulting R.java
- -I - after this option, we specify the path to the plug-in library - enable android.jar
After its execution, the same
R.java file should appear in the src directory. Check it out.
Now there is no magic in our project and it is completely syntactically correct in Java. And now the fun part. Remember how classic Java programs are compiled via
javac ? Previously, he was also included in the sequence of assembling Android applications. We took our source code
(*. Java) , obtained from them the JVM byte-code
(* .class) and only then from it received the byte-code for Dalvic
(* .dex) . With the advent of Jack ToolChain, we have reduced our build sequence by one step. From the source
(* .java) we immediately get the byte code for Dalvic
(* .dex) .
Where do you get Jack? It is located in the
build-tools folder as
jack.jar and runs as a regular Java archive.
%JAVAVM% -jar %JACK_JAR% --output-dex "%DEV_HOME%\bin" -cp %ANDROID_JAR% -D jack.java.source.version=1.8 "%DEV_HOME%\src\com\example\testapp\R.java" "%DEV_HOME%\src\com\example\testapp\MainActivity.java"
The arguments are as follows:
- -jar - Standard option for JVM, indicating that you need to run a Java archive. Has nothing to do with jack
- --output-dex - The folder where the final dex file should be placed. Let it be in bin
- -D jack.java.source.version = 1.8 - βDβ indicates that we are setting a property. jack.java.source.version allows us to indicate that we are using Java 8. Without it, the lambda will not work and there will be errors. For a complete list of properties, see the % JAVAVM% -jar% JACK_JAR% --help-properties command.
- [List of * .java files] - Your source code. We only have 2 files - R.java and MainActivity.java
For a complete list of options for Jack, see the
% JAVAVM% -jar% JACK_JAR% --help command.Make sure our classes.dex is in the bin folder. Now you just have to pack it together with the resources in the APK file. Let's do it:
call %AAPT_PATH% package -f -M %DEV_HOME%/AndroidManifest.xml -S %DEV_HOME%/res -I %ANDROID_JAR% -F %DEV_HOME%/bin/AndroidTest.unsigned.apk %DEV_HOME%/bin
Here the options are similar to those we used when creating
R.java :
- package - says that we just need to pack resources (and not add or remove)
- -f - overwrite existing AndroidTest.unsigned.apk , if any
- -M - after this option, we specify the path to the manifest file
- -S - after this option, we specify the directory with resources
- -I - after this option, we specify the path to the plug-in library - enable android.jar
- -F - after this option, we specify where to save the resulting AndroidTest.unsigned.apk
- the last argument is the path to the folder with dex files
AndroidTest.unsigned.apk should now appear in the bin folder. And we called it for a reason! He does not have a digital signature. Android prohibits install and run applications without a signature. But creating it is not as difficult as it may seem at first glance.
call %JAVA_HOME%/bin/keytool -genkey -validity 10000 -dname "CN=AndroidDebug, O=Android, C=US" -keystore %DEV_HOME%/AndroidTest.keystore -storepass android -keypass android -alias androiddebugkey -keyalg RSA -v -keysize 2048 call %JAVA_HOME%/bin/jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore %DEV_HOME%/AndroidTest.keystore -storepass android -keypass android -signedjar %DEV_HOME%/bin/AndroidTest.signed.apk %DEV_HOME%/bin/AndroidTest.unsigned.apk androiddebugkey
Actually, these lines launch 2 Java utilities that have nothing to do with the Android SDK - but they are necessary. The first one creates the AndroidTest.keystore file (check its availability), and the second one connects this file to
AndroidTest.unsigned.apk . It turns out the file
AndroidTest.signed.apk . Here is such a wild craft files. But once you create a bat-script, run it - and it will do it all automatically.
I think it is not worth spending time on a detailed analysis of the options of these utilities within this article. You just need to grasp the point - they take
AndroidTest.unsigned.apk , sign it with the
AndroidTest.keystore file and save to
AndroidTest.signed.apk . If you wish, you can read
in the documentation .
You will most likely have the warning "
Warning: No -tsa or -tsacert is provided and this jar ... ", but do not pay attention.
Launch
Now that we have finally compiled our apk-file, we can run it. Connect usb to your device, or run the emulator. And then do
call %ADB% uninstall %PACKAGE% call %ADB% install %DEV_HOME%/bin/AndroidTest.signed.apk call %ADB% shell am start %PACKAGE%/%PACKAGE%.%MAIN_CLASS%
Actually, the first line removes the program, if it is already there. For re-launches useful. The second - installs the APK on your device or emulator. The third one runs. Let's take a closer look at her arguments:
- shell - we want to execute some commands on our device
- am - use for execution of commands the activity manager
- start - we want to start some activity
- the package name and the full class name (including the package) that we start
Attention - during installation, a confirmation dialog box may appear on the device. If it is not approved in time, the installation will occur with the error
[INSTALL_FAILED_USER_RESTRICTED] . Also, you may have a question - why do
uninstall / install instead of
install -r . I did so for the purity of the experiment. The script completely removes all the products of its activities and creates them from scratch with each launch. Even the keys. You can use
install -r , but then you should remove the code that is responsible for the re-creation of keys. Otherwise, you will encounter an error
[INSTALL_FAILED_UPDATE_INCOMPATIBLE] .
If everything went well, you will see something like this:
Conclusion
After all the files have been assembled, the directory tree should look something like this.
β AndroidManifest.xml β AndroidTest.keystore β Clear.bat β Compile.bat β ββββbin β AndroidTest.signed.apk β AndroidTest.unsigned.apk β classes.dex β ββββres β ββββvalues β strings.xml β ββββsrc ββββcom ββββexample ββββtestapp MainActivity.java R.java
Now you can visually see and understand how the assembly of the android application occurs at a lower level. When you use IDE - if the assembly suddenly goes wrong (and this often happens) - you can get the situation right. Also note that the final apk-file
takes only about 4 kilobytes .
I post the archive of the project . Please note that I added another small script there - Clear.bat. It deletes all files created during the assembly. And put his launch on top of Compile.bat. Also added comments using Rem - in steps.
Thus, the script produces a
complete cleanup and reassembly of the project , including the signature, as well as its removal on the device, installation and launch.
My parameters
PCOS: Windows 10 Pro x64
JDK: 1.8.0_73
Android SDK: 24
Mobile deviceModel: Meizu MX4
Android: 5.1
OS: Flyme 5.6.8.9 beta