美文网首页
Android NDK模拟native crash

Android NDK模拟native crash

作者: 12313凯皇 | 来源:发表于2020-07-23 14:43 被阅读0次

    最近需要模拟出一个native crash,简单来说就是声明一个native方法,然后在c/c++层实现这个方法并触发一个异常即可。由于之前没有接触过这些,所以实现起来还是花费了挺多的时间的,这期间也涉及到了很多知识点或概念,如NDKJNIabi以及so等。

    JNI的全称为Java Native Interface,即Java本地接口,类似于AIDL,提供了若干的API实现了Java和其他语言的通信(主要是CC++),目的是使Java方法能够调用C实现的一些函数

    NDK全称为Native Development Kit,它是一个工具集,NDK允许用户使用类似C/C++之类的原生原生代码执行部分程序,其内部还是采用JNI机制实现的。

    在对上述概念有了一个基础了解后,才好理解后面要做的事情都是在干什么,下面就以楼主现在需要实现的这个小功能为例阐述一个如何通过NDK实现一个native方法:

    一、配置安装NDK

    楼主的运行环境是Ubuntu,开发工具选用的Android Studio。所以NDK的安装比较简单:

    1. 打开Android StudioFile->Settings->Appearance & Behavior->System Settings->Android SDK,然后选中安装SDK Tools中的NDK即可。

    2. 安装好NDK后还需要给项目设置NDK版本,点击File->Project Structure->SDK Location,然后设置NDK的目录,我这里下来了多个NDK版本,所以我选择了其中一个版本。

    3. 安装好NDK后可以先尝试在Android StudioTerminal终端中输入ndk-build命令看是否有反应。如果报错未找到命令的话就还需要配置一下环境变量,命令如下(详细内容参考Android JNI和NDK学习(01)--搭建NDK开发环境):
      NDK路径就用上面自己配置的那个路径就可以了。

      # 替换成自己的ndk路径
      export NDK_HOME=/home/hy/Android/Sdk/ndk/21.3.6528147
      export PATH=$PATH:$NDK_HOME
      source ~/.bashrc
      

      配置完成后在终端输入ndk-build命令应该就会有输出了,如果Studio中的Terminal还是未找到命令可以重启或者在系统终端中cd到项目目录再输入命令。

    二、编写so库准备工作

    NDK环境配置好了之后就可以开始编写代码了。

    1. 创建TestJNI.Java声明一个native方法
      public class TestJNI {
      
          static {
              System.loadLibrary("TestJNI");
          }
      
          public native void crashTest();
      }
      
    2. 使用javac生成.class文件
      javac TestJNI.java
      
    3. 使用javah生成.h文件,注意此时命令所处的位置应该是项目的java目录下
      以我的项目为例此时命令应到是在~/AndroidStudioProjects/NativeCrashTest/app/src/main/java目录下执行的,否则会报找不到类文件的错误
      javah  -jni com.example.nativecrashtest.jni.TestJNI
      
    4. 编写C/C++实现native方法:
      #include "jni.h"
      #include "com_example_nativecrashtest_jni_TestJNI.h"
      #include <cstdio>
      
      JNIEXPORT void JNICALL 
      Java_com_example_nativecrashtest_jni_TestJNI_crashTest(JNIEnv *, jobject) {
          printf("make a crash");
          //TODO 制造一个native carash
          int *p = 0; //空指针
          *p = 1; //写空指针指向的内存,产生SIGSEGV信号,造成crash
      }
      
    5. 创建Android.mk文件,添加如下代码:
      记得将LOCAL_MODULELOCAL_SRC_FILES替换成你自己的文件名。
      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE        := TestJNI         
      LOCAL_SRC_FILES     := TestJni.cpp      
      include $(BUILD_SHARED_LIBRARY)
      
    6. 配置app下的build.gradle文件
      android {
          ...
      
          defaultConfig {
              ...
      
              ndk {
                  moduleName "TestJNI"
                  abiFilters "armeabi-v7a"
              }
          }
      
          externalNativeBuild {
              ndkBuild {
                  path "src/main/java/com/example/nativecrashtest/jni/Android.mk"
              }
          }
      
          sourceSets {
              main() {
                  jniLibs.srcDirs = ['libs']
              }
          }
      
          ...
      }
      

    同样这里需要根据自己的情况修改相应的文件名或者路径,其中abiFilters相关知识可以参考Android ABI,如果仅是测试的话可以通过adb shell cat /proc/cpuinfo命令查看自己的测试机类型(参考如何查看Android设备的ABI)。

    最后的项目目录结构图如下:


    项目结构图

    三、生成.so文件并调用实现方法

    1. 相关准备工作完成之后,进入到Android.mk所在目录执行ndk-build命令即可:
    hy@hy-OptiPlex-7070:~/AndroidStudioProjects/NativeCrashTest/app/src/main/java/com/example/nativecrashtest/jni$ ndk-build
    Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.    
    [arm64-v8a] Compile++      : TestJNI <= TestJni.cpp
    [arm64-v8a] SharedLibrary  : libTestJNI.so
    [arm64-v8a] Install        : libTestJNI.so => libs/arm64-v8a/libTestJNI.so
    [armeabi-v7a] Compile++ thumb: TestJNI <= TestJni.cpp
    [armeabi-v7a] SharedLibrary  : libTestJNI.so
    [armeabi-v7a] Install        : libTestJNI.so => libs/armeabi-v7a/libTestJNI.so
    [x86] Compile++      : TestJNI <= TestJni.cpp
    [x86] SharedLibrary  : libTestJNI.so
    [x86] Install        : libTestJNI.so => libs/x86/libTestJNI.so
    [x86_64] Compile++      : TestJNI <= TestJni.cpp
    [x86_64] SharedLibrary  : libTestJNI.so
    [x86_64] Install        : libTestJNI.so => libs/x86_64/libTestJNI.so
    

    命令执行完成后可以看到项目目录下多出来一个libs文件夹,里面有生成的.so文件。
    ps:如果Studio的终端命令行还是提示未找到命令就通过系统终端跳转到.mk文件所在目录然后执行命令即可。

    1. 添加一个button按钮点击事件,在点击按钮时触发crash
    //MainActivity.java
    public class MainActivity extends WearableActivity {
    
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mTextView = (TextView) findViewById(R.id.text);
    
            findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    com.example.nativecrashtest.jni.TestJNI testJNI = new TestJNI();
                    testJNI.crashTest();
                }
            });
    
            // Enables Always-on
            setAmbientEnabled();
        }
    }
    
    1. 启动demo点击按钮触发崩溃,然后执行adb shell ls -l /data/system/dropbox命令,可以看到多出来了一个data_app_native_crash@1595486083697.txt.gz文件,取出解压就可以看到完整的crash调用栈信息了。

    参考文章

    相关文章

      网友评论

          本文标题:Android NDK模拟native crash

          本文链接:https://www.haomeiwen.com/subject/ypvclktx.html