这篇文章主要讲解Android开发时在C方法中调用JAVA方法的步骤
一、前期知识准备
由于在C方法中调用JAVA方法需要运用反射的知识来。这里先来回顾一下java中是如何使用反射来调用一个类的方法的。
下边直接附上我在JAVA中利用反射的代码,具体的步骤在代码中有注释:
写一个类供反射访问:
package simpletest;
public class MyClass {
private void say(String msg){
System.out.print("say::" + msg);
}
}
写一个利用反射调用该类中say方法的java类:
package simpletest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
//反射使用的步骤
//1、获取类的字节码
Class clazz = Class.forName("simpletest.MyClass");
//2、获取指定的方法
Method method = clazz.getDeclaredMethod("say", String.class);
method.setAccessible(true);
//3、调用该方法
method.invoke(clazz.newInstance(), "我是反射出来的");
}
}
打印出来的结果为:say::我是反射出来的
二、在C语言中使用的具体步骤
在了解了JAVA中使用反射的步骤后,在C代码中利用反射也是类似的步骤。
下边直接放代码(具体的在代码中有详细的注释)
JNIAlipay\jni\Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS += -llog
LOCAL_MODULE := JNIAlipay
LOCAL_SRC_FILES := JNIAlipay.c
include $(BUILD_SHARED_LIBRARY)
JNIAlipay\jni\Application.mk:
APP_ABI := all
JNIAlipay\jni\JNIAlipay.c:
#include <jni.h>
#include <stdlib.h>
#include <string.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__)
//LOGI("info\n"); //打印日志的方法
// LOGD("debug\n");
/*
* 将jstring类型 的字符串,转换为 char* 类型
*/
char* Jstring2CStr(JNIEnv* env, jstring jstr)
// jstr 是java语言中的 String,该方法的目的就是调用 jstr.getByte("GB2312");
{
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env,"java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"GB2312");
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1); //"\0"
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
(*env)->ReleaseByteArrayElements(env,barr,ba,0); //
return rtn;
}
/**
*一个c方法
* 调用MainActivity中的方法显示ProgressDialog
*/
void showProgress(JNIEnv * env, jobject obj, char * jmsg){
//1、获取类的字节码:查询jni.h返回jclss类型的方法:jclass (*FindClass)(JNIEnv*, const char*);
/**
* 参数1:jni开发环境的引用
* 参数2:使用类的全名,注意要将其中的.换成/
*/
jclass jclazz = (*env)->FindClass(env,"com/example/jnialipay/MainActivity");
//2、获取指定的方法 : jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
/**
* 参数1:jni开发环境的引用
* 参数2:类引用
* 参数3:要调用的JAVA方法名称
* 参数4:要调用的JAVA方法的方法签名
* (获取方法签名:使用 javap -s com.example.jnialipay.MainActivity 可以获得所有方法的签名
* 注意这个命令执行的路径需要在项目路径/bin/classes下打开命令窗口来执行。获取JAVA类中所有的方法签名后,
* 将我们要调用的方法签名copy过来就行了)
*/
jmethodID jmethod = (*env)->GetMethodID(env,jclazz,"showProgress","(Ljava/lang/String;)V");
//3、调用该方法:void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
/**
* 参数1:jni开发环境的引用
* 参数2:是调用该函数的java对象的引用
* 参数3:jmethodID对象
* 参数4/5/6/...:可以是多个。是调用JAVA方法要传入的参数。
* 在java方法中参数为String类型,我们要将char *转化为jstring传入,不能直接使用char *类型
*
*/
jstring msg = (*env)->NewStringUTF(env,jmsg);//将char * 类型转为jstring类型
(*env)->CallVoidMethod(env,obj,jmethod,msg);
}
/**
* 开始支付
* @param userid 用户名
* @param pwd 密码
* @param money 支付金额
* @return
* 200 支付成功
* 400 支付失败 - 用户名、密码不正确
* 500 支付失败 -- 金额受限制
*/
JNIEXPORT jint JNICALL Java_com_example_jnialipay_MainActivity_pay
(JNIEnv * env, jobject obj, jstring name, jstring pwd, jint money){
showProgress(env,obj,"本地数据打包");//调用上边的方法
LOGI("本地数据打包");//打印log日志
sleep(2);//休眠:单位为秒 模拟本地数据打包操作
showProgress(env,obj,"联接服务器验证数据");//调用上边的方法
LOGI("联接服务器验证数据");//打印log日志
sleep(2);//休眠:单位为秒 模拟联接服务器验证数据
showProgress(env,obj,"联接服务器验证数据");//调用上边的方法
LOGI("联接服务器验证数据");//打印log日志
sleep(2);//休眠:单位为秒 模拟联接服务器进行支付
// 获得用户名
char * pUserId = Jstring2CStr(env,name);
// 获得密码
char * pPwd = Jstring2CStr(env,pwd);
// 获得金额
int mun = money;
// 假设 用户名是abc 密码 123 金额最大,不能起过5000
int result_user = strcmp(pUserId,"abc");//c语言中字符串的比较
int result_pwd = strcmp(pPwd,"123");
if(result_user == 0 && result_pwd == 0){ // 表明用户名密码正确
if(money<5000){
// 支付成功
return 200;
}else{
// 金额受限
return 500;
}
}else{
//用户名,或密码不正确
return 400;
}
};
JNIAlipay\src\com\example\jnialipay\MainActivity.java:
package com.example.jnialipay;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
private EditText etName;
private EditText etPwd;
private EditText etMoney;
private ProgressDialog proDlg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
etName = (EditText) findViewById(R.id.et_name);
etPwd = (EditText) findViewById(R.id.et_pwd);
etMoney = (EditText) findViewById(R.id.et_money);
proDlg = new ProgressDialog(this);
proDlg.setTitle("提醒");
proDlg.setMessage("正在处理,请稍候..");
}
public void btnClick(View v){
final String etNameStr = etName.getText().toString().trim();
final String etPwdStr = etPwd.getText().toString().trim();
final String etMoneyStr = etMoney.getText().toString().trim();
if(TextUtils.isEmpty(etNameStr)){
Toast.makeText(this, "请输入用户名", 1).show();
return;
}
if(TextUtils.isEmpty(etPwdStr)){
Toast.makeText(this, "请输入密码", 1).show();
return;
}
if(TextUtils.isEmpty(etMoneyStr)){
Toast.makeText(this, "请输入要转账的金额", 1).show();
return;
}
new Thread(){
public void run() {
int result = pay(etNameStr,etPwdStr,Integer.parseInt(etMoneyStr));
handler.sendEmptyMessage(result);
};
}.start();
}
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
dissProgress();
switch (msg.what) {
case 200:
Toast.makeText(MainActivity.this, "支付成功", 1).show();
break;
case 400:
Toast.makeText(MainActivity.this, "支付失败 --用户名、密码不正确", 1).show();
break;
case 500:
Toast.makeText(MainActivity.this, "支付失败 --金额受限制", 1).show();
break;
default:
break;
}
};
};
/**
* 该方法 ,是让C代码中调用的,用于提示用户,当前的工作
* @param msg
*/
public void showProgress(final String msg){
// 该方法,会在子线程中调用,为了显示进度框,代码应应在主线程执行
runOnUiThread(new Runnable() {
@Override
public void run() {
if (proDlg != null && proDlg.isShowing()) {
proDlg.dismiss();
}
proDlg.setMessage(msg);
proDlg.show();
}
});
}
private Handler handler2 = new Handler(){
public void handleMessage(android.os.Message msg) {
};
};
/**
* 隐藏进度框
*/
public void dissProgress() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (proDlg != null && proDlg.isShowing()) {
proDlg.dismiss();
}
}
});
}
/**
* 支付的方法,这个方法写在c代码中
* @param userid 用户名
* @param pwd 密码
* @param money 支付金额
* @return
* 200 支付成功
* 400 支付失败 - 用户名、密码不正确
* 500 支付失败 -- 金额受限制
*/
private native int pay(String userid,String pwd ,int money);
static{
System.loadLibrary("JNIAlipay");
}
}
JNIAlipay\res\layout\activity_main.layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/et_name"
android:hint="请输入账号"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/et_pwd"
android:hint="请输入密码"
android:layout_margin="10dp"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/et_money"
android:hint="请输入金额"
android:layout_margin="10dp"
android:inputType="number"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btnClick"
android:text="点击开始转账" />
</LinearLayout>
如果想要直接下载项目的可以点击下边的链接免费下载源码:
点击打开链接