Wednesday, June 19, 2013

Android NDK / JNI (Java Native Interface)


NDK - Native Development Kit NDK je sada nástrojov, ktorá umožňuje implementovať natívny kód v iných jazykoch (C, C + +).  NDK využíva štandardný java framework k tomu určený – JNI.
JNI – Java Native Interface je framework umožňujúci volať natívne programy a knižnice napísané v iných jazykoch (C, C++, ..) priamo v kóde, bežiacom v Java Virtual Machine (JVM).
Uplatnenie:
  • využitie staršieho kódu napísaného v inom jazyku (nie Java) bez nutnosti ho prepisovať do javy
  • využitie API iných jazykov (nie Java)
  • komunikácia s HW

Obmedzenia (nevýhody):
  • strata platformovej nezávislosti javy (natívny kód je potrebne prepísať pre každú platformu)
  • vlastnosti javy ako typová bezpečnosť, garbage collector sa nevzťahujú na natívny kód

Implementácia:
  1.  triede v ktorej chceme volať metódu, implementovanú v natívnom jazyku, vytvoríme deklaráciu natívnej metódy. Natívna metóda musí obsahovať modifikátor „native“,

    public native int callJni(int i);
    


  2. Signatúru natívnej metódy môžeme vytvoriť pomocou nástroja „javah“ (javah -jni ClassName), alebo ju vytvoríme ručne, teda vytvoríme samostatný súbor pre požadovanú metódu (napr.: „jni_part.cpp“) v adresári „jni“ (napr.: project_folder/jni/jni_part.cpp),


    #include <jni.h>
    extern "C" {
        JNIEXPORT jint JNICALL Java_com_example_test04jni_MainActivity_callJni(JNIEnv*, jclass, jint);
        JNIEXPORT jint JNICALL Java_com_example_test04jni_MainActivity_callJni(JNIEnv* je, jclass jc, jint i)
        {
            return (i * i);
        }
    }
    


  3. V mieste umiestnenia natívnej funkcie vytvoríme „makefile“ s názvom „Android.mk“ pre android build,

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := jni_example  
    LOCAL_SRC_FILES := jni_part.cpp
    include $(BUILD_SHARED_LIBRARY)
    


  4. Zbuildujeme natívnu metódu pomocou „android-ndk“,

    LINUX shell:
    "project dir"$ ~/.android-ndk-r8c/ndk-build
    

  5. Nakoniec v android aplikácii v mieste volania natívnej metódy, najskôr načítame knižnicu, ktorá bude kompilovať natívnu metódu a zavoláme samotnú metódu.

    // Load native library
    System.loadLibrary("jni_example");
    ...
    // call native method: "callJni(i)"
    textView_result.setText(Integer.toString(callJni(i)));
    


    Zdroj android triedy, v ktorej bola volaná natívna metóda:


    package com.example.test04jni;
     
    import android.os.Bundle;
    import android.app.Activity;
    import android.view.Menu;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    public class MainActivity extends Activity {
     
        private TextView textView_result;
         
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
             
            // Load native library
            System.loadLibrary("jni_example");
             
            textView_result = (TextView) findViewById(R.id.textView_result);
             
            final Button btn_start = (Button) findViewById(R.id.button1);
            btn_start.setOnClickListener(new View.OnClickListener() {
                 public void onClick(View v) {
                    Integer i = 10;                   
                    // call native method: "callJni(i)"                       
                    textView_result.setText(Integer.toString(callJni(i)));           }            
            });
             
        }
     
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
     
        // Declaration of native methods.
        public native int callJni(int i);
    }
    

Zdroj príkladu: Android-NDK-JNI-example