折腾折腾用android studio去编写so--动态注册
本帖最后由 欧雨鹏 于 2020-10-23 22:02 编辑静态注册C方法:https://www.52pojie.cn/thread-1288308-1-1.html
序言
因为android studio版本我给升级到4.1。有些以前的操作可能无法进行完整的操作。
之前在搜索资料的时候,看到一位前辈提到,除了cmake,还可以ndk-build。
现在重新进行一次操作。动态注册c/c++,代码量比静态注册C比较多。
分两步过程 :先把代码码一遍。再一次性生成操作。
一、创建jni类,生成h和实现native,绑定注册
1.创建jni类,添加native方法
在src/main/java/com.example.myapplication里面创建 java类,操作:选中com.example.myapplication,右键,New---Java Class.
这里取的类名为:lstnjisuan
添加native方法,为loadLibrary设置名称
代码如下:
package com.example.myapplication;
public class lstnjisuan {
static{
System.loadLibrary("funjisuan");//设置so名称
}
public native float add(float firstnumber,float secondnumber);//加法方法
public native float sub(float firstnumber,float secondnumber);//减法方法
public native float mul(float firstnumber,float secondnumber);//剩法方法
public native float div(float firstnumber,float secondnumber);//除法方法
}
然后先把MainActivity里的调用方法写完,后面就不会再来写了。
先给界面拖几个控件,设置 一下id
控件为:2个 plain text,4个button,1个textview
用于输入的两个plain text ,id分别为:numbertext1,numbertext2
5个button 用于选 择加减乘除,计算按钮,id分别为addbutton,subbutton,mulbutton,divbutton
1个textview控件用于显示结果,id为:sumtextview
因为懒得搞布局,把辅助线添加,拉一拉界线。免得运行控件全跑了。(惨痛的教训)
这样,控件就设置完成了,界面也算做好了。
下面开始写代码,在MainActivity类里进行初始化和计算操作
先对控件做一个初始化.
代码如下:
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button add;//这五个按钮用于获取id和进行监听
private Button sub;
private Button mul;
private Button div;
private EditText first;//用来获取输入两个操作数
private EditText second;
private TextView result;//用来转换后输出到textview显示结果的变量
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contextinIt();
}
privatevoid contextinIt()
{
first=findViewById(R.id.numbertext1);
second=findViewById(R.id.numbertext2);
add=(Button) findViewById(R.id.addbutton);
sub=(Button) findViewById(R.id.subbutton);
mul=(Button) findViewById(R.id.mulbutton);
div=(Button) findViewById(R.id.divbutton);
result=findViewById(R.id.sumtextview);
}
}
findViewById这个函数有什么 作用?
定位函数,主要是引用.R文件里的引用名。一般在R.java文件里系统会自动帮你给出你在XML里定义的ID或者Layout里面的名称。
接着设置button监听计算.
完整代码如下:
package com.example.myapplication9;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View.OnClickListener;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button add;//这四个按钮用于获取id和进行监听
private Button sub;
private Button mul;
private Button div;
private EditText first;//用来获取输入两个操作数
private EditText second;
private TextView result;
private float oneNumber;//临时变量,用于计算和获取结果
private float twoNumber;
private float resultsum=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
controlinIt();
add.setOnClickListener(onls);
sub.setOnClickListener(onls);
mul.setOnClickListener(onls);
div.setOnClickListener(onls);
}
private void controlinIt()
{
first=findViewById(R.id.numbertext1);
second=findViewById(R.id.numbertext2);
add=(Button) findViewById(R.id.addbutton);
sub=(Button) findViewById(R.id.subbutton);
mul=(Button) findViewById(R.id.mulbutton);
div=(Button) findViewById(R.id.divbutton);
result=(TextView)findViewById(R.id.sumtextview);
}
privateOnClickListener onls=new OnClickListener()
{
public void onClick(View v)
{
lstnjisuan ls=new lstnjisuan();
switch (v.getId())
{
case R.id.addbutton:
oneNumber=Float.parseFloat(first.getText().toString());
twoNumber=Float.parseFloat(second.getText().toString());
resultsum=ls.add(oneNumber,twoNumber);
result.setText(Float.toString(resultsum));
break;
case R.id.subbutton:
oneNumber=Float.parseFloat(first.getText().toString());
twoNumber=Float.parseFloat(second.getText().toString());
resultsum=ls.sub(oneNumber,twoNumber);
result.setText(Float.toString(resultsum));
break;
case R.id.mulbutton:
oneNumber=Float.parseFloat(first.getText().toString());
twoNumber=Float.parseFloat(second.getText().toString());
resultsum=ls.mul(oneNumber,twoNumber);
result.setText(Float.toString(resultsum));
break;
case R.id.divbutton:
oneNumber=Float.parseFloat(first.getText().toString());
twoNumber=Float.parseFloat(second.getText().toString());
resultsum=ls.div(oneNumber,twoNumber);
result.setText(Float.toString(resultsum));
break;
default:
break;
}
}
};
}
2.生成h文 件,并创建 c文 件实现,注册和绑定native方法
在main层,创建 一个jni文 件夹,操作:New---Folder---JNI Folder
alt+f12打开terminal,cd 到java层
执行javah命令
javah -d ../jni com.example.myapplication.lstnjisuan
生成h文件。
创建c文件,实现计算,注册和绑定
选中jni文件夹,右键,New---C/C++ Source File,选择文件格式为.c
这里设置名称为:bindNativeJisuan
实现代码如下:
//
// Created by pady on 2020/10/22.
//
#include "com_example_myapplication_lstnjisuan.h"
JNIEXPORT jfloat JNICALL Java_com_example_myapplication_lstnjisuan_add
(JNIEnv *env, jobject obj, jfloat one, jfloat two)
{
return one+two;
}
JNIEXPORT jfloat JNICALL Java_com_example_myapplication_lstnjisuan_sub
(JNIEnv *env, jobject obj, jfloat one, jfloat two)
{
return one-two;
}
JNIEXPORT jfloat JNICALL Java_com_example_myapplication_lstnjisuan_mul
(JNIEnv *env, jobject obj, jfloat one, jfloat two)
{
return one*two;
}
JNIEXPORT jfloat JNICALL Java_com_example_myapplication_lstnjisuan_div
(JNIEnv *env, jobject obj, jfloat one, jfloat two)
{
return one/two;
}
//绑定java层中native方法
static const JNINativeMethod nativeMethod[]={
{"add","(FF)F",(void**)&Java_com_example_myapplication_lstnjisuan_add},
{"sub","(FF)F",(void**)&Java_com_example_myapplication_lstnjisuan_sub},
{"mul","(FF)F",(void**)&Java_com_example_myapplication_lstnjisuan_mul},
{"div","(FF)F",(void**)&Java_com_example_myapplication_lstnjisuan_div}
};
//注册nativeMethod方法,注册相应的类以及方法
jint registNativeMethod(JNIEnv *env)
{
char* strname="com/example/myapplication9/lstnjisuan";
jclass funclass=(*env)->FindClass(env,"strname");
//进行JAVA层注册,如果不等于0返回错误
if(((*env)-> RegisterNatives(env, funclass,nativeMethod,sizeof(nativeMethod)/sizeof(nativeMethod)))!=0)
{
return JNI_ERR;
}
return JNI_OK;
}
//实现JNI_ONLOAD动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm,void *reserved)
{
JNIEnv *env;
#ifndef JNI_VERSION_1_4
if((*jvm)->GetEnv(jvm,(void**)&env,JNI_VERSION_1_4)!=JNI_OK)
{
return JNI_ERR;
}
if(registNativeMethod(env)!=JNI_OK)
{
return JNI_ERR;
}
return JNI_VERSION_1_4;
#endif
if((*jvm)->GetEnv(jvm,(void**)&env,JNI_VERSION_1_6)!=JNI_OK)
{
return JNI_ERR;
}
if(registNativeMethod(env)!=JNI_OK)
{
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
讲解:
JNINativeMethod 这是个结构体,在jni.h中定义。结构体如下:/*
* used in RegisterNatives to describe native method name, signature,
* and function pointer.
*/
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;上面有提到的,三个参数,name是java类中的native方法名,第二个参数是签名,就是指参数和返回值,最后是对应C函数的地址比如上面看到的"(FF)F",F是什么呢?是Smali语法的数据类型,F是float类型。括号里面两个FF,指明有两个float参数,外面的F,指明返回值是float.其它数据类型请查看smali语法。jint是什么?就是int类型。请看jni.h文件就明白了。上面很多函数都可以在jni.h文件中查看声明定义。可在jdk版本的include文件夹里找到jni.h文件。
上面除了计算实现的函数,其它的一列系操作最后就是主要被jni_onload函数调用的。代码都写好了。现在开始生成so文件。
============================================================================================================================================================================================================================
二、用ndk-build生成so操作(这一部分可不看,是旧版的操作,因为操作没有成功。采用CMake,请直接往下拉到第三部分看正常生成的操作。。)
这一段是不成功的操作,生成配置写在.mk文件里。折腾了一天都没成功。不知道是什么原因。1.在jni文件夹里,创建两个文件:android.mk和application.mk操作:New---File,直接输入android.mk .再次New---File,直接输入application.mk选中android.mk,输入以下内容LOCAL_PATH:=$(call my-dir)#开头必须定义的LOCAL_PATH,my-dir是宏函数,用来返回android.mk所在目录的路径
include $(CLEAR_VARS)#负责清除LOCAL_ 变量,除了path
LOCAL_MODULE:=funjisuan#模块名称,就是生成so的名称,和loadlibrary设置的名称一致
LOCAL_SRC_FILES:=Cfunjisuan.c#C源文件
include $(BUILD_SHARED_LIBRARY) #设置动态链接
application.mk内容如下:APP_ABI :=all
APP_PLATFORM:=android-16
生成支持的所有版本如下图:
在terminal里cd 到jni层,就可以直接ndk-build了。操作方法:ndk-build
出现一个警告:Android NDK: WARNING: APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in E:/DownLoad/MyApplication8/app/src/main/AndroidManifest.xml. NDK binaries will *not* be compatible with de
vices older than android-16. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.
不添加就提示默认为android-16,添加了又给出这个警告。不知道怎么解决。so文件顺利生成。顺便查看了一下AndroidManifest.xml<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>搜索WARNING: APP_PLATFORM android-16 is higher than android:minSdkVersion 1 ,大多说要把build/core/add-application.mk中的__ndk-warning改成__ndk_info改了依然警告.不管它了。查看生成的情况。发现在main层生成libs和obj
最后一步,直接Rebuild Project。这一步操作是成功的。查看一下生成的APK.so文件没有打包进去,也就是lib文件生成的各个版本的so没有打包进去。这种APK安装后是无法运行的。因为加载不到so文件。需要在build.gradle中设置ndkbuild?那么在build.gradle设置一下:在defaultConfig 节点里面添加下面代码: externalNativeBuild{
ndkBuild{
arguments "NDK_APPLICATION_MK:=src/main/jni/application.mk"
cFlags "-DTEST_C_FLAGS1","-DTEST_C_FLAG2"
cppFlags "-DTEST_CPP_FLAGS1","-DTEST_CPP_FLAG2"
}
}
在andoird节点添加下面代码: externalNativeBuild {
ndkBuild {
path file('src/main/jni/android.mk')
}
}
完整代码如下:plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild{
ndkBuild{
arguments "NDK_APPLICATION_MK:=src/main/jni/application.mk"
cFlags "-DTEST_C_FLAGS1","-DTEST_C_FLAG2"
cppFlags "-DTEST_CPP_FLAGS1","-DTEST_CPP_FLAG2"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
ndkBuild {
path file('src/main/jni/android.mk')
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
再次执行ReBuild Project又出现报错
D:/SDK/ndk/21.1.6352462/build//../build/core/build-module.mk:34: *** Android NDK:Assertion failure: LOCAL_MAKEFILE is not defined .Stop.
executing external native build for ndkBuild E:\DownLoad\MyApplication8\app\src\main\jni\android.mk提示LOCAL_MAKEFILE is not defined。什么玩意?这个LOCAL_MAKEFILE是什么?找到build/core/build-module.mk,打开一看。#
$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT))
$(call check-LOCAL_MODULE,$(LOCAL_MAKEFILE))
# This file is used to record the LOCAL_XXX definitions of a given
# module. It is included by BUILD_STATIC_LIBRARY, BUILD_SHARED_LIBRARY
# and others.
#
LOCAL_MODULE_CLASS := $(strip $(LOCAL_MODULE_CLASS))
ifndef LOCAL_MODULE_CLASS
$(call __ndk_info,$(LOCAL_MAKEFILE):$(LOCAL_MODULE): LOCAL_MODULE_CLASS definition is missing !)
$(call __ndk_error,Aborting)
endif
$(if $(call module-class-check,$(LOCAL_MODULE_CLASS)),,\
$(call __ndk_info,$(LOCAL_MAKEFILE):$(LOCAL_MODULE): Unknown LOCAL_MODULE_CLASS value: $(LOCAL_MODULE_CLASS))\
$(call __ndk_error,Aborting)\
)
$(call module-add,$(LOCAL_MODULE))
# Eval sucks. It's not possible to preserve even properly escaped # characters
# as far as I can tell, and we need that for -Werror=#warnings. Manually stash
# all the flags variations so we can preserve these.
__ndk_modules.$(LOCAL_MODULE).ASFLAGS := $(LOCAL_ASFLAGS)
__ndk_modules.$(LOCAL_MODULE).ASMFLAGS := $(LOCAL_ASMFLAGS)
__ndk_modules.$(LOCAL_MODULE).CFLAGS := $(LOCAL_CFLAGS)
__ndk_modules.$(LOCAL_MODULE).CLANG_TIDY_FLAGS := $(LOCAL_CLANG_TIDY_FLAGS)
__ndk_modules.$(LOCAL_MODULE).CONLYFLAGS := $(LOCAL_CONLYFLAGS)
__ndk_modules.$(LOCAL_MODULE).CPPFLAGS := $(LOCAL_CPPFLAGS)
__ndk_modules.$(LOCAL_MODULE).CXXFLAGS := $(LOCAL_CXXFLAGS)
__ndk_modules.$(LOCAL_MODULE).LDFLAGS := $(LOCAL_LDFLAGS)
__ndk_modules.$(LOCAL_MODULE).RENDERSCRIPT_FLAGS := $(LOCAL_RENDERSCRIPT_FLAGS)
于是折腾了很久,最后android.mk改成这样。LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS:=optional
LOCAL_MODULE:=funjisuan
LOCAL_SRC_FILES:=bindNativeJisuan.c
LOCAL_MODULE_CLASS:=SHARED_LIBRAYIES
include $(BUILD_SHARED_LIBRARY)重新ndk-build正常生成so.接着继续ReBuild Project又报错了:Execution failed for task ':app:generateJsonModelDebug'.
> executing external native build for ndkBuild E:\DownLoad\MyApplication8\app\src\main\jni\android.mk
得,卡在这一步了。查了半天,都说要降低cmake版本。这是不可能的事了。。果断放弃。
============================================================================================================================================================================================================================
删除操作,后面再开始cmakebuild.gradle设置中删除下面这条设置externalNativeBuild{
ndkBuild{
arguments "NDK_APPLICATION_MK:=src/main/jni/application.mk"
cFlags "-DTEST_C_FLAGS1","-DTEST_C_FLAG2"
cppFlags "-DTEST_CPP_FLAGS1","-DTEST_CPP_FLAG2"
}
}
删除下面这条设置externalNativeBuild {
ndkBuild {
path file('src/main/jni/android.mk')
}
}再删除除android.mk,删除application.mk点一下sync now
============================================================================================================================================================================================================================
三、用CMake生成SO
ndk-build果断玩不动。还是继续CMAKE
在gradle.properties中,添加下面这条:ndk{
moduleName "funjisuan"
}
在app层里的build.gradle中,android节点的设置。defaultConfig节点中添加下面内容:externalNativeBuild{
cmake{
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'//生成多个版本的so文件
}
}
defaultConfig节点外面,android节点里面添加下面内容: externalNativeBuild{
cmake{
path "CMakeLists.txt"//设置所要编写的C源码位置,以及编译后so文件的名字
}
}
完整设置如下:android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild{
cmake{
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'//生成多个版本的so文件
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild{
cmake{
path "CMakeLists.txt"//设置所要编写的C源码位置,以及编译后so文件的名字
}
}
}
在app层,新建CMakeLists.txt文件,操作:选中app,右键,New---FileCMakeLists.txt添加下面内容:# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
#CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
# 设置so文件名称.
funjisuan
# Sets the library as a shared library.
SHARED
# 设置这个so文件为共享.
# Provides a relative path to your source file(s).
# 设置这个so文件为共享.
src/main/jni/bindNativeJisuan.c)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
# 制定目标库.
funjisuan
# Links the target library to the log library
# included in the NDK.
${log-lib} )
好了就可以直接点上面的Build----Rebuild project
运行效果如下:
总结:动态注册中,native方法不是static,C方法中,需要用JNI_onLoad注册。后面生成so的操作过程是一样的。之前操作的直接返回一个static方法(返回字符串).最后还是用CMAKE生成SO。现在比较新的android studio版本,可以创建Native C++,只需要把C++层代码写一写就可以直接生成了。之所以搞这么老的操作,是想理解这个过程。以后写这种代码肯定直接创建Native C++项目。不必再这样折腾。
最后,不得不说的一句,代码逻辑有问题。点击计算按钮就闪退了。搞不动。睡觉。。
============================================================================================================================================================================================================================
现在编写SO创建Native C++项目。
Next,设置好项目名称,再Next ,选择C++标准。再点击finish就完成创建.
完了项目已经自动生成cpp文件,CMakeLists.txt。需要改动的地方就三个文件,MainActivity类里,因为system.loadlibrary写在这里,需要修改so名称直接在这里改。cpp文件名称可直接修改,然后实现代码完好。CMakeLists.txt,需要修改一下cpp被修改后的名称,还有LoadLibrary修改后的名称。就可以直接Build了。
============================================================================================================================================================================================================================
这又过了一天。昨天写的C代码居然因为一个字母导致异常退出了。
经过数次的调试,原来是c文件在FindClass时,写错了类文件名称。导致找不到jni类.所以再来修改一下。修改的地方是:
jint registNativeMethod(JNIEnv *env)
{
char* strname="com/example/myapplication9/lstnjisuan";
jclass funclass=(*env)->FindClass(env,"strname");
//进行JAVA层注册,如果不等于0返回错误
if(((*env)-> RegisterNatives(env, funclass,nativeMethod,sizeof(nativeMethod)/sizeof(nativeMethod)))!=0)
{
return JNI_ERR;
}
return JNI_OK;
}
原来写的是:
jclass funclass=(*env)->FindClass(env,"./listnjisuan");
现在改过来了。
简易器计算稍微正常了。没有对输入框对输入的数字进行判断。这种BUG以后再去补充。
运行效果如下:
这整个过程就算是完成了。
总结一下:
动态注册C就是通过JNI_onLoad来完成。在android的jni文档里有示例。h文件生不生成其实没关系,至少目前没关系,因为都在C文件实现了。只不过写C语言写习惯了,总会写个h文件进行声明。
上面提到的生成so方法两种:
1.把CMakeLists.txt文件设置完,build.gradle,还有build.properties设置一下,整个代码写完可以直接build就可以了。过程不算复杂。
2.不要创建默认的空项目,或者hello项目,选择Native C++项目,cpp代码写好,native代码写好。直接修改CMakeLists.txt的内容,cpp名称,so名称,就可以直接Build。
贝优妮塔 发表于 2020-10-23 10:47
写so居然这么麻烦
比C++写dll 麻烦多了
没怎么玩过java
so 约等于 dllso是在Android 平台运行的(linux也行),dll 是在window平台运行的,他这个只是借助Android studio来写so, linux可以直接写,用不了这么麻烦,只是Android 开发一般会用Android studio来写,方便和Android程序一起调试 小骚 发表于 2020-10-23 08:42
那么问题来了.........为啥要用代码生成h或者cpp.......右键新建一个或者直接改后缀貌似都可以的吧........ ...
没问题的。目前写的只是没用到h文件。用javah命令创建h文件是,因为省去了再去写代码的步骤。目前的例子没用到h文件,其实不创建h文件也可以的。直接创建个.c或者.cpp把代码写进去就好。android studio中的Native C++项目,自动创建的就只有cpp文件,没有h文件。可见h文件不是必须的。 6666,真不错 有电复杂 有些看不明白,慢慢看,感谢分享 那么问题来了.........为啥要用代码生成h或者cpp.......右键新建一个或者直接改后缀貌似都可以的吧.............我用着好像没毛病{:301_982:} 小骚 发表于 2020-10-23 08:42
那么问题来了.........为啥要用代码生成h或者cpp.......右键新建一个或者直接改后缀貌似都可以的吧........ ...
写so居然这么麻烦
比C++写dll 麻烦多了
没怎么玩过java
一直认为so=dll的
{:1_937:} 楼主威武!厉害厉害 好复杂啊 楼主厉害! 感谢大佬的分享,学习学习{:301_1000:}