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、運行