天天看點

在Android Studio 3.0中使用C語言以及彙編語言

從Android Studio 2.2起,我們可以直接通過CMake在Android Studio中寫C源代碼以及彙編代碼,而不需要通過NDK編譯工具鍊生成好.so檔案後再導入到工程中。

而到了Android 3.0,使用C代碼就更友善了,我們通過工程向導設定使用C語言之後,向導會自動建立一個完整的利用C++語言JNI的工程,我們隻要把預設的那個惡心的cpp源檔案修改為C源檔案即可。下面我将詳細列出直接通過Android Studio 3.0來編寫C代碼和彙編代碼的步驟。對于其中的細節,如果各位有條件的話可以參考Android開發者官網中關于NDK的資訊:

這條是關于CMake的:https://developer.android.com/ndk/guides/cmake.html

這條是關于在項目中使用C++代碼的:https://developer.android.com/studio/projects/add-native-code.html

1、下載下傳Android Studio 3.0,然後第一次打開Android Studio之後會彈出一個歡迎界面。我們點選右下角的Configure按鈕,如下圖所示。

在Android Studio 3.0中使用C語言以及彙編語言

2、然後會彈出一個清單框,我們選擇第一個:SDK Manager,然後會彈出如下圖所示的對話框。我們在中間欄點選“SDK Tools”,這裡需要至少增加三個選項——“CMake”,“LLDB”以及“NDK”。

在Android Studio 3.0中使用C語言以及彙編語言

3、選完了之後,我們開始安裝。安裝完之後,我們開始建立一個全新的工程。我們在配置這個工程的時候可以根據自己的喜好進行設定,當然大部分用預設的配置即可,不過我們這裡要增加對C語言支援的選項,如下圖所示。

在Android Studio 3.0中使用C語言以及彙編語言

4、這裡勾選之後,後面的配置可以不用管。點選Finish之後需要等待一些時間讓IDE做項目配置以及初始編譯。最後我們進入到了主工程中。

到了主工程中,我們先别着急去寫C代碼,因為此時還沒有C源檔案,我們可以先配置build.gradle(Module:app)配置檔案,這裡列出android配置部分:

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.greengames.zennychen.ctest"
        minSdkVersion 23
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                // 使用NEON技術
                arguments "-DANDROID_ARM_NEON=TRUE"

                // C語言使用GNU11标準
                cFlags "-std=gnu11"
            }
        }
        ndk {
            // Specifies the ABI configurations of your native
            // libraries Gradle should build and package with your APK.
            abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}
           

這裡各位請注意内部的cmake部分,這裡使用了建構參數以及全局編譯選項。然後再注意一下對ndk的配置,這裡加入了在本項目工程中所支援的ABI架構。ABI架構可以根據自己的需求進行配置,不過筆者所列出的這幾個已經能涵蓋目前活躍的絕大部分的安卓裝置了。

接下去,我們可以把IDE自動生成的native-lib.cpp檔案名修改為native-lib.c,然後編輯成以下C代碼:

//
// Created by Zenny Chen on 2017/10/27.
//

#include <jni.h>
#include <stdio.h>
#include <string.h>

#ifndef __x86_64__

/**
 * 測試内聯彙編,分别根據AArch32架構以及AArch64架構來實作一個簡單的減法計算
 * @param a 被減數
 * @param b 減數
 * @return 減法得到的內插補點
 */
static int __attribute__((naked, pure)) MyASMTest(int a, int b)
{
#ifdef __arm__

    asm(".thumb");
    asm(".syntax unified");

    asm("sub r0, r0, r1");
    asm("add r0, r0, #1");  // 為了區分目前用的是AArch32還是AArch64,這裡對于AArch32情況下再加1
    asm("bx lr");

#else

    asm("sub w0, w0, w1");
    asm("ret");

#endif
}

#else

extern int MyASMTest(int a, int b);

#endif

JNICALL jstring Java_com_greengames_zennychen_ctest_MainActivity_stringFromJNI
        (JNIEnv *env, jobject this)
{
    char strBuf[128];

    sprintf(strBuf, "Hello from C! ASM test result: %d", MyASMTest(6, 4));

    return (*env)->NewStringUTF(env, strBuf);
}
           

上述C代碼對AArch32與AArch64架構下的内聯彙編以及對x86_64架構下待會兒要使用的獨立的YASM彙編器編譯的彙編代碼進行了引用。下面我們可以在預設的cpp檔案夾下再新增一個名為test.asm的彙編檔案,輸入以下x86_64的彙編代碼:

; 這是一個彙編檔案
; YASM的注釋風格使用分号形式

global MyASMTest

section .text

MyASMTest:

    sub     edi, esi
    mov     eax, edi
    ret
           

最後,我們對CMakeLists.txt進行編輯,這裡面需要引入YASM彙編器。

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

if(${ANDROID_ABI} STREQUAL "x86_64")
    set(asm_SRCS src/main/cpp/test.asm)
endif()

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.c ${asm_SRCS})

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
           

我們這裡使用了條件判斷,隻有在目前所編譯的架構為x86_64的情況下才把test.asm給加入到所編譯的庫中。

上述檔案都編輯好之後,我們就可以開測了。如果各位沒有x86的安卓裝置,那麼我們可以建立一個基于x86-64架構的模拟器,x86-64架構的模拟器運作速度還是挺快的,而對于ARM架構的處理器,我們最好用真機來測。

最後,筆者提供一下各位可用于參考的Google官方給的使用Android Studio來寫JNI的例子:https://github.com/googlesamples/android-ndk

這篇Wiki文章則比較詳細地介紹了NASM的彙編基本用法:https://en.wikipedia.org/wiki/Netwide_Assembler

由于YASM衍生自NASM,是以在文法和用法上都差不多,并且YASM使用了更自由的BSD License,是以各位可以毫無顧慮地使用~

繼續閱讀