1, C++ basic knowledge

1.1 the function

  • A function is a group of statements that perform a task together. Every C program has at least one function, the main functionmain()All simple programs can define additional functions.
  • .hHeader files.
  • Pointer function: a function with a pointer, that is, it is essentially a function. A function return type is a pointer to a type.int *func(int x, int y).
  • Function pointer: a pointer variable to a function, which is essentially a pointer variable.int (*funcp)(int x).
int i; int *a = &i; // where a is a pointer to the variable I int &b = I; Int * &c = a; int * &c = a; // where c is a reference, it is a reference to a pointer int & *d; // Where d is a pointer, it points to a reference, but the reference is not an entity, so this is incorrectCopy the code

When analyzing the above code, you can start with the variable identifier and look from right to left. The closest thing to the identifier is the essential type of the variable, and further left is the modification of the variable type.

For example, int * & a is a reference to a variable with an & immediately to the left of the identifier a, and * to the left is a reference to a pointer.

  • .->
struct Data { int a,b,c; }; /* struct Data * p; /* struct Data A = {1,2,3}; / * int x; / * int x; /* declare A variable x */ p = &a; A */ x = p-> A; P ->a == A.a (1 */)Copy the code

Since p is a pointer here, it cannot be used. Number to access internal members (i.e., not p.a), but to use ->. But A.a is ok because A is not A pointer, it is the name of the structure.

In general use “.” You only need to declare one structure. The format is: structure type name + structure name. Then add “. “to the name of the structure. Add the member name to reference the member. Because the memory of the structure is automatically allocated. As int a; The same. Use “- >”, to declare a structure pointer, to manually create a the structure of memory (the above code is built a structure as an example, automatically assigns the memory, this example would be when it comes to manually dynamically allocated), then the return address assigned to a structure pointer declarations, to use correct reference “- >”. Otherwise, only pointer memory is allocated, and no structure memory is allocated, so the desired structure does not actually exist. “->” = “->”; In addition, (*p).a is equivalent to P ->a.

  • : :

:: is the scope character, which is the highest rank of operators. It comes in three types:

  1. Global scope character, usage::name
  2. Class scope, usageclass::name
  3. Namespace scope character, usagenamespace::name

They are both left related, and their function is to call the desired variable more explicitly:

  1. Say at some point in the program you want to call a global variableaSo let’s just write::a; (Can also be a global function)
  2. If you want to callclass AMember variables inaSo let’s just writeA::a;
  3. The other one if you want to callnamespace stdIn thecoutMember, you just writestd::cout(the equivalent ofUsing namespace STD. cout“(” I want to use it herecoutObjects are namespacesstdIn the cout(that is, the standard library edgecout);
  1. Represents “domain operator” : declares a classAThe classAA member function is declared invoid f()But is not given in the class declarationfSo when you define f outside the class, you have to writevoid A::f()That means thisf()Function is a classAMember functions of.
  2. Used directly before a global function to indicate that it is a global function: in VC, you can call an API function by adding the name of the API function before the name of the API function: :
  3. Represents reference member functions and variables, scoped member operators:System::Math::Sqrt()The equivalent ofSystem.Math.Sqrt();

1.2 Linux Memory Layout

1.3 Pointer Array

  • Array:Int arr [] = {1, 2, 3};
  • Pointer:int* p = arr, the pointer p to the first address of array arr;*p = 6;Assign the first element of the arR array to 6;*(p+1) = 10;Assign the second element of the arr array to 10;
  • Pointer array: Each element in an array is a pointer
Int * p [3]; for(int i = 0; i<3; i++){ p[i] = &arr[i]; }Copy the code
  • Array pointer: also known as row pointer;int (*p)[n]

It’s a pointer to a one-dimensional array of integers, and the length of that one-dimensional array is n, or the step size of p. When p+1 is executed, p crosses n lengths of integer data.

int a[3][4]; int (*p)[4]; // This statement defines an array pointer to a 4-element one-dimensional array p = a; // Assign the first address of the two-dimensional array to p, that is, a[0] or &a[0][0] p++; // after the statement is executed, that is, p = p+1; P crosses line a[0][] to line A [1][]

1.4 structure

struct Person
{
	char c;
	int i;
    char ch;
};

int main()
{
	struct Person person;
	person.c = 8;
	person.i = 9;
}
Copy the code

When storing variables, the address must be aligned. The compiler follows two principles when compiling programs:

(1) The offset of the member in the struct variable must be an integer multiple of the size of the member. (2) The struct size must be an integer multiple of the size of all the members, that is, a common multiple of the size of all the members

1.5 the appropriate

  • Commons is a special data type that allows you to store different data types in the same memory location.
  • You can define a community with multiple members, but only one member can have a value at any one time.
  • Shared bodies provide an efficient way to use the same memory location.
  • The shared body should have enough memory to store the largest member of the shared body.
union Data
{
	int i;
	float f;
	char str[20];
}data;

int main()
{
	union Data data;
	data.i = 11;
}
Copy the code

1.6 typedef

  • Define an alias for a type, not just a simple macro replacement. Can be used to declare multiple objects of pointer type simultaneously:
char *pa, *pb; // Traditional writingCopy the code
typedef char* PCHAR; // Use typedefs to write PCHAR pa, pb; // Yes, declare two Pointers to a character variableCopy the code
  • Used in old C code to helpstruct. In the previous code, declarestructWhen you’re new, you have to bring itstruct, namely, the form is:Struct Structure name Specifies the name of the object:
struct tagPOINT1
{
int x;
int y;
};
struct tagPOINT1 p1;
Copy the code
Struct {int x; int y; }POINT; POINT p1; // In this way, it is easier to write one less struct than the original method, especially when using a large number of structsCopy the code
  • withtypedefTo define platform-independent types:
#if __ANDROID__
typedef double SUM;
#else
typedef float SUM ;
#endif

int test() {
    SUM a;
    return 0;
}
Copy the code
  • Define a new simple alias for a complex declaration:
Int *(*a[5])(int, char*); Typedef int *(*pFun)(int, char*); // The simplest version of the original declaration: pFun a[5];Copy the code

1.7 Class construction and parsing, friend functions

1.7.1 header (.h) and source (.cpp) files in C++
  • .hClass declarations (including class member and method declarations), function prototypes, #define constants, etc., but generally do not write the specific implementation. To prevent repeated compilation when writing header files, we must add precompiled statements at the beginning and end as follows:
#ifndef CIRCLE_H #define CIRCLE_H class Circle { private: double r; public: Circle(); // Constructor Circle(double R); // Double Area(); }; #endifCopy the code

As for the name CIRCLE_H, it really doesn’t matter, you can call it whatever you want, as long as it conforms to the specification. In principle, it is highly recommended to write it in this form because it is easier to match the name of the header file.

  • .cppThe source file mainly writes the specific code that implements the functions already declared in the header file. Note that the beginning must be#includeThe following is the implementation header file, and the header file to use.
#include "Circle.h"

Circle::Circle()
{
    this->r=5.0;
}
Circle::Circle(double R)
{
    this->r=R;
}
double Circle:: Area()
{
    return 3.14*r*r;
}
Copy the code
  • And finally, let’s build onemain.cppTo test the Circle class we wrote
#include <iostream>
#include "Circle.h"
using namespace std;

int main()
{
    Circle c(3);
    cout<<"Area="<<c.Area()<<endl;
    return 1;
}
Copy the code
1.7.2 Constructors and destructors
  • A class constructor is a special member function of a class that is executed each time a new object of the class is created. The name of the constructor is exactly the same as the name of the class, and it does not return any type, nor does it return void. Constructors can be used to set initial values for certain member variables.
  • A class destructor is a special member function of the class that is executed each time an object is deleted. The name of the destructor is exactly the same as the name of the class, except that it is prefixed with a tilde (~), returns no value, and cannot take any arguments. Destructors help to free resources before exiting the program (such as closing files, freeing memory, and so on).
1.7.3 Friend functions and friend classes
  • A friend function is an ordinary function defined outside a class. It does not belong to any class, but it needs to be specified in the body of the class. In order to distinguish it from member functions of the class, it is specified before the keywordfriend.
  • A friend function is not a member function, but it can access private members of the class.
  • A function can be a friend function of multiple classes, and only needs to be declared separately in each class.
  • Friends make a program more efficient (that is, they reduce the amount of time required for type checking, security checking, etc.), but they break the class’s encapsulation and hiding, allowing non-member functions to access private members of the class.
  • All member functions of a friend class are friend functions of another class and have access to hidden information (both private and protected members) in the other class.
  • When you want a class to have access to the private members of another class, you can declare that class as a friend of the other class. The format of a statement to define a friend metaclass is as follows:Friend the class name of the class(Friend and class are keywords; the class name must be a defined class in the program).
class INTEGER { private: int num; public: friend void Print(const INTEGER& obj); // Declare a friend function; Void Print(const INTEGER& obj) // Do not use friend and class: : {// function body} void main() {INTEGER obj; Print(obj); // Call directly}Copy the code
#include <iostream> using namespace std; class girl { private: char *name; int age; friend class boy; Public: girl(char *n,int age):name(n),age(age){}; }; class boy { private: char *name; int age; public: boy(char *n,int age):name(n),age(age){}; void disp(girl &); }; Void boy::disp(girl &x) // This function must be defined after the girl class definition, otherwise the girl class private variable is unknown {cout<<"boy's name is:"<<name<<",age:"<<age<<endl; cout<<"girl's name is:"<<x.name<<",age:"<<x.age<<endl; } void main() {boy b("aaa",8);} void main() {boy b("aaa",8); girl g("bbb",99); b.disp(g); }Copy the code

1.8 Overloaded singleton objects and Operators

  • We can redefine or override most of C++ ‘s built-in operators. This allows you to use operators of custom types. An overloaded operator is a function with a special name. The function name is a keywordoperatorAnd the operator symbol to be overridden. Like other functions, overloaded operators have a return type and a list of arguments.
#include <iostream> using namespace std; class Box { public: double getVolume(void) { return length * breadth * height; } void setLength( double len ) { length = len; } void setBreadth( double bre ) { breadth = bre; } void setHeight( double hei ) { height = hei; } // Overload the + operator to add two Box objects. Box operator+(const Box &b) {Box Box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box; } private: double length; // Length double lines; // double height; / / height}; Int main() {Box Box1; // Declare Box1, type Box Box Box2; // Declare Box2, type Box Box Box3; // Declare Box3, type: Box double volume = 0.0; // store the volume in this variable // Box1 details box1.setLength (6.0); Box1. SetBreadth (7.0); Box1. SetHeight (5.0); // Box2 details box2.setLength (12.0); Box2. SetBreadth (13.0); Box2. SetHeight (10.0); // getVolume = box1.getVolume (); cout << "Volume of Box1 : " << volume <<endl; // getVolume = box2.getVolume (); cout << "Volume of Box2 : " << volume <<endl; Box3 = Box1 + Box2; Box3 = Box1 + Box2; // getVolume = box3.getVolume (); cout << "Volume of Box3 : " << volume <<endl; return 0; }Copy the code

Print result:

Volume of Box1 : 210 Volume of Box2 : 1560 Volume of Box3 : 5400

1.9 Inheriting polymorphic and virtual functions

1.9.1 inheritance
  • A class can derive from multiple classes, which means that it can inherit data and functions from multiple base classes. To define a derived class, we use a list of derived classes to specify the base class. The class derived list is named after one or more base classes:class derived-class: access-specifier base-class
  • Where, the access modifieraccess-specifierpublic,protectedprivateOne of them,base-classIs the name of a class defined earlier. If the access modifier is not usedaccess-specifier, the default isprivate.
  • A derived class can access all non-private members of the base class. Therefore, base class members that do not want to be accessed by derived class member functions should be declared asprivate.
  • A derived class inherits all base class methods, except in the following cases:

The constructor, destructor, and copy constructor of the base class.

Overloaded operator for the base class. Friend functions of the base class.

  • When a class derives from a base class, that base class can be inherited as public, protected, or private. Inheritance types are specified using the access-specifier described above.

  • We rarely use protected or private inheritance, and usually use public inheritance. When using different types of inheritance, follow these rules:

Public: When a class is derived from a public base class, the public members of the base class are also public members of the derived class, and the protected members of the base class are also protected members of the derived class. The private members of the base class cannot be accessed directly by the derived class, but can be accessed by calling the public and protected members of the base class.

Protected: When a class derives from a protected base class, the public and protected members of the base class become protected members of the derived class. Private: When a class derives from a base class, the public and protected members of the base class become private members of the derived class.

#include <iostream> using namespace std; Public: void setWidth(int w) {width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; // derived class Rectangle: public Shape {public: int getArea() {return (width * height); }}; int main(void) { Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); Cout << "Total area: "<< rect.getarea () << endl; return 0; }Copy the code

Print result:

Total area: 35

1.9.2 virtual function

Defining a function to be virtual does not mean that the function is not implemented.

It is defined as a virtual function to allow it to be called from a pointer to the base class. Defining a function as purely virtual means that the function is not implemented. A pure virtual function is defined to implement an interface that serves as a specification. Programmers who inherit from this class must implement the function.

class A { public: virtual void foo() { cout<<"A::foo() is called"<<endl; }}; class B:public A { public: void foo() { cout<<"B::foo() is called"<<endl; }}; int main(void) { A *a = new B(); a->foo(); // In this case, a is a pointer to a, but the called function (foo) is B! return 0; }Copy the code
  • The call to a class function is not determined at compile time, but at run time. They are called “virtual” functions because there is no way to determine whether a function of the base class or a function of the derived class is being called.
  • A pure virtual function is a virtual function declared in a base class that is not defined in the base class but requires any derived class to define its own implementation method. We implement a pure virtual function in the base class by adding “=0” to the function prototype:virtual void funtion()=0
  • When a function is defined as purely virtual, the compiler requires that it be overridden in a derived class to achieve polymorphism. A class that declares a pure virtual function is an abstract class. Therefore, a user cannot create an instance of a class, only an instance of its derived class.

1.10 Class templates and function templates

  • Templates are the foundation of generic programming, which is writing code in a way that is independent of any particular type.
  • A template is a blueprint or formula for creating a generic class or function.

The general form of a template function definition is as follows:

Template <typename type> ret-type func-name(parameter list) {Copy the code
#include <iostream>
#include <string>
 
using namespace std;
 
template <typename T>
inline T const& Max (T const& a, T const& b) 
{ 
    return a < b ? b:a; 
} 
int main ()
{
 
    int i = 39;
    int j = 20;
    cout << "Max(i, j): " << Max(i, j) << endl; 
 
    double f1 = 13.5; 
    double f2 = 20.7; 
    cout << "Max(f1, f2): " << Max(f1, f2) << endl; 
 
    string s1 = "Hello"; 
    string s2 = "World"; 
    cout << "Max(s1, s2): " << Max(s1, s2) << endl; 
 
   return 0;
}
Copy the code

Print result:

Max(i, j): 39 Max(f1, f2): 20.7 Max(s1, s2): World

The general form of a generic class declaration is as follows:

template <class type> class class-name {
.
.
.
}
Copy the code
#include <iostream> #include <vector> #include <cstdlib> #include <string> #include <stdexcept> using namespace std; template <class T> class Stack { private: vector<T> elems; Public: void push(T const&); Void pop(); // const T top(); Bool empty() const{// Return true if empty. return elems.empty(); }}; Template <class T> void Stack<T>::push (T const& elem) {// add a copy of the element elems.push_back(elem); } template <class T> void Stack<T>::pop () { if (elems.empty()) { throw out_of_range("Stack<>::pop(): empty stack"); } // Delete the last element elems.pop_back(); } template <class T> T Stack<T>::top () const { if (elems.empty()) { throw out_of_range("Stack<>::top(): empty stack"); } // Return a copy of the last element return elems.back(); } int main() { try { Stack<int> intStack; Stack<string> stringStack; IntStack. Push (7); cout << intStack.top() <<endl; String stack.push ("hello"); cout << stringStack.top() << std::endl; stringStack.pop(); stringStack.pop(); } catch (exception const& ex) { cerr << "Exception: " << ex.what() <<endl; return -1; }}Copy the code

Print result:

7 hello Exception: Stack<>::pop(): empty stack

1.11 container

  • Sequence containers, which are sequentially clustered, where each element has a fixed place-depending on the time and place of insertion, and independent of the element value. If you append six elements to a cluster, they will be sorted in the same order as they were inserted. The STL provides three sequential containers: vectors, deques, and lists. You can also think of strings and arrays as sequential containers.
  • Associative containers, which are ordered clusters where element positions depend on specific sorting criteria and element values, regardless of insertion order. If you put six elements into a group like this, their positions depend on the value of the element, regardless of the order in which they were inserted. The STL provides four associative containers: set, multiset, Map, and multimap.
  • Container connector: based on the above seven basic container categories. In stack, elements are managed in LIFO (last in, first out), in queue, and FIFO (first in, first out). That is, it is a normal buffer, priority_queue, and elements can have different priorities. Priorities are defined based on the sorting criteria provided by the programmer (operators are the default). A Priority queue acts like a buffer that says, “The next element in the queue is always the highest Priority.” If more than one element has the highest priority at the same time, the order is not clearly defined.

Features:

  1. vectorThe insertion and deletion efficiency in the header and middle is low, while the insertion and deletion efficiency in the tail is high, and random access is supported.
  2. dequeIs in the head and tail insert and delete efficiency is higher, support random access, but not efficiencyvectorHigh.
  3. listThe efficiency of insertion and deletion at any location is high, but random access is not supported.
  4. setIt is implemented by red-black tree, its internal elements are automatically sorted according to their values, each element value can only appear once, not allowed to repeat, and the efficiency of insertion and deletion is higher than that of other sequence containers.
  5. mapYou can automatically create key-value correspondence, Key and value can be any type you need, according to the Key to quickly find records.

Options:

  1. If you need efficient random access, don’t care about the efficiency of insertion and deletion, usevector.
  2. If you need to insert and delete a large number of elements and don’t care about the efficiency of random access, uselist.
  3. If random access is required and efficiency of insertion and deletion of data on both ends is concerned, usedeque.
  4. If you intend to store a data dictionary and require easy access to a value based on a key, use it one-on-onemap, one to many casesmultimap.
  5. If you want to find whether an element exists in a set, use the unique casesetNot only exists in the case of usemultiset.

Time complexity:

  1. vectorThe time complexity of inserting and deleting in the header and middle position is O(N), the time complexity of inserting and deleting in the tail is O(1), and the time complexity of searching is O(1).
  2. dequeThe time complexity of inserting and deleting in the middle position is O(N), the time complexity of inserting and deleting in the head and tail is O(1), and the time complexity of searching is O(1).
  3. listThe time complexity of insertion and deletion at any position is O(1), and the time complexity of search is O(N).
  4. setmapAll are done through red-black trees, so the time complexity of insert, delete, and find operations is order log N.

1.12 Namespace

1.12.1 namespace
  • Namespaces are a mechanism for describing logical groupings in which declarations that logically belong to the same group by some criteria can be placed in the same namespace. Used to distinguish functions, classes, and variables with the same name from different libraries.
Namespace namespace_name {// code declaration}Copy the code
  • Namespaces. You can use the nameless namespace member name directly in the current compilation unit (outside of the nameless namespace), but it is not visible outside of the current compilation unit. It protects code from being used illegally by others by keeping it local.
Namespace {// code declaration}Copy the code
  • You cannot declare (another nested) subnamespaces in the definition of a namespace. Subnamespaces can only be defined in the definition of a namespace.
  • Not directly usedNamespace name :: Member name......To add a new member to a namespace, you must first add the declaration of the new member to the definition of the namespace.
  • Namespaces are open, meaning that new member names can be added to existing namespaces at any time. The way to do this is to declare and define the same namespace multiple times, adding its own new members and names each time.
1.12.2 using
  • You can useusing namespaceDirective so that namespaces are not preceded by the name of the namespace when used. This directive tells the compiler that subsequent code will use the name in the specified namespace.
#include <iostream> using namespace std; First_space {void func(){cout << "Inside first_space" << endl; }} second_space{void func(){cout << "Inside second_space" << endl; } } using namespace first_space; Int main () {// call the function func() in the first namespace; return 0; }Copy the code
  • In addition, you can use the using compilation directive (combining keywords)using namespace), it can be usedUsing declarationTo simplify the use of names in the namespace:Using namespace name ::[namespace name ::...] Member name;. Pay attention to the keywordusingIt’s not followed by a keywordnamespaceAnd the last must be the name of the member in the namespaceusingAt the end of the compilation directive, it must be the namespace name.

Using directives can be used once and for all for all members of the entire namespace, which is convenient. Using declarations must be declared one at a time for the names of different members of the namespace. In general, however, it is safer to use a using declaration. Because the using declaration only imports the specified name, the compiler will report an error if the name conflicts with the local name. The using directive imports the names of all members in the entire namespace, including names that may not be used at all. If a name conflicts with a local name, the compiler does not issue any warning. Instead, the local name is used to automatically override the namespace member with the same name. In particular, the openness of namespaces means that the members of a namespace can be scattered in multiple places, making it difficult for programmers to know exactly what names have been added to the namespace.

Java calls C/C++

  • loading.soLibrary;
//MainActivity.java
static {
        System.loadLibrary("native-lib");
}
Copy the code
  • Write Java functions;
//MainActivity.java
public native String stringFromJNI();
Copy the code
  • Write C/C++ functions;
//native-lib. CPP #include <jni.h> #include <string> Java_packageName_methodName) extern "C" JNIEXPORT jString JNICALL Java_com_example_cppdemo_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }Copy the code
  • NDK /cmake configuration (only cmake configuration is listed below);
# set the minimum version of cbuild required to build the local library. Cmake_minimum_required (VERSION 3.4.1) # Create and name a library, set it to static # or shared, and provide a relative path to its source code. You can define multiple libraries, and cbuild builds them for you. Gradle automatically packages shared libraries with your APK. Add_library (native-lib # sets the name of the library. The name of the SO file is "libnative-lib. SO". When loading, "system.loadLibrary ("native-lib");". SHARED # Set the library to a SHARED library. Hellojni.cpp # provides a relative path to another source file in the same SO file) # Searches for the specified prebuilt library and stores the path as a variable. Because cbuild contains the system libraries in the search path by default, you only need to specify the name of the common NDK library that you want to add. Cbuild verifies that the library exists before completing the build. Find_library (log-lib # sets the name of the path variable. Log # specifies the name of the NDK library you want CMake to locate. The library specified should link to your target library. You can link multiple libraries, such as those defined in this build script, pre-built third-party libraries, or system libraries. Target_link_libraries (native-lib # specifies the target library. ${log-lib} # Link target library to log library included in NDK.) Add_library (natave-lib # set the name of the library. The name of the other so file SHARED # sets the library to a SHARED library. Natavejni.cpp # provides a relative path to the source file) target_link_libraries(natave-lib # specifies the target library. ${log-lib} # Link target library to log library included in NDK.)Copy the code
// build.gradle(:app) android {compileSdkVersion 29 buildToolsVersion "30.0.2" defaultConfig {applicationId "Com. Example. Cppdemo" minSdkVersion 16 targetSdkVersion 29 versionCode 1 versionName testInstrumentationRunner "1.0" "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags "" } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard - rules. Pro externalNativeBuild}} {cmake {path "/ SRC/main/CPP CMakeLists. TXT" version "3.10.2"}}}Copy the code

Iii. JNI Foundation

3.1 JNIEnv 、JavaVM

  • JavaVMJNI is a representation of the Java virtual machine in the JNI layer.
  • From the above code, we can see that while the Java function takes no arguments, the native function takes two arguments, the first argumentJNIEnvIs an interface pointer to a table of available JNI functions, second argumentjobjectIs a Java object reference to an instance of the class in which the Java function resides;
  • Jnienvs are JavaVM’s representation in threads. Each thread has one JNIEnv. JNI may have many JNiEnVs. Jnienvs are thread dependent.
  • JNIEnvThe type actually represents the Java environment, through thisJNIEnv* Pointer to the java-side code:

Calling Java functions; Manipulating Java objects;

  • JNIEnvIs essentially a thread-dependent structure that represents the JNI environment and holds a large number of JNI function Pointers.
  • JNIEnvThe internal structure is as follows:

  • JavaVMIs structured as follows:

3.2 Data Types

3.2.1 Basic Data Types
Signature format Java Native Description
B byte jbyte signed 8 bits
C char jchar unsigned 16 bits
D double jdouble 64 bits
F float jfloat 32 bits
I int jint signed 32 bits
S short jshort signed 16 bits
J long jlong signed 64 bits
Z boolean jboolean unsigned 8 bits
V void void N/A
3.2.2 Array data types

Array abbreviation: before adding [

Signature format Java Native
[B byte[] jbyteArray
[C char[] jcharArray
[D double[] jdoubleArray
[F float[] jfloatArray
[I int[] jintArray
[S short[] jshortArray
[J long[] jlongArray
[Z boolean[] jbooleanArray
3.2.3 Complex data types

Object type abbreviation: L+classname +;

Signature format Java Native
Ljava/lang/String; String jstring
L+classname +; All the objects jobject
[L+classname +; Object[] jobjectArray
Ljava.lang.Class; Class jclass
Ljava.lang.Throwable; Throwable jthrowable
3.2.4 Function signature

(Input parameter...) Return value parameter

Signature format Java functions
()V void func()
(I)F float func(int i)
([I)J long func(int[] i)
(Ljava/lang/Class;) D double func(Class c)
([ILjava/lang/String;)Z boolean func(int[] i,String s)
(I)Ljava/lang/String; String func(int i)

3.3 JNI Operations on JAVA objects and classes

  • Get the class of the Java object you need to access:
jclass thisclazz = env->GetObjectClass(thiz); // Use the GetObjectClass method to get the jclass corresponding to thiz. jclass thisclazz = env->FindClass("com/xxx/xxx/abc"); // Search for the class name directlyCopy the code
  • Get MethodID:
/** * thisclazz --> jclass * "onCallback"--> name of the method to call * "(I)Ljava/lang/String; See the table in section 3.2 above for the Signature of the method. */ jmethodID mid_callback = env->GetMethodID(thisclazz , "onCallback", "(Ljava/lang/String;) I"); jmethodID mid_callback = env->GetStaticMethodID(thisclazz , "onCallback", "(Ljava/lang/String;) I"); // Get the ID of the static methodCopy the code
  • Call method:
jint result = env->CallIntMethod(thisclazz , mid_callback , jstrParams); jint result = env->CallStaticIntMethod(thisclazz , mid_callback , jstrParams); // Call static methodsCopy the code

Post JNI common interface documents, there is a need to query here.

3.4 the JNI references

3.4.1 Local reference
  • throughNewLocalRefAnd various JNI interfaces are created (FindClass,NewObject,GetObjectClassandNewCharArray, etc.).
  • Prevents GC from collecting referenced objects that are not used across functions within local functions and cannot be used across lines.
  • After the function returns, the object referenced by the local reference is either automatically freed by the JVM or manually freed.
  • Manual release: GetXXX must call ReleaseXXXGetStringUTFCharsAfter that, callReleaseStringUTFCharsRelease; For manually createdjclass.jobjectEtc object useDeleteLocalRefMethod to release.
3.4.2 Global Reference
  • callNewGlobalRefCreate based on local references.
  • This prevents GC from reclaiming the referenced object. It can be used across methods, across threads.
  • The JVM does not automatically release it; you must manually release it.
  • Global references remain valid until explicitly freed and must be passedDeleteGlobalRefTo manually remove the global reference call.
3.4.3 Weak global Reference
  • callNewWeakGlobalRefBased on local reference or global reference.
  • It does not prevent GC from collecting referenced objects and can be used across methods and threads.
  • The reference is not automatically freed, but is reclaimed when the JVM thinks it should (such as when memory is tight). Or callDeleteWeakGlobalRefRelease manually.

subsequent

This article references some videos, books, and blogs. I’m not going to list it here.