JNI(Java Native Interface)_03

JNI(Java Native Interface)_03

c/c++區別

jni調用:

*   c: jstring str = (*env)->NewStringUTF(env,getHello());
*  c++:jstring str = (env)->NewStringUTF(getHello());
*  區別如下:
*  1,c++中把(*env) 的\*號去掉
*  2,調用函數的時候吧參數env去掉
*  3,c中是結構體  c++是類
*  4,c結構體中的函數指針  c++類的成員函數(c結構體不能聲明函數)
*  5,c 申請內存用malloc /realloc ,c++ 申請空間用new
*  6,c 釋放內存用free()  ,c++ 釋放空間用 delete
*  7, c++ 結構體和class基本一致(除了訪問權限,結構體的訪問權限沒有類多)

1種開發模式

* 1, java程序員定義好native
* 2, java程序員生成jni規範
* 3, 找一個c程序員寫好代碼實現
* 4, 通過jni調用(.so)

2種開發模式

* 1,c代碼已經寫好
* 2,根據c代碼定義native方法
* 3,jni規範
* 4,調用(.so)

3種開發模式

* 1. .so文件已經生成
* 2, 拿到jni規範
* 3, 直接調用

jni實際開發

案例一:10_鍋爐監控1

鍋爐監控的由來:

北方天氣很冷,所以需要鍋爐加熱來取暖,但是鍋爐加熱如果不監控的話,會產生溫度過高而導致爆炸,而人爲的監控比較麻煩,所以我們就可以給鍋爐加上一個電子監控器(C語言控制的),而當監控器監控到溫度過高,就可以給通過與Java語言通信而給用戶發送信息告知,如果C與java不通信的話,單靠C語言實現給設備發送信息,這實現難度難以估計。這就是這個案例的由來

案例目的:

1、演示在jni文件夾下的c程序如何調用別的c程序的方法
2、爲完成鍋爐監控的全功能實現做鋪墊

案例步驟:

1,創建工程

右鍵工程-->android tools-->add native support->輸入動態庫名字->點擊確定

2,把cpp改爲c,同時在Android.mk文件中也把cpp改成c

3,定義java中native方法

/**
 * 該方法獲取鍋爐發來的監控數據,這裏爲了模擬就採用C語言調用僞隨機數發來的數據
 * @return
 */
public native int getPress();

4,實現c代碼

這一步和之前的不一樣了,這裏需要在jni自動生成的c文件裏調用另外一個c文件裏的方法,這樣可以降低耦合性。可以另程序員工作量大大減低

guolu.c
#include <stdio.h>
#include<stdlib.h>

/*

*/
int getNumber()
{
    return rand() % 100;//鍋爐壓力錶 
}

main()
{
      printf("%d\n",getNumber());
      system("pause");
}

5,在c源文件中實現jni規範(native方法) javah命令參考上面

    右鍵工程--》properties->c/c++ general->paths and symbols 選擇includes 點擊add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存

6,在c源文件中實現jni方法

guolu1.c

#include <jni.h>

jint Java_com_itheima_guolu1test_MainActivity_getPress
  (JNIEnv * env, jobject obj)
{
    return getNumber();//可以直接調用了
}

7,java中加載.so文件

package com.itheima.guolu1test;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
    static{
        System.loadLibrary("guolu1");
    }
    /**
     * 該方法獲取鍋爐發來的監控數據,這裏爲了模擬就採用C語言調用僞隨機數發來的數據
     * @return
     */
    public native int getPress();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void click(View view){
        Toast.makeText(this, getPress()+"", 0).show();

    }

    @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;
    }

}

8,運行


案例二、11_鍋爐監控2

案例目的:

1、加強案例一的功能:用seekbar控件來展示

2、演示C程序和Java程序間的方法互調

案例步驟

1、創建工程 右鍵工程-->android tools-->add native support->輸入動態庫名字->點擊確定

2、把cpp改爲c,同時在Android.mk文件中也把cpp改成c

3、定義java中native方法

public native void startMonitor();
public native void stopMonitor();

4、實現c代碼

guolu.c

#include <stdio.h>
#include<stdlib.h>

/*

*/
int getNumber()
{
    return rand() % 100;//鍋爐壓力錶 
}

main()
{
      printf("%d\n",getNumber());
      system("pause");
}

guolu1.c

#include <jni.h>


jint Java_com_itheima_guolu1test_MainActivity_getPress
  (JNIEnv * env, jobject obj)
{
    return getNumber();//可以直接調用了
}


int state;
void  Java_com_itheima_guolu1test_MainActivity_startMonitor
  (JNIEnv *env, jobject obj)
{
    state = 1;
    while(state){
        int press = getNumber();
        //1,class
        //jclass      (*FindClass)(JNIEnv*, const char*);
        jclass clazz = (*env)->FindClass(env,"com/itheima/guolu1test/MainActivity");
        //2,method
        // jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
        jmethodID mid = (*env)->GetMethodID(env,clazz,"setMyProgress","(I)V");
        //3,obj
        //4,invoke
        //    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
        (*env)->CallVoidMethod(env,obj,mid,press);
        sleep(1);
    }
}


void  Java_com_itheima_guolu1test_MainActivity_stopMonitor
  (JNIEnv *env, jobject obj)
{
    state = 0;

}

5、在c源文件中實現jni規範(native方法) javah命令參考上面

右鍵工程--》properties->c/c++ general->paths and symbols 選擇includes 點擊add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存

6、在c源文件中實現jni方法

7、java中加載.so文件

<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:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="開始監控"
        android:onClick="startclick" />
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="停止監控"
        android:onClick="endclick" />
    <SeekBar 
        android:id="@+id/seek"
                android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
</LinearLayout>


package com.itheima.guolu1test;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.SeekBar;
import android.widget.Toast;

public class MainActivity extends Activity {
    private SeekBar seek;
    static{
        System.loadLibrary("guolu1");
    }
    public native void startMonitor();
    public native void stopMonitor();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        seek = (SeekBar) findViewById(R.id.seek);
        seek.setMax(100);
    }
    public void setMyProgress(int progress){
        seek.setProgress(progress);
    }


    public void startclick(View view){
        new Thread(){
            public void run(){
                startMonitor();
            };
        }.start();
    }
    public void endclick(View view){
        new Thread(){
            public void run(){
                stopMonitor();
            };
        }.start();
    }

    @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;
    }

}

8、運行


案例三、12_自定義ViewTest

案例目的

1、可以不使用Android提供的控件去顯示監控的數據,而使用自定義的控件

2、爲後面的綜合鍋爐監控案例做鋪墊

案例步驟

1、新建一個MyView繼承View

2、實現View的所有構造函數

3、重寫onDraw方法

代碼如下:

package com.itheima.zidinyiView;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class MyView extends View {
    private int currentPress = 60;
    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint  = new Paint();
        paint.setStrokeWidth(2);
        paint.setTextSize(20);
        if(currentPress<50){
            paint.setColor(Color.GREEN);
        }else if(currentPress<70){
            paint.setColor(Color.YELLOW);
        }else if(currentPress<90){
            paint.setColor(Color.BLUE);
        }else {
            paint.setColor(Color.RED);
        }
        //float left, float top, float right, float bottom, Paint paint)
        canvas.drawRect(50, 300 - currentPress, 100, 300, paint);
        //畫東西到左面上
        canvas.drawText("當前壓力值:", 130, 270, paint);
    }

    public int getCurrentPress() {
        return currentPress;
    }

    public void setCurrentPress(int currentPress) {
        this.currentPress = currentPress;
    }

}   

4、在layout裏使用該自定義的控件(繼承了View的自定義類)去佈局

<RelativeLayout 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=".MainActivity" >

    <com.itheima.zidinyiView.MyView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</RelativeLayout>

5、在手機上測試佈局是否滿意


案例四、13_鍋爐監控3

案例目的:

1、把之前的鍋爐監控案例綜合起來,加入自定義的顯示控件,和自己的功能

案例步驟:工程內容大致和案例二一樣

1、把案例三實現的MyView複製過來

2、改寫工程的layout佈局

<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:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="開始監控"
        android:onClick="startclick" />
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="停止監控"
        android:onClick="endclick" />
    <com.itheima.guolu1test.MyView 
                android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/myView"
        />
</LinearLayout>

3、然後在MainActivity裏實現該控件

package com.itheima.guolu1test;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.SeekBar;
import android.widget.Toast;

public class MainActivity extends Activity {
//  private SeekBar seek;
    private MyView view;
    static{
        System.loadLibrary("guolu1");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//        seek = (SeekBar) findViewById(R.id.seek);
//        seek.setMax(100);
        view = (MyView) findViewById(R.id.myView);
    }
    public void setMyProgress(int progress){
//      seek.setProgress(progress);
        if(progress>90){
            //發送短信
            //播放警告音樂
        }
        view.setCurrentPress(progress);
    }

    public native void startMonitor();
    public native void stopMonitor();

    public void startclick(View view){
        new Thread(){
            public void run(){
                startMonitor();
            };
        }.start();
    }
    public void endclick(View view){
        new Thread(){
            public void run(){
                stopMonitor();
            };
        }.start();
    }

    @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;
    }

}

案例結果:


案例四、14_卸載監聽

案例目的:

1、演示android的app如果在裏面有交叉調用了C程序的方法,而在C程序方法中如果使用到了分叉函數fork();,而導致的app被卸載後C程序產生的進程還繼續在手機裏運行不會停止。也就是所謂的流氓軟件所使用到的手段

案例步驟:

1、創建工程 右鍵工程-->android tools-->add native support->輸入動態庫名字->點擊確定

2、把cpp改爲c,同時在Android.mk文件中也把cpp改成c

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_LDLIBS += -llog
LOCAL_MODULE    := removeListener
LOCAL_SRC_FILES := removeListener.c

include $(BUILD_SHARED_LIBRARY)

3、定義java中native方法

public native void shabusi();//殺不死的方法

4、實現c代碼

#include <jni.h>
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)


void  Java_com_itheima_removelistenerTest_MainActivity_shabusi
(JNIEnv * env, jobject obj)
{
    //分叉函數
    //當java軟件被卸載後還會繼續發出這些信息,因爲在c程序裏的這個分叉函數java程序關不掉,所以程序員需要職業道德
    //使用c程序去監聽文件包什麼時候被刪去了,如果刪去了就關掉這個分叉函數產生的線程
    int res = fork();
    if(res == 0){
        while(1){//死循環
            LOGI("我還活着\n");
            sleep(1);
            LOGI("我還活着1");
        }
    }
}

5、在c源文件中實現jni規範(native方法) javah命令參考上面

右鍵工程--》properties->c/c++ general->paths and symbols 選擇includes 點擊add 把I:\android-ndk-r9\platforms\android-9\arch-arm\usr\include 保存

6、在c源文件中實現jni方法

7、java中加載.so文件

package com.itheima.removelistenerTest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {
    static{
        System.loadLibrary("removeListener");
    }
    public native void shabusi();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        shabusi();
    }


    @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;
    }

}

8、運行

App卸載前


App卸載


App卸載後


案例五、15_cpphello

案例目的:

1、Java和C++如何交叉編程
2、在android中輸出hello

案例步驟:

1、創建工程 右鍵工程-->android tools-->add native support->輸入動態庫名字->點擊確定

2、定義java中native方法

public native String helloFromCpp();

3、在cpp源文件中實現jni規範(native方法),使用javah命令,而生成的java頭文件也要導入到jni文件夾中,並在cpp文件裏聲明該頭文件


4、實現cpp方法

#include <jni.h>
#include<com_itheima_cpphelloTest_MainActivity.h>



////public native String helloFromC() Java_包名_類名_方法名(JNIEnv* env,jobject obj)
//// doGet(reqeust,response)
//jstring Java_com_itheima_helloworld_MainActivity_helloFromC(JNIEnv* env,jobject obj)
//{
//  //jstring     (*NewStringUTF)(JNIEnv*, const char*);
//  jstring str = (*env)->NewStringUTF(env,getHello());
//  return str;
//}
//以上是c的jni調用過程
//和以上區別的是,c++的調用過程如下,是有區別的
 jstring  Java_com_itheima_cpphelloTest_MainActivity_helloFromCpp
  (JNIEnv *env, jobject obj){
     return (env)->NewStringUTF("hello from cpp");
 }

5、在MainActivity中調用方法

package com.itheima.cpphelloTest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.Toast;

public class MainActivity extends Activity {
    static{
        System.loadLibrary("cpp");
    }
    public native String helloFromCpp();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toast.makeText(this, helloFromCpp(), 1).show();
    }


    @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;
    }

}

6、運行



資料下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章