前言

作者使用命令行进行开发已经习惯了,
刚开始Android 开发就遇到了一些问题,
网上搜寻很多资料和书籍都是基于 Android Studio的,
今天偶然在简书看到了一篇基于命令行构建应用的教程,
因此特地写下此教程,并表示感谢下面两篇文章的作者。

我为什么使用命令行开发
其实买本 Android 书(基于 Android Studio)进行学习也是一个不错的选择!
比起在命令行中进行软件开发,我更不愿意去学习和研究如何使用一个软件,
一来是浪费时间精力去学习使用软件,二来是不熟悉和无法掌握软件的构建过程,
另外是能提高开发效率(直接编辑文件+命令编译远比在软件中点来点去要方便多)。
但无论如何,从命令中构建程序可使自己更了解项目的目录结构及每个文件所存在的意义。

插曲
此教程仅告诉你如何从下载、安装、配置并直到编译成 apk 文件并测试安装的过程。
如果你不太懂下面的一些操作,可以参考我另一篇文章 – [剖析一个项目][0]
这篇文章会告诉你那些文件或目录是必须有的,那些是可以改变的,那些是不需要的。

本文全程参考于以下三个地址:

本次测试环境各软件版本

  • 测试系统:Arch Linux 64 bit 4.14.3-1
  • JDK 版本:openjdk 7 即:1.7.0_151
  • Android platform API 版本:26.8.0.0_r02-1
  • Android SDK tool 版本:26.1.1-1
  • Android platform tool 版本:r27.0.0-1
  • Android Build tool 版本:r27.0.2-1
  • 本次测试的应用程序 – 简单的 Hello World 程序

下载和安装(参考于Arch Wiki)

下载 JDK ,之所以下载它是因为它包含一个编译器 javac
Arch Packages/Extra: AUR
官网: Oracle java
# pacman -S jdk7-openjdk
下载 Android SDK 相关工具

  1. 下载 Android SDK 基础工具(包含一些管理 sdk 的命令和设置)
    Arch: Android sdk tool
    官网: Android sdk tool
    # yaourt -S android-sdk
  2. 下载 Android 平台相关工具(包含一些和平台相关的工具)
    Arch: Android platform tool
    官网: Android platform tool
    # yaourt -S android-sdk-platform-tools
  3. 下载 Android 编译工具(包含和编译软件相关的工具)
    Arch: Android Build tool 27
    官网: Android Build tool 27
    # yaourt -S android-sdk-build-tools
  4. 下载 Android SDK 平台 API(包含软件要使用的对应平台的 api)
    参考 Wiki: 下拉直到标题为 Android SDK Platform APK
    Arch: 本地测试版本: Android platform 26
    官网: Android platform 26
    # yaourt -S android-platform-26
  5. 下载 System 镜像(可选)
    这是可选的。
    如果你打算在电脑上运行程序,就需要下载这个镜像。
    如果你更我一样使用真机进行测试,则忽略这个部分。
    因为我没有安装过,所以请参考 Arch Wiki 官方

提示:

  • JDK 有两个版本(openjdk 和 oraclejdk),但我下载的是 openjdk
  • 貌似 Android API 的版本 和 Build tool 的版本是对应的
  • JDK 的版本不能低于Android api 的版本,否则可能无法编译。
  • 一般情况下,下载最新的 JDK 和最新的 Android SDK 及 Andriod Build tool

重要声明:

  • 我初次接触 Android 开发,所以对于各版本匹配不是很明白。
  • 使用 openjdk 配合以上版本的 Android 26 编译 Hello world 正常。
  • 我不知道 openjdk 编译其他组件是否正常,因为我才刚开始。
  • 我不知道以上各版本匹配是否合适于开发各种应用,毕竟 openjdk 是开源的。
  • 如果有懂这方面的并且不嫌麻烦且乐意的话,
    还清发一份教程到我邮箱谢谢 xiaocstudio@gmailcom。非常感谢!

在我的系统上:如果你跟我一样从 AUR 安装,那么
所有关于Android 的 sdk 及 tool 都安装在 /opt 目录下
java 被安装于 /usr/lib/jvm/ 目录下

配置环境变量

对于 Arch Linux 系统:
打开 home 目录下的 .bashrc 文件并添加环境变量。
更改之前请先做一个测试,
因为对于可以搜寻到软件位置的,则不必再配置。

  • 测试 java,或显示版本
    $ java -version
  • 测试 Android sdk tools
    $ android
  • 测试 Android platform tools
    $ adb
  • 测试 Android Build tools
    $ aapt

对于提示没有该命令的,则需要添加环境变量到 ~/.bashrc
在我的系统中的 ~/.bashrc 如下

1
2
3
4
5
6
7
# 尽管我 jdk 版本这样设置但还是 jdk7 我也纳闷!!
export JAVA_HOME=/usr/lib/jvm/java-9-openjdk
export PATH=$JAVA_HOME/jre/bin:$PATH

# 依次添加 tools 目录,platform-tools 目录,build-tools目录
export ANDROID_HOME=/opt/android-sdk
export PATH=$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/:$PATH

提示:确保以上测试命令都可运行。

构建项目之创建目录结构

基于 Hello World 程序
第一阶段

  1. 创建项目名字 HelloWorld
    $ mkdir HelloWorld
  2. 进入项目根目录创建 AndroidManifest.xml 文件
    $ touch AndroidManifest.xml
  3. 我们需要一个源码目录,来保存我们的源代码
    $ mkdir src
  4. 我们需要一个资源目录,来保存我们的资源文件
    $ mkdir res
  5. 我们需要一个bin目录,来存放编译好的可指向文件
    $ mkdir bin
  6. 我们需要一个obj目录,来存放编译期间的中间码文件
    $ mkdiri obj

第二阶段

  1. 为了编译 java 文件,我们需要在 src/ 创建一个库为 a
    $ mkdir src/a
  2. 在库里创建一个包,名为 b
    $ mkdir src/a/b
  3. 在包里创建一个类别,名为 c
    $ mkdir src/a/b/c
  4. 在类别里创建我们的第一个程序源文件名为 MainActivity.java
    $ touch src/c/b/c/MainActivity.java

第三阶段

  1. 我们需要在 res/ 下创建一个 drawable 目录来存放程序图标资源名为 logo.png
    $ mkdir res/drawable
    将图片改名为 logo.png 移入 HelloWorld/res/drawable/ 中
  2. 我们还得创建一个 values 目录来存放我们程序所用到的字符串资源
    $ mkdir res/values
    $ mkdir res/values/strings.xml

编辑文件,则写代码

这里不教你如何写代码,直接复制代码测试是否成功编译。

  1. 编辑 HelloWorld/AndroidManifest.xml 文件,写入以下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="a.b.c"
    android:versionCode="0"
    android:versionName="1.0">

    <uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
    android:icon="@drawable/logo"
    android:label="@string/app_name">

    <activity
    android:name="a.b.c.MainActivity"
    android:label="@string/app_name">
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>

    </application>

    </manifest>
  2. 编辑 HelloWorld/res/values/strings.xml 文件,写入以下内容

    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <string name="app_name">My Apps</string>
    <string name="hello_world">Hello,world!</string>
    </resources>
  3. 编辑我们的程序文件,HelloWorld/src/a/b/c/MainActivity.java 写入以下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package a.b.c;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;

    public class MainActivity extends Activity
    {
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    TextView textView = new TextView(this);
    String helloWorld = getResources().getString(R.string.hello_world);
    textView.setText(helloWorld);
    setContentView(textView);
    }
    }

构建项目之编译和生成

以下命令运行之前要确保你在项目根目录下

  • 打包资源文件并生成 R 文件
    aapt package -v -f -S res -J ./ -M AndroidManifest.xml -I $ANDROID_HOME/platforms/android-26/android.jar
    此时在根目录下生成一个 R.java 文件,所有资源都打包在里面
  • 编译源文件(.java)生成二进制文件(.class)
    javac -verbose -d obj -classpath $ANDROID_HOME/platforms/android-26/android.jar -sourcepath src src/a/b/c/MainActivity.java ./R.java
    会依次生成以下这些文件位于 obj/ 目录下
    boj/
    ├── MainActivity.class
    ├── R$attr.class
    ├── R.class
    ├── R$drawable.class
    └── R$string.class

  • 转换所有 .class 文件并生成 dex 可执行文件
    dx --dex --verbose --output=bin/classes.dex obj
    会在 bin 目录下生成 class.dex 文件
    bin
    └── classes.dex

  • 打包并生成 apk 文件
    aapt package -v -f -M AndroidManifest.xml -S res -I $ANDROID_HOME/platforms/android-26/android.jar -F bin/helloworld.apk bin
    会在 bin 目录下生成未签名的 helloword.apk 文件
    bin
    ├── classes.dex
    └── helloworld.apk

  • 签名 apk 文件

  1. 要有签名文件,你肯能没有,我们这里是为了测试所以临时创建一个加密文件
    keytool -v -genkeypair -validity 10000 -dname "CN=company_name,OU=organisational_unit,O=organisation,L=location,S=state,c=country_code" -keystore myapp.keystore -storepass my_keys -keypass key_password -alias key_alias -keyalg RSA
    其中 my_keys 加密密码,后面会验证
    会在当前目录下创建一个 myapp.keystore 文件

  2. 签名 apk
    jarsigner -verbose -keystore ./myapp.keystore -keypass key_password -signedjar bin/HelloWorld.apk bin/helloworld.apk key_alias
    会在 bin 目录下生成 HelloWorld.apk
    bin
    ├── classes.dex
    ├── helloworld.apk 未签名
    └── HelloWorld.apk 已签名

测试软件

由于我是在真机测试,所以把 HelloWorld.apk 传到手机安装后即可运行了。