• [编译] 5、在Linux下搭建安卓APP的开发烧写环境(makefile版)—— 在Linux上用命令行+VIM开发安卓APP


    星期三, 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(#1)

      sdkmanager是用来查看、安装、更新、卸载Android SDK的命令行工具,官方说明如下:

      The sdkmanager is a command line tool that allows you to view, install, update, and uninstall packages for the Android SDK.
      The sdkmanager tool is provided in the Android SDK Tools package (25.2.3 and higher) and is located in android_sdk/tools/bin/.

      列出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之后(#4)

    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'

    [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
    
  • 相关阅读:
    Linux系统负载
    full nat
    close wait 状态的随想
    记录一下 性能分析问题
    golang 反射
    socket里面那个又爱又恨的锁
    Difference between skbuff frags and frag_list
    浅析TCP协议---转载
    http 怎样关闭
    http 响应 ngx_http_send_header ngx_http_output_filter
  • 原文地址:https://www.cnblogs.com/zjutlitao/p/9672376.html
Copyright © 2020-2023  润新知