星期三, 19. 九月 2018 02:19上午 - BEAUTIFULZZZZ
0)前言
本文不讨论用IDE和文本编辑器开发的优劣,是基于以下两点考虑去尝试用命令行编译安卓APP的:
- 了解安卓APP的编译过程,了解IDE干了什么事;
- 放在打包服务器上需要自动化生成APP的脚本;
1)安装配置环境
安装java
sudo apt-get install openjdk-8-jdk-headless
**note: **安装之前先要卸载之前版本的java,否则会报错!!! [error-1].
安装SDK tools
wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
建议将其解压到
/opt
目录下:mkdir -p /opt/android-sdk
unzip sdk-tools-linux-3859397.zip -d /opt/android-sdk
用sdkmanager安装SDK
sdkmanager是用来查看、安装、更新、卸载Android SDK的命令行工具,官方说明如下:
列出Installed packages和Available Packages,查看包安装情况:
cd /opt/android-sdk/tools/bin
./sdkmanager --list
安装platform tools 19(写文章时最新的是26),该工具包含adb和fastboot,该工具对应的API级别也是19:
./sdkmanager "platform-tools" "platforms;android-19"
安装build tools 26.0.1(最新的),该工具包含aapt、apksigner、zipalign等编译、认证、打包工具:
./sdkmanager "platform-tools" "build-tools;26.0.1"
最后你会在/opt/android-sdk/中看到build-tools、paltforms、tools三个文件夹~
2)编写简单Hello World程序
创建工程文件夹
cd ~/Downloads/
mkdir HelloAndroid
cd HelloAndroid
创建工程文件tree
mkdir -p src/com/example/helloandroid
mkdir obj
mkdir bin
mkdir -p res/layout
mkdir res/values
mkdir res/drawable
Make the file src/com/example/helloandroid/MainActivity.java and put that inside:
package com.example.helloandroid;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Make the strings.xml file in the res/values folder. It contains all the text that your application uses:
<resources>
<string name="app_name">A Hello Android</string>
<string name="hello_msg">Hello Android!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">MainActivity</string>
</resources>
The activity_main.xml is a layout file which have to be in res/layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_msg"
tools:context=".MainActivity" />
</RelativeLayout>
You also have to add the file AndroidManifest.xml at the root:
<?xml version='1.0'?>
<manifest xmlns:a='http://schemas.android.com/apk/res/android' package='com.example.helloandroid' a:versionCode='0' a:versionName='0'>
<application a:label='A Hello Android'>
<activity a:name='com.example.helloandroid.MainActivity'>
<intent-filter>
<category a:name='android.intent.category.LAUNCHER'/>
<action a:name='android.intent.action.MAIN'/>
</intent-filter>
</activity>
</application>
</manifest>
最终文件构成为:
➜ HelloAndroid tree
.
├── AndroidManifest.xml
├── bin
├── libs
├── obj
├── res
│ ├── drawable
│ ├── layout
│ │ └── activity_main.xml
│ └── values
│ └── strings.xml
└── src
└── com
└── example
└── helloandroid
└── MainActivity.java
3)编译工程
将工程路径设置为变量(方便一会使用):
export PROJ=~/Downloads/HelloAndroid
First, we need generate the R.java file which is necessary for our code:
cd /opt/android-sdk/build-tools/26.0.1/
./aapt package -f -m -J $PROJ/src -M $PROJ/AndroidManifest.xml -S $PROJ/res -I /opt/android-sdk/platforms/android-19/android.jar
compile the .java files:
cd ~/Downloads/HelloAndroid
javac -d obj -classpath src -bootclasspath /opt/android-sdk/platforms/android-19/android.jar src/com/example/helloandroid/*.java
The compiled .class files are in obj folder, but Android can’t read them. We have to translate them in a file called “classes.dex” which will be read by the dalvik Android runtime:
cd /opt/android-sdk/build-tools/26.0.1/
./dx --dex --output=$PROJ/bin/classes.dex $PROJ/obj
We can now put everything in an APK:
./aapt package -f -m -F $PROJ/bin/hello.unaligned.apk -M $PROJ/AndroidManifest.xml -S $PROJ/res -I /opt/android-sdk/platforms/android-19/android.jar
cp $PROJ/bin/classes.dex .
./aapt add $PROJ/bin/hello.unaligned.apk classes.dex
If you want, you can check the content of the package like this:
./aapt list $PROJ/bin/hello.unaligned.apk
至此,我们生成了一个hello.unaligned.apk文件,但是,它是不能安装到安卓手机里面的!因为它unaligned && unsigned。
4)Align and Sign the package
keytool -genkeypair -validity 365 -keystore mykey.keystore -keyalg RSA -keysize 2048
cd /opt/android-sdk/build-tools/26.0.1/
./zipalign -f 4 $PROJ/bin/hello.unaligned.apk $PROJ/bin/hello.apk
./apksigner sign --ks mykey.keystore $PROJ/bin/hello.apk
使用keytool创建一个keystore,只要依次回答其问题即可,输入密码自己别忘了,今后会用到!运行成功之后会生成一个mykey.keystore文件,用于今后给apk签名。
note: 记住务必要先Align,然后再Sign,否则会出错 [error-2].
5)真机安装测试
安装并运行比较简单:
adb install $PROJ/bin/hello.apk
adb shell am start -n com.example.helloandroid/.MainActivity
note: 一般运行安装前,建议先运行adb logcat看看安卓有没有连接并开启开发者模式
6)自动化脚本
为了不用每次都要手输上面的每一步,我们将上面的操作整理成一个脚本run.sh:
➜ HelloAndroid cat run.sh
#!/bin/bash
set -e
AAPT="/opt/android-sdk/build-tools/26.0.1/aapt"
DX="/opt/android-sdk/build-tools/26.0.1/dx"
ZIPALIGN="/opt/android-sdk/build-tools/26.0.1/zipalign"
APKSIGNER="/opt/android-sdk/build-tools/26.0.1/apksigner" # /!\ version 26
PLATFORM="/opt/android-sdk/platforms/android-19/android.jar"
function build(){
echo "Generating R.java file..."
$AAPT package -f -m -J src -M AndroidManifest.xml -S res -I $PLATFORM
echo "Compiling..."
javac -d obj -classpath src -bootclasspath $PLATFORM src/com/example/helloandroid/*.java
echo "Translating in Dalvik bytecode..."
$DX --dex --output=classes.dex obj
echo "Making APK..."
$AAPT package -f -m -F bin/hello.unaligned.apk -M AndroidManifest.xml -S res -I $PLATFORM
$AAPT add bin/hello.unaligned.apk classes.dex
echo "Aligning and signing APK..."
$ZIPALIGN -f 4 bin/hello.unaligned.apk bin/hello.apk
$APKSIGNER sign --ks mykey.keystore bin/hello.apk
}
function clean(){
echo "Cleaning..."
rm -rf classes.dex
rm -rf bin/*
rm -rf obj/*
rm -rf src/com/example/helloandroid/R.java
}
function program(){
echo "Launching..."
adb install -r bin/hello.apk
adb shell am start -n com.example.helloandroid/.MainActivity
}
if [ "$1" == "all" ]; then
clean
build
program
elif [ "$1" == "clean" ]; then
clean
elif [ "$1" == "build" ]; then
build
elif [ "$1" == "program" ]; then
program
else
echo "error"
fi
并编写一个makefile脚本,通过调用run.sh实现编译、清除、安装各种操作:
➜ HelloAndroid cat makefile
clean:
./run.sh clean
build:
./run.sh build
program:
./run.sh program
all:
./run.sh all
至此,我们完成了一个简单的命令行版的Hello World工程!当然,大多数非常复杂的安卓工程需要用IDE去开发,或者开发用IDE打包用命令 ~ 下次我会把一个稍微复杂点的蓝牙安卓工程改造成命令行版。
常见错误
error-1:
➜ HelloAndroid javac -d obj -classpath src -bootclasspath /opt/android-sdk/platforms/android-19/android.jar src/com/example/helloandroid/*.java
javac: option --boot-class-path not allowed with target 1.10
error-2:
➜ HelloAndroid ./build.sh test
Cleaning...
Generating R.java file...
Compiling...
Translating in Dalvik bytecode...
Making APK...
'classes.dex'...
Aligning and signing APK...
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.android.apksigner.PasswordRetriever (file:/opt/android-sdk/build-tools/26.0.1/lib/apksigner.jar) to method java.io.Console.encoding()
WARNING: Please consider reporting this to the maintainers of com.android.apksigner.PasswordRetriever
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Keystore password for signer #1:
Launching...
Failed to install bin/hello.apk: Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from /data/app/vmdl1775334521.tmp/base.apk: META-INF/MYKEY.SF indicates /data/app/vmdl1775334521.tmp/base.apk is signed using APK Signature Scheme v2, but no such signature was found. Signature stripped?]
需要If you need to run zipalign, do it before the APK is signed,因此将APKSIGNER放在ZIPALIG之后。
error-3:
$ avdmanager
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156)
at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75)
at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81)
at com.android.sdklib.tool.AvdManagerCli.run(AvdManagerCli.java:213)
at com.android.sdklib.tool.AvdManagerCli.main(AvdManagerCli.java:200)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
... 5 more
需要加:export JAVA_OPTS='-XX:+IgnoreUnrecognizedVMOptions --add-modules java.se.ee'
LINKS
[1].sdkmanager 用法
[2].Build your app from the command line
[3].How to make Android apps without IDE from command line
[4].Android signing apk signature V2
[5].What are the Android SDK build-tools, platform-tools and tools? And which version should be used?
[6].Not able to build code after installing latest java version 1.9
[7].Android SDK is not installed or is not configured properly, environment looks ok
[8].Command line tools only Download
[9].GITHUB this project
@beautifulzzzz
智能硬件、物联网,热爱技术,关注产品
博客:http://blog.beautifulzzzz.com
园友交流群:414948975