zoukankan      html  css  js  c++  java
  • Android Studio--NDK编译C代码为.so文件,JNI调用

    前言:

      从Android Studio开始,就支持jni和.so库调用了。

    环境:

      Windows 7+Android Studio2.1.2+NDK版本:android-ndk-r10e

    准备工作:

      1) ndk文件:

        1,可以根据需求,从网上下载, 网址(科学上网): https://developer.android.com/ndk/downloads/index.html

        2,可以查找下当前电脑是否已有ndk包,我的在目录:

          C:ProgramDataMicrosoftAndroidNDK64android-ndk-r10e

       

      2)配置环境变量:

        选中System的path变量,进行编辑,把  ;C:ProgramDataMicrosoftAndroidNDK64android-ndk-r10e添加到最后,保存:

           

           

           

    开始:

      Step1:新建一个项目

      Step2:添加C代码和Android.mk文件

       Demo中我们直接使用ndk包自带的C代码做示例,此处我使用hello-jni的代码:

      a) 将jni文件夹整体copy,粘贴到main文件夹下:

        

      b)在activity_main.xml中添加一个Button:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="me.jnidemo.myjnidemo.MainActivity"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/edv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!" />
    
        <Button
            android:id="@+id/clickId"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="clickBtn"/>
    </LinearLayout>

      d)新建一个class为NdkJniUtils,在内部声明native方法(jni使用的定义,使用关键字native)  

    package me.jnidemo.myjnidemo;
    
    /**
     * Created by c-yangx on 6/22/2016.
     */
    public class NdkJniUtils {
        static{
            System.loadLibrary("hello-jni");//.so文件格式为:lib+库名+.so
        }
        public static native String stringFromJNI();//函数名与C代码的函数名保持一致
    }

      c)修改hello-jni.c文件中的函数名称,格式为:Java_包名_类名_函数名( Java_me_jnidemo_myjnidemo_NdkJniUtils_stringFromJNI)

      d) jni文件夹 右键=>show in Explorer 进入目录;cmd进入此文件夹中,ndk-build命令进行编译:

        此编译会触发gradle,项目中会自动多出libs文件夹:

          

      e)此时在MainActivity.java中添加Button的点击事件:

    package me.jnidemo.myjnidemo;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity {
    
        TextView ev1;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            ev1=(TextView)findViewById(R.id.edv) ;
    
            Button btn=(Button)findViewById(R.id.clickId);
            btn.setOnClickListener(new Button.OnClickListener(){
                @Override
                public void onClick(View v) {
                    String res=NdkJniUtils.stringFromJNI();
                    ev1.setText(res);
                }
            });
        }
    }

      f) 在gradle.properties中添加配置: android.useDeprecatedNdk=true;

       在local.properties中添加 ndk.dir=C:\ProgramData\Microsoft\AndroidNDK64\android-ndk-r10e

       否则gradle会抛异常

      g)运行,触发click事件时,会抛异常:

    06-22 15:11:04.174 7196-7196/me.jnidemo.myjnidemo E/AndroidRuntime: FATAL EXCEPTION: main
                                                                        Process: me.jnidemo.myjnidemo, PID: 7196
                                                                        java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/me.jnidemo.myjnidemo-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]] couldn't find "libhello-jni.so"
                                                                            at java.lang.Runtime.loadLibrary(Runtime.java:378)
                                                                            at java.lang.System.loadLibrary(System.java:997)
                                                                            at me.jnidemo.myjnidemo.NdkJniUtils.<clinit>(NdkJniUtils.java:8)
                                                                            at me.jnidemo.myjnidemo.MainActivity$1.onClick(MainActivity.java:23)
                                                                            at android.view.View.performClick(View.java:4837)
                                                                            at android.view.View$PerformClick.run(View.java:20020)
                                                                            at android.os.Handler.handleCallback(Handler.java:739)
                                                                            at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                            at android.os.Looper.loop(Looper.java:135)
                                                                            at android.app.ActivityThread.main(ActivityThread.java:5669)
                                                                            at java.lang.reflect.Method.invoke(Native Method)
                                                                            at java.lang.reflect.Method.invoke(Method.java:372)
                                                                            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
                                                                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

      因此我们还需进行配置----在build.gradle中做一个资源路径指定即可:

    // build.gradle
    android {
    //
       //...
        sourceSets {
            main {
                //你的源码目录
                jniLibs.srcDirs  'src/main/libs'
            }
        }
    }    

      重新编译运行,ok了:

    补录:

      a)指定生成基于哪些CPU架构的.so文件:

        1) Application.mk文件配置方式:

          本文配置项,生成的为全架构平台的.so文件,缺点是会使得apk包变大:

        可以修改,指定多个平台。如: APP_ABI := armeabi armeabi-v7a x86 mips

       b)build.gradle配置方式:在app module目录下的build.gradle中设置库文件名(生成的so文件名),找到gradle文件的defaultConfig这项,在里面添加如下内容:

    defaultConfig {
        ......
        ndk{
            //moduleName "YanboberJniLibName"            //生成的so名字
            abiFilters "armeabi", "armeabi-v7a", "x86"    //输出指定三种abi体系结构下的so库。目前可有可无。
        }
    }

      b) Android.mk配置含义:

    LOCAL_PATH := $(call my-dir)     #提定当前路径
            
    include $(CLEAR_VARS)            #清除全局配置变量,LOCAL_XXX,除了LOCAL_PATH
            
    LOCAL_MODULE    := hello        #指定生成动态库名hello,生成的动态库文件libhello.so
    LOCAL_SRC_FILES := hello.c        #指定生成动态库的源文件
            
    include $(BUILD_SHARED_LIBRARY) #提定生成动态库

    后续:

      a) Bug 1 =>  app:compileDebugNdk FAILED

        

        最近在研究WebRtc的APM(Audio_Processing_Module)音频处理模块,将所有的C代码文件导入项目,通过ndk-build命令在cmd窗口编译时,一切ok。

      但是项目编译时却报此错误,最后(科学上网)找到了解决方案(网址:http://stackoverflow.com/questions/27453085/execution-failed-for-task-appcompiledebugndk-failed-to-run-this-command-ndk),

        

        大体意思就是说gradle尝试自己编译你的源文件,而不是使用你编译好的.so文件。既然在cmd中编译通过了,那么咱们可以通过配置,禁用gradle的ndk调用行为

      ,而使用你配置的路径下的.so文件。于是,我在app build.gradle中添加了 =>  jni.srcDirs = [],然后编译运行,一切ok了。

           

        

  • 相关阅读:
    yum源的制作
    债券到期收益率计算公式
    IMP同库Type对象导入报错ORA-02304
    oracle自动挂掉问题分析
    CPP读取dbf文件
    oracle忘记system密码
    沪C转浙A
    业务词汇
    VS2017使用Resharp开发CPP程序
    CPP调用webservice
  • 原文地址:https://www.cnblogs.com/Andrew-XinFei/p/5608001.html
Copyright © 2011-2022 走看看