[NDK/JNI系列04] JNI接口方法表、基础API与异常API

简介: [NDK/JNI系列04] JNI接口方法表、基础API与异常API
  • 学习了如何在C++中调用Java的一些类和方法,例如System.currentTimeMillis(), Integer.parseInt(), String.substring()和ArrayList。
  • 学习了如何使用JNI函数来查找类、获取方法ID、调用方法、创建对象、传递参数和返回值。
  • 学习了如何使用JNI函数来检查、清除、打印和抛出Java异常。
  • 学习了如何使用命名空间、常量和辅助函数来组织和简化的C++代码。
  • 学习了如何在Kotlin中定义和调用原生方法,并使用System.loadLibrary()来加载本地库。

4.1 JNI接口方法表

  • JNI接口方法表是一个结构体指针,它包含了JNI提供的所有函数的指针。JNI接口方法表的类型是JNIEnv,它是一个二级指针,指向一个JNIEnv_结构体,该结构体只有一个成员,即指向JNI函数表的指针。
  • JNI接口方法表是线程相关的,每个线程都有自己的JNIEnv。在原生函数中,第一个参数就是JNIEnv,可以通过它调用JNI函数。例如:
JNIEXPORT void JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv *env, jobject obj) {
    // 调用JNI函数
    jclass cls = (*env)->FindClass(env, "java/lang/String");
    // ...
}
  • 在C++中,JNIEnv被定义为一个类,它包含了一个指向JNI函数表的指针,并且为每个JNI函数提供了一个成员函数,可以直接调用。例如:
JNIEXPORT void JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv *env, jobject obj) {
    // 调用JNI函数
    jclass cls = env->FindClass("java/lang/String");
    // ...
}
  • JNIEnv的类型是一个指向JNI接口方法表的指针,它的定义如下:
typedef const struct JNINativeInterface *JNIEnv;
struct JNINativeInterface {
    void *reserved0;
    void *reserved1;
    void *reserved2;
 
    void *reserved3;
    jint (JNICALL *GetVersion)(JNIEnv *env);
 
    jclass (JNICALL *DefineClass)
      (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
       jsize len);
    jclass (JNICALL *FindClass)
      (JNIEnv *env, const char *name);
 
    /* ... more functions ... */
};
  • JNI接口方法表中的函数可以分为以下几类:
  • 版本信息:GetVersion
  • 类操作:DefineClass, FindClass, GetSuperclass, IsAssignableFrom等
  • 异常处理:Throw, ThrowNew, ExceptionOccurred, ExceptionDescribe等
  • 全局引用操作:NewGlobalRef, DeleteGlobalRef等
  • 局部引用操作:NewLocalRef, DeleteLocalRef等
  • 弱全局引用操作:NewWeakGlobalRef, DeleteWeakGlobalRef等
  • 监视器操作:MonitorEnter, MonitorExit等
  • Java对象操作:AllocObject, NewObject等
  • 字段访问:GetFieldID, GetObjectField等
  • 方法调用:GetMethodID, CallObjectMethod等
  • 数组操作:NewArray, GetArrayLength等
  • 字符串操作:NewStringUTF, GetStringUTFChars等
  • 直接缓冲区操作:NewDirectByteBuffer, GetDirectBufferAddress等

4.2 JNI基础API的使用

  • JNI基础API是一些常用的JNI函数,它们可以实现以下功能:
  • 查找Java类、字段和方法的ID
  • 调用Java实例方法和静态方法
  • 获取和设置Java实例字段和静态字段的值
  • 创建Java对象和数组
  • 操作Java字符串和原始类型数组
  • 抛出和处理Java异常
  • JNI基础API的函数名都遵循一定的命名规则,例如:
  • FindClass: 查找一个Java类。
  • GetMethodID / GetStaticMethodID: 获取Java方法的ID。
  • CallMethod / CallStaticMethod: 调用Java方法。
  • GetFieldID / GetStaticFieldID: 获取Java字段的ID。
  • GetField / SetField: 获取或设置Java字段的值。
  • 其中,表示返回值或参数的类型,可以是以下之一:
  • Boolean: 布尔型
  • Byte: 字节型
  • Char: 字符型
  • Short: 短整型
  • Int: 整型
  • Long: 长整型
  • Float: 浮点型
  • Double: 双精度浮点型
  • Object: 对象型
  • Void: 空类型
  • 下面是一些JNI基础API的使用示例:
// 查找java.lang.String类
jclass stringClass = (*env)->FindClass(env, "java/lang/String");
 
// 获取java.lang.String类的构造方法ID,参数为字节数组和编码名称
jmethodID stringConstructor = (*env)->GetMethodID(env, stringClass, "<init>", "([BLjava/lang/String;)V");
 
// 获取java.lang.String类的length方法ID,无参数,返回值为整型
jmethodID stringLength = (*env)->GetMethodID(env, stringClass, "length", "()I");
 
// 创建一个字节数组对象,长度为10
jbyteArray byteArray = (*env)->NewByteArray(env, 10);
 
// 填充字节数组对象的内容为"Hello JNI"
jbyte buf[10] = {'H', 'e', 'l', 'l', 'o', ' ', 'J', 'N', 'I', '\0'};
(*env)->SetByteArrayRegion(env, byteArray, 0, 10, buf);
 
// 创建一个字符串对象,表示"UTF-8"编码
jstring encoding = (*env)->NewStringUTF(env, "UTF-8");
 
// 调用java.lang.String类的构造方法,创建一个字符串对象,内容为"Hello JNI"
jstring str = (*env)->NewObject(env, stringClass, stringConstructor, byteArray, encoding);
 
// 调用java.lang.String类的length方法,获取字符串的长度
jint len = (*env)->CallIntMethod(env, str, stringLength);
 
// 打印字符串的长度
printf("The length of the string is %d\n", len);

4.3 JNI异常API的使用

  • JNI异常API是一些用于处理Java异常的JNI函数,它们可以实现以下功能:
  • 检查是否发生了Java异常
  • 清除Java异常
  • 获取Java异常对象
  • 抛出Java异常
  • 抛出新的Java异常
  • JNI异常API的函数名都以Exception开头,例如:
  • ExceptionOccurred: 检查是否发生了Java异常。
  • ExceptionClear: 清除Java异常。
  • ExceptionDescribe: 打印Java异常的堆栈跟踪信息。
  • ExceptionGetCause: 获取Java异常的原因对象。
  • Throw: 抛出Java异常对象。
  • ThrowNew: 抛出新的Java异常。
  • 下面是一些JNI异常API的使用示例:
// 查找java.lang.Integer类
jclass integerClass = (*env)->FindClass(env, "java/lang/Integer");
 
// 获取java.lang.Integer类的parseInt方法ID,参数为字符串,返回值为整型
jmethodID parseInt = (*env)->GetStaticMethodID(env, integerClass, "parseInt", "(Ljava/lang/String;)I");
 
// 创建一个字符串对象,内容为"123"
jstring str = (*env)->NewStringUTF(env, "123");
 
// 调用java.lang.Integer类的parseInt方法,将字符串转换为整数
jint num = (*env)->CallStaticIntMethod(env, integerClass, parseInt, str);
 
// 检查是否发生了Java异常
jthrowable exception = (*env)->ExceptionOccurred(env);
if (exception != NULL) {
    // 清除Java异常
    (*env)->ExceptionClear(env);
 
    // 获取Java异常的原因对象
    jthrowable cause = (*env)->ExceptionGetCause(env, exception);
 
    // 打印Java异常的堆栈跟踪信息
    (*env)->ExceptionDescribe(env, exception);
 
    // 抛出新的Java异常,类型为java.lang.RuntimeException,信息为"JNI error"
    jclass runtimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
    (*env)->ThrowNew(env, runtimeExceptionClass, "JNI error");
}
else {
    // 打印转换后的整数
    printf("The number is %d\n", num);
}

4.3 完整例子

Chapter04.java

class Chapter04 : AppCompatActivity() {
    // 定义一些原生方法,用于调用测试函数
    external fun testSystemCurrentTimeMillis(): Long // 原生方法:测试System.currentTimeMillis()函数的调用
    external fun testIntegerParseInt(str: String): Int // 原生方法:测试Integer.parseInt()函数的调用
    external fun testStringSubstring(str: String, beginIndex: Int, endIndex: Int): String // 原生方法:测试String.substring()函数的调用
    external fun testArrayList(): Any // 原生方法:测试ArrayList的调用
    var TAG = "Chapter04"; // 定义TAG变量用于日志输出
 
    private lateinit var binding: Chapter04Binding
 
    // 在onCreate方法中调用原生方法,并打印结果
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        this.setTitle("Chapter04")
        binding = Chapter04Binding.inflate(layoutInflater)
        setContentView(binding.root)
 
        // Example of a call to a native method
        binding.sampleText.text = stringFromJNI() // 调用原生方法,返回字符串,并显示在UI中
 
        // 调用测试函数并打印结果
        val time = testSystemCurrentTimeMillis() // 调用原生方法获取当前时间戳
        Log.d(TAG, "当前时间为:$time")
 
        try {
            val num = testIntegerParseInt("123A") // 调用原生方法尝试将字符串解析为整数(故意触发错误)
            // val num = testIntegerParseInt("123") // 调用原生方法将字符串解析为整数(正确示例)
            Log.d(TAG, "解析得到的数字为:$num")
        } catch (e: Exception) {
            e.printStackTrace()
        }
 
        val result = testStringSubstring("Hello JNI", 0, 5) // 调用原生方法截取字符串的子串
        Log.d(TAG, "截取得到的子串为:$result")
 
        val firstElement = testArrayList() // 调用原生方法获取ArrayList的第一个元素
        Log.d(TAG, "ArrayList的第一个元素为:$firstElement")
    }
 
    /**
     * 一个由本应用程序打包的 'jnidemo' native库实现的原生方法。
     */
    external fun stringFromJNI(): String // 原生方法:获取一个字符串
 
    companion object {
        // 用于在应用程序启动时加载 'jnidemo' 库。
        init {
            System.loadLibrary("jnidemo")
        }
    }
}

chapter04.cpp

#include <jni.h>
#include <string>
 
using namespace chapter04; // 使用chapter04命名空间
 
// 定义一些常量,表示类名和方法签名
const char* SYSTEM_CLASS = "java/lang/System";
const char* INTEGER_CLASS = "java/lang/Integer";
const char* STRING_CLASS = "java/lang/String";
const char* ARRAYLIST_CLASS = "java/util/ArrayList";
 
const char* LONG_RETURN_VOID_ARG = "()J";
const char* INT_RETURN_STRING_ARG = "(Ljava/lang/String;)I";
const char* STRING_RETURN_INT_INT_ARG = "(II)Ljava/lang/String;";
const char* VOID_RETURN_VOID_ARG = "()V";
const char* BOOLEAN_RETURN_VOID_ARG = "()Z";
const char* BOOLEAN_RETURN_OBJECT_ARG = "(Ljava/lang/Object;)Z"; // 定义这个常量
const char* VOID_RETURN_OBJECT_ARG = "(Ljava/lang/Object;)V";
const char* OBJECT_RETURN_INT_ARG = "(I)Ljava/lang/Object;";
 
// 定义一个辅助函数,用于检查并打印JNI异常
void checkAndPrintException(JNIEnv *env) {
    jthrowable exception = env->ExceptionOccurred(); // 检查是否发生了Java异常
    if (exception != NULL) {
        env->ExceptionDescribe(); // 打印Java异常的堆栈跟踪信息
    }
}
 
// 定义一个测试函数,用于调用Java的System.currentTimeMillis()方法
jlong testSystemCurrentTimeMillis(JNIEnv *env) {
    // 查找System类
    jclass systemClass = env->FindClass(SYSTEM_CLASS);
    checkAndPrintException(env);
 
    // 获取currentTimeMillis方法的ID
    jmethodID currentTimeMillisMethod = env->GetStaticMethodID(systemClass, "currentTimeMillis", LONG_RETURN_VOID_ARG);
    checkAndPrintException(env);
 
    // 调用currentTimeMillis方法,获取当前时间的毫秒数
    jlong time = env->CallStaticLongMethod(systemClass, currentTimeMillisMethod);
    checkAndPrintException(env);
 
    // 返回时间值
    return time;
}
 
// 定义一个测试函数,用于调用Java的Integer.parseInt()方法
jint testIntegerParseInt(JNIEnv *env, jstring str) {
    // 查找Integer类
    jclass integerClass = env->FindClass(INTEGER_CLASS);
    checkAndPrintException(env);
 
    // 获取parseInt方法的ID
    jmethodID parseIntMethod = env->GetStaticMethodID(integerClass, "parseInt", INT_RETURN_STRING_ARG);
    checkAndPrintException(env);
 
    // 调用parseInt方法,将字符串转换为整数
    jint num = env->CallStaticIntMethod(integerClass, parseIntMethod, str);
    checkAndPrintException(env);
 
    // 检查是否发生了Java异常
    jthrowable exception = env->ExceptionOccurred();
    if (exception != NULL) {
        // 清除Java异常
        env->ExceptionClear();
 
        // 获取Java异常的原因对象(如果API版本低于24,注释掉这一行)
        // jthrowable cause = env->ExceptionGetCause(exception);
 
        // 打印Java异常的堆栈跟踪信息
        env->ExceptionDescribe(); // 修改函数调用,不传入任何参数
        checkAndPrintException(env);
 
        // 抛出新的Java异常,类型为java.lang.RuntimeException,信息为"JNI error"
        jclass runtimeExceptionClass = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(runtimeExceptionClass, "JNI error 》 testIntegerParseInt");
    }
 
    // 返回转换后的整数
    return num;
}
 
 
// 定义一个测试函数,用于调用Java的String.substring()方法
jstring testStringSubstring(JNIEnv *env, jstring str, jint beginIndex, jint endIndex) {
    // 查找String类
    jclass stringClass = env->FindClass(STRING_CLASS);
    checkAndPrintException(env);
 
    // 获取substring方法的ID
    jmethodID substringMethod = env->GetMethodID(stringClass, "substring", STRING_RETURN_INT_INT_ARG);
    checkAndPrintException(env);
 
    // 调用substring方法,截取字符串的一部分
    jstring result = (jstring) env->CallObjectMethod(str, substringMethod, beginIndex, endIndex);
    checkAndPrintException(env);
 
    // 检查是否发生了Java异常
    jthrowable exception = env->ExceptionOccurred();
    if (exception != NULL) {
        // 清除Java异常
        env->ExceptionClear();
 
        // 返回空字符串
        result = env->NewStringUTF("");
    }
 
    // 返回截取后的字符串
    return result;
}
 
// 定义一个测试函数,用于创建一个Java的ArrayList对象,并向其中添加一些元素,然后获取其中的第一个元素
jobject testArrayList(JNIEnv *env) {
    // 查找ArrayList类
    jclass arrayListClass = env->FindClass(ARRAYLIST_CLASS);
    checkAndPrintException(env);
 
    // 获取ArrayList类的构造方法ID,无参数,返回值为void
    jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", VOID_RETURN_VOID_ARG);
    checkAndPrintException(env);
 
    // 获取ArrayList类的add方法ID,参数为对象,返回值为布尔型
    jmethodID arrayListAdd = env->GetMethodID(arrayListClass, "add", BOOLEAN_RETURN_OBJECT_ARG);
    checkAndPrintException(env);
 
    // 获取ArrayList类的get方法ID,参数为整型,返回值为对象
    jmethodID arrayListGet = env->GetMethodID(arrayListClass, "get", OBJECT_RETURN_INT_ARG);
    checkAndPrintException(env);
 
    // 创建一个ArrayList对象
    jobject arrayList = env->NewObject(arrayListClass, arrayListConstructor);
    checkAndPrintException(env);
 
    // 创建一些字符串对象,并向ArrayList中添加
    jstring str1 = env->NewStringUTF("Hello");
    jstring str2 = env->NewStringUTF("JNI");
    jstring str3 = env->NewStringUTF("World");
    env->CallBooleanMethod(arrayList, arrayListAdd, str1);
    env->CallBooleanMethod(arrayList, arrayListAdd, str2);
    env->CallBooleanMethod(arrayList, arrayListAdd, str3);
    checkAndPrintException(env);
 
    // 获取ArrayList中的第一个元素
    jobject firstElement = env->CallObjectMethod(arrayList, arrayListGet, 0);
    checkAndPrintException(env);
 
    // 返回第一个元素
    return firstElement;
}
 
extern "C" JNIEXPORT jstring JNICALL
Java_com_ln28_jnidemo_Chapter04_stringFromJNI( // 修改函数名前缀,与Chapter04.kt中对应
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
 
extern "C" JNIEXPORT jlong JNICALL
Java_com_ln28_jnidemo_Chapter04_testSystemCurrentTimeMillis( // 修改函数名前缀,与Chapter04.kt中对应
        JNIEnv* env,
        jobject /* this */) {
    return testSystemCurrentTimeMillis(env);
}
 
extern "C" JNIEXPORT jint JNICALL
Java_com_ln28_jnidemo_Chapter04_testIntegerParseInt( // 修改函数名前缀,与Chapter04.kt中对应
        JNIEnv* env,
        jobject /* this */,
        jstring str) {
    return testIntegerParseInt(env, str);
}
 
extern "C" JNIEXPORT jstring JNICALL
Java_com_ln28_jnidemo_Chapter04_testStringSubstring( // 修改函数名前缀,与Chapter04.kt中对应
        JNIEnv* env,
        jobject /* this */,
        jstring str,
        jint beginIndex,
        jint endIndex) {
    return testStringSubstring(env, str, beginIndex, endIndex);
}
 
extern "C" JNIEXPORT jobject JNICALL
Java_com_ln28_jnidemo_Chapter04_testArrayList( // 修改函数名前缀,与Chapter04.kt中对应
        JNIEnv* env,
        jobject /* this */) {
    return testArrayList(env);
}

异常打印:

java.lang.NumberFormatException: For input string: "123A"
java.lang.RuntimeException: JNI error 》 testIntegerParseInt
异常被捕获,程序没有崩溃。

4.4 疑问和问题解答

JNI接口方法表:

  1. JNI接口方法表是什么?它的作用是什么?
    JNI接口方法表是一个结构体,其中包含了所有可用的JNI函数的指针。可以通过在C/C++代码中使用JNIEnv指针来调用这些函数。这个结构体为提供了一个与Java交互的接口,如访问和修改Java对象的字段,调用Java方法,处理Java异常等。
  2. 如何使用JNI接口方法表?
    在C/C++代码中,可以通过JNIEnv指针来访问JNI接口方法表中的函数。例如,可以使用(*env)->GetObjectField(env, obj, fieldID)来访问一个Java对象的字段。
  3. JNI接口方法表中的函数有哪些?我需要了解所有的函数吗?
    JNI接口方法表中包含了大约100多个函数,这些函数提供了丰富的功能,如创建Java对象,访问和修改Java对象的字段,调用Java方法,抛出和处理Java异常等。不需要了解所有的函数,只需要掌握常用的一些函数就可以了。
  4. 有没有一个示例展示如何使用JNI接口方法表的某个函数?
    当然,例如,下面的代码展示了如何使用JNI接口方法表的GetObjectField函数和SetIntField函数来访问和修改Java对象的字段:
jclass cls = (*env)->GetObjectClass(env, obj);
jfieldID fieldID = (*env)->GetFieldID(env, cls, "intValue", "I");
jint value = (*env)->GetIntField(env, obj, fieldID);
value++;
(*env)->SetIntField(env, obj, fieldID, value);

       5.我是否可以修改JNI接口方法表?

不,不能修改JNI接口方法表。这个结构体是由JNI环境提供的,只能通过JNIEnv指针来访问它,不能修改它。

JNI基础API:

  1. JNI基础API包含哪些函数?
    JNI基础API包含了许多函数,如创建Java对象,访问和修改Java对象的字段,调用Java方法,抛出和处理Java异常等。一些常用的函数包括NewObjectGetObjectClassGetFieldIDGetObjectFieldSetObjectFieldCallVoidMethod等。
  2. JNI基础API的主要作用是什么?
    JNI基础API的主要作用是提供一个接口,让可以在C/C++代码中与Java交互。可以通过JNI基础API来创建Java对象,访问和修改Java对象的字段,调用Java方法,抛出和处理Java异常等。
  3. 如何使用JNI基础API访问和修改Java对象的字段?
    可以使用GetFieldID函数来获取一个字段的ID,然后使用Get<Type>FieldSet<Type>Field函数来访问和修改该字段。例如:

jclass cls = (*env)->GetObjectClass(env, obj);

jfieldID fieldID = (*env)->GetFieldID(env, cls, "intValue", "I");

jint value = (*env)->GetIntField(env, obj, fieldID);

value++;

(*env)->SetIntField(env, obj, fieldID, value);

       4.如何使用JNI基础API调用Java方法

可以使用GetMethodID或者GetStaticMethodID函数来获取一个方法的ID,然后使用Call<Type>Method或者CallStatic<Type>Method函数来调用该方法。例如:

jclass cls = (*env)->GetObjectClass(env, obj);

jmethodID mid = (*env)->GetMethodID(env, cls, "print", "(I)V");

(*env)->CallVoidMethod(env, obj, mid, 123);

       5.如何创建和处理Java对象?

可以使用NewObject函数来创建一个新的Java对象,然后使用各种Get<Type>FieldSet<Type>Field函数来访问和修改其字段。如果需要处理Java对象的数组,可以使用NewObjectArrayGetObjectArrayElementSetObjectArrayElement等函数。

JNI异常API:

  1. JNI异常API是用来做什么的?
    JNI异常API用于在native代码中抛出和处理Java异常。当在C/C++代码中调用Java方法时,可能会发生Java异常,可以使用JNI异常API来检查和处理这些异常。2
  2. 如果在native方法中发生了Java异常,我应该如何处理?可以使用ExceptionOccurred函数来检查是否发生了异常,然后使用ExceptionDescribe函数来打印异常的堆栈轨迹,使用ExceptionClear函数来清除异常。如果想在native代码中处理异常,可以使用ThrowNew函数来抛出一个新的异常。
jmethodID mid = (*env)->GetMethodID(env, cls, "method", "()V");
if ((*env)->ExceptionOccurred(env)) {
    (*env)->ExceptionDescribe(env);
    (*env)->ExceptionClear(env);
    return;
}

       3.我应该如何使用JNI异常API抛出一个新的Java异常

可以使用ThrowNew函数来抛出一个新的Java异常。需要指定异常类和异常信息。

jclass exCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if (exCls != NULL) {
    (*env)->ThrowNew(env, exCls, "Illegal argument");
}

       4.JNI异常API中的ExceptionCheckExceptionOccurred有什么区别?

ExceptionCheckExceptionOccurred函数都可以用来检查是否发生了Java异常,但是ExceptionCheck函数不会清除异常状态,而ExceptionOccurred函数会返回一个异常对象并清除异常状态。

       5。如果我忽略了一个Java异常(没有清除或者抛出),会发生什么?

如果在native方法中忽略了一个Java异常,这个方法将会立即返回,不会执行后面的代码。如果这个native方法是由Java调用的,Java将会收到这个异常。所以,如果不打算处理Java异常,应该至少使用ExceptionDescribe函数来打印异常的堆栈轨迹,然后使用ExceptionClear函数来清除异常,避免影响后续代码的执行。

(1) JNI 提示 | Android NDK | Android Developers - Android 开发者. JNI 提示  |  Android NDK  |  Android Developers.

(2) NDK系列:JNI基础 - 掘金. NDK系列:JNI基础 - 掘金.

相关文章
|
1天前
|
JSON 安全 API
如何高效编写API接口:以Python与Flask为例
构建RESTful API的简明教程:使用Python的Flask框架,从环境准备(安装Python,设置虚拟环境,安装Flask)到编写首个API(包括获取用户列表和单个用户信息的路由)。运行API服务器并测试在`http://127.0.0.1:5000/users`。进阶话题包括安全、数据库集成、API文档生成和性能优化。【6月更文挑战第27天】
17 7
|
1天前
|
JSON 安全 API
实战指南:使用PHP构建高性能API接口服务端
构建RESTful API的简要指南:使用PHP和Laravel,先安装Laravel并配置数据库,接着在`api.php`中定义资源路由,创建`PostController`处理CRUD操作,定义`Post`模型与数据库交互。使用Postman测试API功能,如创建文章。别忘了关注安全性、错误处理和性能优化。
12 2
|
3天前
|
人工智能 运维 Serverless
函数计算产品使用问题之启动的实例是否有调用api接口停止功能
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2天前
|
敏捷开发 测试技术 API
阿里云云效产品使用问题之API中包含有获取测试计划的接口吗
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
3天前
|
XML JSON 程序员
程序员必知:常用天气预报API接口整理(转)
程序员必知:常用天气预报API接口整理(转)
|
3天前
|
XML JSON 程序员
程序员必知:常用天气预报API接口整理(转)
程序员必知:常用天气预报API接口整理(转)
|
3天前
|
安全 API 数据安全/隐私保护
​6个值得测试的发送邮件接口API
在数字营销和企业通信中,发送邮件接口API如SendGrid、Mailgun、Amazon SES、AOKSend、Postmark和Sendinblue是关键工具。这些API自动化邮件发送,提高效率,确保及时送达。各具特色,如SendGrid和Mailgun适合大规模发送,AOKSend易于集成,Postmark专于事务邮件,而Sendinblue提供综合营销解决方案。根据企业需求,如邮件量、成本、功能和集成简易度,可选择最适合的API服务。
|
3天前
|
API 开发工具
企业微信SDK接口API调用-通过手机号或微信好友添加客户
企业微信SDK接口API调用-通过手机号或微信好友添加客户
|
3天前
|
API 开发工具
企业微信SDK接口API调用-触发推送企业微信微信好友
企业微信SDK接口API调用-触发推送企业微信微信好友
|
4天前
|
自然语言处理 搜索推荐 安全
电商 API 接口:连接数字商业的关键纽带
**电商API接口是商业世界的隐形支柱,连接电商平台、应用和服务,确保高效运行和创新。它们促进用户体验提升(如实时库存、安全支付),拓展业务功能(如个性化推荐、智能客服),加速数据流通分析,并增强系统灵活性。随着技术进步,AI、大数据和物联网将使API接口更加智能、安全,成为电商在全球化竞争中的关键优势。**