android studio使用CMake和NDK,實現應用自身被卸載時打開某一網址

来源:http://www.cnblogs.com/qynprime/archive/2017/12/18/8057770.html
-Advertisement-
Play Games

實現應用自身被卸載時打開某一網址的c代碼 以上c文件源碼和使用的文章出自: http://blog.csdn.net/qq_21793463/article/details/48246685 向您的項目添加 C 和 C++ 代碼 搭配使用 Android Studio 2.2 或更高版本與 Andr ...


實現應用自身被卸載時打開某一網址的c代碼

MyActivity:
public class MyActivity extends Activity {
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       init();
    }

    private native void init();

    static {
        Log.d("onEvent", "load jni lib");
        System.loadLibrary("hello-jni");
    }

}
CMakeLists.txt:
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library.
             hello-jni

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/hello-jni.c )

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

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

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                     hello-jni

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

 add_library( app-glue
              STATIC
              ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

 # You need to link static libraries against your shared native library.
 target_link_libraries( hello-jni app-glue ${log-lib} )
hello-jni.c:
1. 記住如果是要在自己項目中添加了jni文件,這個類名“Java_com_example_qyn_projectqyn_activitys_MainActivity_init”要換成自己的喲。其中MainActivity中放有調用該c文件產生的.so文件的代碼。
2. "/data/data/
com.example.qyn.projectqyn" 自己的包名也要改喲。
3.
execlp這個函數中的網址改成自己的喲。
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <android/log.h>
#include <unistd.h>
#include <sys/inotify.h>

/* 巨集定義begin */
//清0巨集
#define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)

//LOG巨集定義
#define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
#define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
#define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
#define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)


/* 內全局變數begin */
static char c_TAG[] = "onEvent";
static jboolean b_IS_COPY = JNI_TRUE;

/*jstring
Java_com_example_qyn_projectqyn_activitys_MainActivity_init( JNIEnv* env,
                                                 jobject thiz )
{

 jstring tag = (*env)->NewStringUTF(env, c_TAG);

      //初始化log
      LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
              , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init OK"), &b_IS_COPY));

      //fork子進程,以執行輪詢任務
      pid_t pid = fork();
      if (pid < 0)
      {
          //出錯log
          LOG_ERROR((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                  , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork error !!!"), &b_IS_COPY));
      }
      else if (pid == 0)
      {
          //子進程輪詢"/data/data/com.example.qyn.projectqyn"目錄是否存在,若不存在則說明已被卸載
         while (1)
          {
              FILE *p_file = fopen("/data/data/com.example.qyn.projectqyn", "r");
              if (p_file != NULL)
              {
                  fclose(p_file);

                  //目錄存在log
                  LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                              , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "I'm OK !!!"), &b_IS_COPY));

                  sleep(1);
              }
              else
              {
                  //目錄不存在log
                  LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                              , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "I'm NOT OK !!!"), &b_IS_COPY));

                  //執行命令am start -a android.intent.action.VIEW -d https://www.google.com
                  execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", "https://www.google.com", (char *)NULL);

                    //4.2以上的系統由於用戶許可權管理更嚴格,需要加上 --user 0
                  //execlp("am", "am", "start","--user", "0" ,"-a", "android.intent.action.VIEW", "-d", "https://www.google.com, (char *)NULL);
              }
          }
      }
      else
      {
          //父進程直接退出,使子進程被init進程領養,以避免子進程僵死
      }
   return (*env)->NewStringUTF(env, "Hello from JNI !");
}*/



jstring
Java_com_example_qyn_projectqyn_activitys_MainActivity_init( JNIEnv* env,
                                                 jobject thiz )
{
    jstring tag = (*env)->NewStringUTF(env, c_TAG);

    //初始化log
    LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
            , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init OK"), &b_IS_COPY));

    //fork子進程,以執行輪詢任務
    pid_t pid = fork();
    if (pid < 0)
    {
        //出錯log
        LOG_ERROR((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork failed !!!"), &b_IS_COPY));
    }
    else if (pid == 0)
    {
        //子進程註冊"/data/data/pym.test.uninstalledobserver"目錄監聽器
        int fileDescriptor = inotify_init();
        if (fileDescriptor < 0)
        {
            LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                    , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_init failed !!!"), &b_IS_COPY));

             exit(1);
        }

        int watchDescriptor;
        watchDescriptor = inotify_add_watch(fileDescriptor, "/data/data/com.example.qyn.projectqyn", IN_DELETE);
        if (watchDescriptor < 0)
        {
            LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                    , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_add_watch failed !!!"), &b_IS_COPY));

            exit(1);
        }

        //分配緩存,以便讀取event,緩存大小=一個struct inotify_event的大小,這樣一次處理一個event
        void *p_buf = malloc(sizeof(struct inotify_event));
        if (p_buf == NULL)
        {
            LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                    , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "malloc failed !!!"), &b_IS_COPY));

            exit(1);
        }
        //開始監聽
        LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                    , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "start observer"), &b_IS_COPY));
        size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));

        //read會阻塞進程,走到這裡說明收到目錄被刪除的事件,註銷監聽器
        free(p_buf);
        inotify_rm_watch(fileDescriptor, IN_DELETE);

        //目錄不存在log
        LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                    , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "uninstalled"), &b_IS_COPY));

        //執行命令am start -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html
       // execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", "http://shouji.360.cn/web/uninstall/uninstall.html", (char *)NULL);
        //4.2以上的系統由於用戶許可權管理更嚴格,需要加上 --user 0
       execlp("am", "am", "start","--user", "0" ,"-a", "android.intent.action.VIEW", "-d", "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1513335535834&di=14e3647eceeda732db1b07f5f5627568&imgtype=0&src=http%3A%2F%2Fwww.jituwang.com%2Fuploads%2Fallimg%2F151123%2F258084-15112306450076.jpg", (char *)NULL);

    }
    else
    {
        //父進程直接退出,使子進程被init進程領養,以避免子進程僵死
    }

    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

 以上c文件源碼和使用的文章出自: http://blog.csdn.net/qq_21793463/article/details/48246685

 

向您的項目添加 C 和 C++ 代碼

搭配使用 Android Studio 2.2 或更高版本與 Android Plugin for Gradle 版本 2.2.0 或更高版本時,您可以將 C 和 C++ 代碼編譯到 Gradle 與 APK 一起打包的原生庫中,將這類代碼添加到您的應用中。您的 Java 代碼隨後可以通過 Java 原生介面 (JNI) 調用您的原生庫中的函數。如果您想要詳細瞭解如何使用 JNI 框架,請閱讀 Android 的 JNI 提示

Android Studio 用於構建原生庫的預設工具是 CMake。由於很多現有項目都使用構建工具包編譯其原生代碼,Android Studio 還支持 ndk-build。如果您想要將現有的 ndk-build 庫導入到您的 Android Studio 項目中,請參閱介紹如何配置 Gradle 以關聯到您的原生庫的部分。不過,如果您在創建新的原生庫,則應使用 CMake。

本頁面介紹的信息可以幫助您使用所需構建工具設置 Android Studio、創建或配置項目以支持 Android 上的原生代碼,以及構建和運行應用。

:如果您的現有項目使用已棄用的 ndkCompile 工具,則應先打開 build.properties 文件,並移除以下代碼行,然後再將 Gradle 關聯到您的原生庫

// Remove this line
android.useDeprecatedNdk = true

實驗性 Gradle 的用戶註意事項:如果您是以下任意一種情況,請考慮遷移到插件版本 2.2.0 或更高版本並使用 CMake 或 ndk-build 構建原生庫:您的原生項目已經使用 CMake 或者 ndk-build;但是您想要使用穩定版本的 Gradle 構建系統;或者您希望支持插件工具,例如 CCache。否則,您可以繼續使用實驗性版本的 Gradle 和 Android 插件

下載 NDK 和構建工具


要為您的應用編譯和調試原生代碼,您需要以下組件:

  • Android 原生開發工具包 (NDK):這套工具集允許您為 Android 使用 C 和 C++ 代碼,並提供眾多平臺庫,讓您可以管理原生 Activity 和訪問物理設備組件,例如感測器和觸摸輸入。
  • CMake:一款外部構建工具,可與 Gradle 搭配使用來構建原生庫。如果您只計劃使用 ndk-build,則不需要此組件。
  • LLDB:一種調試程式,Android Studio 使用它來調試原生代碼

您可以使用 SDK 管理器安裝這些組件:

  1. 在打開的項目中,從菜單欄選擇 Tools > Android > SDK Manager
  2. 點擊 SDK Tools 標簽。
  3. 選中 LLDBCMake 和 NDK 旁的覆選框,如圖 1 所示。

    圖 1. 從 SDK 管理器中安裝 LLDB、CMake 和 NDK。

  4. 點擊 Apply,然後在彈出式對話框中點擊 OK
  5. 安裝完成後,點擊 Finish,然後點擊 OK

創建支持 C/C++ 的新項目


創建支持原生代碼的項目與創建任何其他 Android Studio 項目類似,不過前者還需要額外幾個步驟:

  1. 在嚮導的 Configure your new project 部分,選中 Include C++ Support 覆選框。
  2. 點擊 Next
  3. 正常填寫所有其他欄位並完成嚮導接下來的幾個部分。
  4. 在嚮導的 Customize C++ Support 部分,您可以使用下列選項自定義項目:
    • C++ Standard:使用下拉列表選擇您希望使用哪種 C++ 標準。選擇 Toolchain Default 會使用預設的 CMake 設置。
    • Exceptions Support:如果您希望啟用對 C++ 異常處理的支持,請選中此覆選框。如果啟用此覆選框,Android Studio 會將 -fexceptions標誌添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake。
    • Runtime Type Information Support:如果您希望支持 RTTI,請選中此覆選框。如果啟用此覆選框,Android Studio 會將 -frtti 標誌添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake。
  5. 點擊 Finish

在 Android Studio 完成新項目的創建後,請從 IDE 左側打開 Project 窗格並選擇 Android 視圖。如圖 2 中所示,Android Studio 將添加 cpp 和 External Build Files 組:

圖 2. 您的原生源文件和外部構建腳本的 Android 視圖組。

:此視圖無法反映磁碟上的實際文件層次結構,而是將相似文件分到一組中,簡化項目導航。

  1. 在 cpp 組中,您可以找到屬於項目的所有原生源文件、標頭和預構建庫。對於新項目,Android Studio 會創建一個示例 C++ 源文件 native-lib.cpp,並將其置於應用模塊的 src/main/cpp/ 目錄中。本示例代碼提供了一個簡單的 C++ 函數 stringFromJNI(),此函數可以返回字元串“Hello from C++”。要瞭解如何向項目添加其他源文件,請參閱介紹如何創建新的原生源文件的部分。
  2. 在 External Build Files 組中,您可以找到 CMake 或 ndk-build 的構建腳本。與 build.gradle 文件指示 Gradle 如何構建應用一樣,CMake 和 ndk-build 需要一個構建腳本來瞭解如何構建您的原生庫。對於新項目,Android Studio 會創建一個 CMake 構建腳本 CMakeLists.txt,並將其置於模塊的根目錄中。要詳細瞭解此構建腳本的內容,請參閱介紹如何創建 Cmake 構建腳本的部分。

構建和運行示例應用

點擊 Run 從菜單欄運行應用 後,Android Studio 將在您的 Android 設備或者模擬器上構建並啟動一個顯示文字“Hello from C++”的應用。下麵的概覽介紹了構建和運行示例應用時會發生的事件:

  1. Gradle 調用您的外部構建腳本 CMakeLists.txt
  2. CMake 按照構建腳本中的命令將 C++ 源文件 native-lib.cpp 編譯到共用的對象庫中,並命名為 libnative-lib.so,Gradle 隨後會將其打包到 APK 中。
  3. 運行時,應用的 MainActivity 會使用 System.loadLibrary() 載入原生庫。現在,應用可以使用庫的原生函數 stringFromJNI()
  4. MainActivity.onCreate() 調用 stringFromJNI(),這將返回“Hello from C++”並使用這些文字更新 TextView

Instant Run 與使用原生代碼的項目不相容。Android Studio 會自動停用此功能。

如果您想要驗證 Gradle 是否已將原生庫打包到 APK 中,可以使用 APK 分析器

  1. 選擇 Build > Analyze APK
  2. 從 app/build/outputs/apk/ 目錄中選擇 APK 並點擊 OK
  3. 如圖 3 中所示,您會在 APK 分析器視窗的 lib/<ABI>/ 下看到 libnative-lib.so

    圖 3. 使用 APK 分析器定位原生庫。

提示:如果您想要試驗使用原生代碼的其他 Android 應用,請點擊 File > New > Import Sample 並從 Ndk 列表中選擇示例項目。

向現有項目添加 C/C++ 代碼


如果您希望向現有項目添加原生代碼,請執行以下步驟:

  1. 創建新的原生源文件並將其添加到您的 Android Studio 項目中。
    • 如果您已經擁有原生代碼或想要導入預構建的原生庫,則可以跳過此步驟。
  2. 創建 CMake 構建腳本,將您的原生源代碼構建到庫中。如果導入和關聯預構建庫或平臺庫,您也需要此構建腳本。
    • 如果您的現有原生庫已經擁有 CMakeLists.txt 構建腳本或者使用 ndk-build 並包含 Android.mk 構建腳本,則可以跳過此步驟。
  3. 提供一個指向您的 CMake 或 ndk-build 腳本文件的路徑,將 Gradle 關聯到您的原生庫。Gradle 使用構建腳本將源代碼導入您的 Android Studio 項目並將原生庫(SO 文件)打包到 APK 中。

配置完項目後,您可以使用 JNI 框架從 Java 代碼中訪問您的原生函數。要構建和運行應用,只需點擊 Run 從菜單欄運行應用。Gradle 會以依賴項的形式添加您的外部原生構建流程,用於編譯、構建原生庫並將其隨 APK 一起打包。

創建新的原生源文件

要在應用模塊的主源代碼集中創建一個包含新建原生源文件的 cpp/ 目錄,請按以下步驟操作:

  1. 從 IDE 的左側打開 Project 窗格並從下拉菜單中選擇 Project 視圖。
  2. 導航到 您的模塊 > src,右鍵點擊 main 目錄,然後選擇 New > Directory
  3. 為目錄輸入一個名稱(例如 cpp)並點擊 OK
  4. 右鍵點擊您剛剛創建的目錄,然後選擇 New > C/C++ Source File
  5. 為您的源文件輸入一個名稱,例如 native-lib
  6. 從 Type 下拉菜單中,為您的源文件選擇文件擴展名,例如 .cpp
    • 點擊 Edit File Types ,您可以向下拉菜單中添加其他文件類型,例如 .cxx 或 .hxx。在彈出的 C/C++ 對話框中,從 Source Extension和 Header Extension 下拉菜單中選擇另一個文件擴展名,然後點擊 OK
  7. 如果您還希望創建一個標頭文件,請選中 Create an associated header 覆選框。
  8. 點擊 OK

創建 CMake 構建腳本

如果您的原生源文件還沒有 CMake 構建腳本,則您需要自行創建一個並包含適當的 CMake 命令。CMake 構建腳本是一個純文本文件,您必須將其命名為 CMakeLists.txt。本部分介紹了您應包含到構建腳本中的一些基本命令,用於在創建原生庫時指示 CMake 應使用哪些源文件。

:如果您的項目使用 ndk-build,則不需要創建 CMake 構建腳本。提供一個指向您的 Android.mk 文件的路徑,將 Gradle 關聯到您的原生庫

要創建一個可以用作 CMake 構建腳本的純文本文件,請按以下步驟操作:

  1. 從 IDE 的左側打開 Project 窗格並從下拉菜單中選擇 Project 視圖。
  2. 右鍵點擊 您的模塊 的根目錄並選擇 New > File

    :您可以在所需的任意位置創建構建腳本。不過,在配置構建腳本時,原生源文件和庫的路徑將與構建腳本的位置相關。

  3. 輸入“CMakeLists.txt”作為文件名並點擊 OK

現在,您可以添加 CMake 命令,對您的構建腳本進行配置。要指示 CMake 從原生源代碼創建一個原生庫,請將 cmake_minimum_required()和 add_library() 命令添加到您的構建腳本中:

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies 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.cpp )

使用 add_library() 向您的 CMake 構建腳本添加源文件或庫時,Android Studio 還會在您同步項目後在 Project 視圖下顯示關聯的標頭文件。不過,為了確保 CMake 可以在編譯時定位您的標頭文件,您需要將 include_directories() 命令添加到 CMake 構建腳本中並指定標頭的路徑:

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

CMake 使用以下規範來為庫文件命名:

lib庫名稱.so

例如,如果您在構建腳本中指定“native-lib”作為共用庫的名稱,CMake 將創建一個名稱為 libnative-lib.so 的文件。不過,在 Java 代碼中載入此庫時,請使用您在 CMake 構建腳本中指定的名稱:

static {
    System.loadLibrary(“native-lib”);
}

:如果您在 CMake 構建腳本中重命名或移除某個庫,您需要先清理項目,Gradle 隨後才會應用更改或者從 APK 中移除舊版本的庫。要清理項目,請從菜單欄中選擇 Build > Clean Project

Android Studio 會自動將源文件和標頭添加到 Project 窗格的 cpp 組中。使用多個 add_library() 命令,您可以為 CMake 定義要從其他源文件構建的更多庫。

添加 NDK API

Android NDK 提供了一套實用的原生 API 和庫。通過將 NDK 庫包含到項目的 CMakeLists.txt 腳本文件中,您可以使用這些 API 中的任意一種。

預構建的 NDK 庫已經存在於 Android 平臺上,因此,您無需再構建或將其打包到 APK 中。由於 NDK 庫已經是 CMake 搜索路徑的一部分,您甚至不需要在您的本地 NDK 安裝中指定庫的位置 - 只需要向 CMake 提供您希望使用的庫的名稱,並將其關聯到您自己的原生庫。

將 find_library() 命令添加到您的 CMake 構建腳本中以定位 NDK 庫,並將其路徑存儲為一個變數。您可以使用此變數在構建腳本的其他部分引用 NDK 庫。以下示例可以定位 Android 特定的日誌支持庫並將其路徑存儲在 log-lib 中:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

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

為了確保您的原生庫可以在 log 庫中調用函數,您需要使用 CMake 構建腳本中的 target_link_libraries() 命令關聯庫:

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

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

NDK 還以源代碼的形式包含一些庫,您在構建和關聯到您的原生庫時需要使用這些代碼。您可以使用 CMake 構建腳本中的 add_library() 命令,將源代碼編譯到原生庫中。要提供本地 NDK 庫的路徑,您可以使用 ANDROID_NDK 路徑變數,Android Studio 會自動為您定義此變數。

以下命令可以指示 CMake 構建 android_native_app_glue.c,後者會將 NativeActivity 生命周期事件和觸摸輸入置於靜態庫中並將靜態庫關聯到 native-lib

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

添加其他預構建庫(不需要時,可以不添加這一項)

添加預構建庫與為 CMake 指定要構建的另一個原生庫類似。不過,由於庫已經預先構建,您需要使用 IMPORTED 標誌告知 CMake 您只希望將庫導入到項目中:

add_library( imported-lib
             SHARED
             IMPORTED )

然後,您需要使用 set_target_properties() 命令指定庫的路徑,如下所示。

某些庫為特定的 CPU 架構(或應用二進位介面 (ABI))提供了單獨的軟體包,並將其組織到單獨的目錄中。此方法既有助於庫充分利用特定的 CPU 架構,又能讓您僅使用所需的庫版本。要向 CMake 構建腳本中添加庫的多個 ABI 版本,而不必為庫的每個版本編寫多個命令,您可以使用 ANDROID_ABI 路徑變數。此變數使用 NDK 支持的一組預設 ABI,或者您手動配置 Gradle 而讓其使用的一組經過篩選的 ABI。例如:

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

為了確保 CMake 可以在編譯時定位您的標頭文件,您需要使用 include_directories() 命令,並包含標頭文件的路徑:

include_directories( imported-lib/include/ )

:如果您希望打包一個並不是構建時依賴項的預構建庫(例如在添加屬於 imported-lib 依賴項的預構建庫時),則不需要執行以下說明來關聯庫。

要將預構建庫關聯到您自己的原生庫,請將其添加到 CMake 構建腳本的 target_link_libraries() 命令中:

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

在您構建應用時,Gradle 會自動將導入的庫打包到 APK 中。您可以使用 APK 分析器驗證 Gradle 將哪些庫打包到您的 APK 中。如需瞭解有關 CMake 命令的詳細信息,請參閱 CMake 文檔

要將 Gradle 關聯到您的原生庫,您需要提供一個指向 CMake 或 ndk-build 腳本文件的路徑。在您構建應用時,Gradle 會以依賴項的形式運行 CMake 或 ndk-build,並將共用的庫打包到您的 APK 中。Gradle 還使用構建腳本來瞭解要將哪些文件添加到您的 Android Studio 項目中,以便您可以從 Project 視窗訪問這些文件。如果您的原生源文件沒有構建腳本,則需要先創建 CMake 構建腳本,然後再繼續。

將 Gradle 關聯到原生項目後,Android Studio 會更新 Project 窗格以在 cpp 組中顯示您的源文件和原生庫,在 External Build Files 組中顯示您的外部構建腳本。

:更改 Gradle 配置時,請確保通過點擊工具欄中的 Sync Project  應用更改。此外,如果在將 CMake 或 ndk-build 腳本文件關聯到 Gradle 後再對其進行更改,您應當從菜單欄中選擇 Build > Refresh Linked C++ Projects,將 Android Studio 與您的更改同步。

您可以使用 Android Studio UI 將 Gradle 關聯到外部 CMake 或 ndk-build 項目:

  1. 從 IDE 左側打開 Project 窗格並選擇 Android 視圖。
  2. 右鍵點擊您想要關聯到原生庫的模塊(例如 app 模塊),並從菜單中選擇 Link C++ Project with Gradle。您應看到一個如圖 4 所示的對話框。
  3. 從下拉菜單中,選擇 CMake 或 ndk-build
    1. 如果您選擇 CMake,請使用 Project Path 旁的欄位為您的外部 CMake 項目指定 CMakeLists.txt 腳本文件。
    2. 如果您選擇 ndk-build,請使用 Project Path 旁的欄位為您的外部 ndk-build 項目指定 Android.mk 腳本文件。如果 Application.mk 文件與您的 Android.mk 文件位於相同目錄下,Android Studio 也會包含此文件。

    圖 4. 使用 Android Studio 對話框關聯外部 C++ 項目。

  4. 點擊 OK

手動配置 Gradle

要手動配置 Gradle 以關聯到您的原生庫,您需要將 externalNativeBuild {} 塊添加到模塊級 build.gradle 文件中,並使用 cmake {} 或 ndkBuild {} 對其進行配置:

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path "CMakeLists.txt"
    }
  }
}

:如果您想要將 Gradle 關聯到現有 ndk-build 項目,請使用 ndkBuild {} 塊而不是 cmake {},並提供 Android.mk 文件的相對路徑。如果 Application.mk 文件與您的 Android.mk 文件位於相同目錄下,Gradle 也會包含此文件。

指定可選配置

您可以在模塊級 build.gradle 文件的 defaultConfig {} 塊中配置另一個 externalNativeBuild {} 塊,為 CMake 或 ndk-build 指定可選參數和標誌。與 defaultConfig {} 塊中的其他屬性類似,您也可以在構建配置中為每個產品風味重寫這些屬性。

例如,如果您的 CMake 或 ndk-build 項目定義多個原生庫,您可以使用 targets 屬性僅為給定產品風味構建和打包這些庫中的一部分。以下代碼示例說明瞭您可以配置的部分屬性:

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use ndkBuild {}
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

        // Sets optional flags for the C compiler.
        cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"

        // Sets a flag to enable format macro constants for the C++ compiler.
        cppFlags "-D__STDC_FORMAT_MACROS"
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries to build and package for this
          // product flavor. If you don't configure this property, Gradle
          // builds and packages all shared object libraries that you define
          // in your CMake or ndk-build project.
          targets "native-lib-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid"
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

要詳細瞭解配置產品風味和構建變體,請參閱配置構建變體。如需瞭解您可以使用 arguments 屬性為 CMake 配置的變數列表,請參閱使用 CMake 變數

指定 ABI

預設情況下,Gradle 會針對 NDK 支持的 ABI 將您的原生庫構建到單獨的 .so 文件中,並將其全部打包到您的 APK 中。如果您希望 Gradle 僅構建和打包原生庫的特定 ABI 配置,您可以在模塊級 build.gradle 文件中使用 ndk.abiFilters 標誌指定這些配置,如下所示:

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your APK.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

在大多數情況下,您只需要在 ndk {} 塊中指定 abiFilters(如上所示),因為它會指示 Gradle 構建和打包原生庫的這些版本。不過,如果您希望控制 Gradle 應當構建的配置,並獨立於您希望其打包到 APK 中的配置,請在 defaultConfig.externalNativeBuild.cmake {} 塊(或 defaultConfig.externalNativeBuild.ndkBuild {} 塊中)配置另一個 abiFilters 標誌。Gradle 會構建這些 ABI 配置,不過僅會打包您在 defaultConfig.ndk{} 塊中指定的配置。

為了進一步降低 APK 的大小,請考慮配置 ABI APK 拆分,而不是創建一個包含原生庫所有版本的大型 APK,Gradle 會為您想要支持的每個 ABI 創建單獨的 APK,並且僅打包每個 ABI 需要的文件。如果您配置 ABI 拆分,但沒有像上面的代碼示例一樣指定 abiFilters 標誌,Gradle 會構建原生庫的所有受支持 ABI 版本,不過僅會打包您在 ABI 拆分配置中指定的版本。為了避免構建您不想要的原生庫版本,請為 abiFilters 標誌和 ABI 拆分配置提供相同的 ABI 列表。

以上編譯c文件的文章出自:https://developer.android.com/studio/projects/add-native-code.html?utm_source=android-studio#download-ndk

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文是學習時的自我總結,用於日後溫習。如有錯誤還望諒解,不吝賜教 此處附上部分內容所出博客:http://blog.csdn.net/ymh198816/article/details/51998085 Flume+Kafka+Storm+Redis實時分析系統基本架構 1) 整個實時分析系統的架構 ...
  • 本文是學習時的自我總結,用於日後溫習。如有錯誤還望諒解,不吝賜教 一、安裝方式(內嵌模式,本地模式遠程模式) 安裝環境以及前提說明: Hive是依賴於hadoop系統的,因此在運行Hive之前需要保證已經搭建好hadoop集群環境。 本例中使用的hadoop版本為2.6.1,Hive版本為2.1.1 ...
  • 方法一:添加代理 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { NSString *tem ...
  • 推送通知可以在應用沒有啟動或者在後臺運行的時候給用戶一些提示。因此,很多應用開發者和公司用它來推銷自己的產品。通過這個渠道推送自己的產品是不錯的選擇,但是一定要遵守起碼的道德規範(不要在用戶睡覺的時候推送你的通知),否則用戶就會毫不留情地屏蔽你這個應用的通知,甚至卸載你的應用。 一,蘋果究竟有多少個 ...
  • 1、Android系統目前支持的CPU架構:ARMv5、ARMv7、x86、MIPS、ARMv8、MIPS64、x86_64,每一種都關聯著一個ABI(Application Binary Interface) 2、ABI:定義了二進位文件(尤其是.so文件)如何運行在相應系統平臺上,包括使用的指令 ...
  • 問題引入: 有一個下載功能,在Android 5.x設備上運行正常,Android 6.x上運行異常,現象是下載進度卡在0%。 問題排查發現,是sdk的target設置為23導致,修改為21則恢復正常。 這就引出了今天討論的問題:Android中build target,minSdkVersion, ...
  • 一、簡介 什麼是cocoapods?——是OS X和iOS下一個第三方類庫管理工具。通過cocoapods,可以為項目添加“Pods“依賴庫,並且管理其版本。 cocoapods好處: (1)可以方便引入第三方依賴庫,自動完成各種配置:配置編譯階段、連接器選項等。 (2)可以方便查找新的、標準的第三 ...
  • 第二篇教程之前寫了一半,感覺不太好寫,而且內容單純介紹API,要說的很多,又枯燥乏味。所以那半篇文章就放下了。後來又開始思考這教程該怎麼寫,經過幾天的沉澱,終於有了一個決定:這個教程,就用IT戀里的實例來和大伙分享了,看看在實戰是怎麼被應用的。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...