JNI

Android Framework基础之JNI JNI(Java Native Interface,Java本地接口),用于打通Java层与Native(C/C++)层。这不是Android系统所独有的,而是Java所有。众所周知,Java语言是跨平台的语言,而这跨平台的背后都是依靠Java虚拟机,虚拟机采用C/C++编写,适配各个系统,通过JNI为上层Java提供各种服务,保证跨平台性。相信不少经常使用Java的程序员,享受着其跨平台性,可能全然不知JNI的存在。 阅读详情

JNI

 

 

JNI

目录
定义
设计目的
书写步骤
简要使用例子
调用中考虑的问题
对JAVA传入数据的处理

定义

JNI是Java Native Interface 的缩写,中文为JAVA本地调用。从 Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
  使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机 实现下。

设计目的

·The standard Java class library may not support the platform-dependent features needed by your application.
  ·You may already have a library or application written in another programming language and you wish to make it accessible to Java applications
  ·You may want to implement a small portion of time-critical code in a lower-level programming language, such as assembly, and then have your Java application call these functions

书写步骤

·编写带有native声明的方法的java类
  ·使用 javac 命令编译所编写的java类
  ·使用javah ?jni java类名生成扩展名为h的头文件
  ·使用C/C++实现本地方法
  ·将C/C++编写的文件生成动态连接库
  ·ok
   1) 编写java程序:这里以HelloWorld为例。
  代码1:
  class HelloWorld {
  public native void displayHelloWorld();
  static {
  System.loadLibrary("hello");
  }
  public static void main(String[] args) {
  new HelloWorld().displayHelloWorld();
  }
  }
  声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 Load动态库:System.loadLibrary("hello");加载动态库(我们可以这样理解:我们的方法 displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是 System .loadLibrary();的参数“hello”是动态库的名字。
   2) 编译
  没有什么好说的了 javac HelloWorld.java
   3) 生成扩展名为h的头文件 javah ?
  jni HelloWorld 头文件的内容: /* DO NOT EDIT THIS FILE - it is machine generated */
  1. include
  /* Header for class HelloWorld */
  1. ifndef _Included_HelloWorld
  2. define _Included_HelloWorld
  3. ifdef __cplusplus
  extern "C" {
  1. endif
  /*
  * Class: HelloWorld
  * Method: displayHelloWorld
  * Signature: ()V
  * /
  JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);
  1. ifdef __cplusplus
  }
  1. endif
  2. endif
  (这里我们可以这样理解:这个h文件相当于我们在java里面的接口,这里声明了一个 Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写 C/C++ 程序的时候所使用的方法名必须和这里的一致)。
   4) 编写本地方法实现和由javah命令生成的头文件里面声明的方法名相同的方法。
  代码2:
  1 #include "jni.h"
  2 #include "HelloWorld.h"
  3 //#include other headers
  4 JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)
  {
  printf("Hello world!/n");
  return;
  }
  注意代码2中的第1行,需要将jni.h(该文件可以在%JAVA_HOME%/include文件夹下面找到)文件引入,因为在程序中的JNIEnv、 jobject等类型都是在该头文件中定义的;另外在第2行需要将HelloWorld.h头文件引入(我是这么理解的:相当于我们在编写java程序的时候,实现一个接口的话需要声明才可以,这里就是将HelloWorld.h头文件里面声明的方法加以实现。当然不一定是这样)。然后保存为 HelloWorldImpl.c就ok了。
   5) 生成动态库
  这里以在Windows中为例,需要生成dll文件。在保存HelloWorldImpl.c文件夹下面,使用VC的编译器cl成。 cl -I%java_home%/include -I%java_home%/include/win32 -LD HelloWorldImp.c -Fehello.dll 注意:生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字是hello。当然这里修改之后那里也需要修改。另外需要将-I%java_home%/include -I%java_home%/include/win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。
   6) 运行程序 java HelloWorld就ok.

简要使用例子

下面是一个简单的例子实现打印一句话的功能,但是用的c的printf最终实现。一般提供给java的jni接口包括一个so文件(封装了c函数的实现)和一个java文件(需要调用path的类)。
  1. JNI的目的是使java方法中能够调用c实现的一些函数,比如以下的java类,就需要调用一个本地函数testjni(一般声明为private native类型),首先需要创建文件weiqiong.java,内容如下:
  class weiqiong { static { System.loadLibrary("testjni");//载入静态库,test函数在其中实现 } private native void testjni(); //声明本地调用 public void test() { testjni(); } public static void main(String args[]) { weiqiong haha = new weiqiong(); haha.test(); } }
  2.然后执行javac weiqiong.java,如果没有报错,会生成一个weiqiong.class。
  3.然后设置classpath为你当前的工作目录,如直接输入命令行:set classpath = weiqiong.class所在的完整目录(如 c:/test)再执行javah weiqiong,会生成一个文件weiqiong.h文件,其中有一个函数的声明如下:
  JNIEXPORT void JNICALL Java_weiqiong_testjni (JNIEnv *, jobject);
  4.创建文件testjni.c将上面那个函数实现,内容如下:
  1. include
  2. include
  JNIEXPORT void JNICALL Java_weiqiong_testjni (JNIEnv *env, jobject obj) { printf("haha---------go into c!!!/n"); }
  5.为了生成.so文件,创建makefile文件如下:
  libtestjni.so:testjni.o makefile gcc -Wall -rdynamic -shared -o libtestjni.so testjni.o testjni.o:testjni.c weiqiong.h gcc -Wall -c testjni.c -I./ -I/usr/java/j2sdk1.4.0/include -I/usr/java/j2sdk1.4.0/include/linux cl: rm -rf *.o *.so 注意:gcc前面是tab空,j2sdk的目录根据自己装的j2sdk的具体版本来写,生成的so文件的名字必须是loadLibrary的参数名前加“lib”。
  6.export LD_LIBRARY_PATH=.,由此设置library路径为当前目录,这样java文件才能找到so文件。一般的做法是将so文件copy到本机的LD_LIBRARY_PATH目录下。
  7.执行java weiqiong,打印出结果:“haha---------go into c!!!”

调用中考虑的问题

在首次使用JNI的时候有些疑问,后来在使用中一一解决,下面就是这些问题的备忘:
  1。 java和c是如何互通的?
  其实不能互通的原因主要是数据类型的问题,jni解决了这个问题,例如那个c文件中的jstring数据类型就是java传入的String对象,经过jni函数的转化就能成为c的char*。
  对应数据类型关系如下表:
  Java 类型 本地c类型 说明 boolean jboolean 无符号,8 位 byte jbyte 无符号,8 位 char jchar 无符号,16 位 short jshort 有符号,16 位 int jint 有符号,32 位 long jlong 有符号,64 位 float jfloat 32 位 double jdouble 64 位 void void N/A
  JNI 还包含了很多对应于不同 Java 对象的引用类型如下图:
  2. 如何将java传入的String参数转换为c的char*,然后使用?
  java传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);注意:test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->ReleaseStringUTFChars(env, jstring, test);
  3. 将c中获取的一个char*的buffer传递给java?
  这个char*如果是一般的字符串的话,作为string传回去就可以了。如果是含有’/0’的buffer,最好作为bytearray传出,因为可以制定copy的length,如果copy到string,可能到’/0’就截断了。
  有两种方式传递得到的数据:
  一种是在jni中直接new一个byte数组,然后调用函数(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。
  一种是return错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:
  class RetObj { public byte[] bytearray; } 这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下: jclass cls; jfieldID fid; jbyteArray bytearray; bytearray = (*env)->NewByteArray(env,len); (*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer); cls = (*env)->GetObjectClass(env, retobj); fid = (*env)->GetFieldID(env, cls, "retbytes", "[B"]); (*env)->SetObjectField(env, retobj, fid, bytearray);
  4. 不知道占用多少空间的buffer,如何传递出去呢?
  在jni的c文件中new出空间,传递出去。java的数据不初始化,指向传递出去的空间即可。

对JAVA传入数据的处理

1. 如果传入的是bytearray的话,作如下处理得到buffer:
  char *tmpdata = (char*)(*env)->GetByteArrayElements(env, bytearray, NULL); (*env)->ReleaseByteArrayElements(env, bytearray, tmpdata, 0);

百度百科中的词条内容仅供参考,如果您需要解决具体问题
(尤其在法律、医学等领域),建议您咨询相关领域专业人士。
本词条对我有帮助 109
扩展阅读:
1. http://www.java2000.net/p11161 JAVA通过JNI获得CPU的使用率(Usage Rate)
2.crybaby2005.bokee.com
3.miniufo.bokee.com
[我来完善 ] 相关词条:
开放分类:
技术 , j2ee , 规范
JNI语法 JNI参考 JNI函数大全 原文地址:JNI语法 JNI参考 JNI函数大全 内容太多,请按Ctrl+F查找你需要的信息。   一、对照表 Java类型    本地类型         描述 boolean    jboolean       C/C++8位整型 byte       jbyte          C/C++带符号的8位整型 char       jchar          阅读详情

相关推荐

Android JNI + AIDL: 第1篇: Android JNI 编程

1. Android's JNI Android JNI编程说到底就是3个步骤,   (1) ①在需要调用本地C/C++实现的API的上层APK的编程中,先声明和引用native性质的jni的java接口,这个java接口的命名可以随意,调用和其他接口一样,声明标注public native性质 例子:   ②利用JDK 生成JNI的头文件。根据这个JNIjava接口的声明和调用,生成JN

CodingSky 3128

JNI详解

JNI

weixin_46987432的博客 1万+

Android系统的JNI原理分析(4)- JNI的jni.h头文件

声明 前阶段在项目中使用了Android的JNI技术,在此文中做些技术知识总结。 此篇参考一些博客和书籍,不方便逐一列出,仅供学习、知识分享,代码基于Android 7.1.1。 本章Demo下载:Demo链接 1 JNI的原头文件jni.h     查看Android的JNI接口集合可以在jni.h文件中看到,此头文件对C风格和C++风格做了区分,在各个JNI函数的参数上会差一个env参数,等下后面章节举例说明,先看下jni.h文件中声明的JNI接口函数(虽然把jni.h代码单独贴在篇博客里有充水嫌疑

Android系统工程师--小馬佩德罗 4850

JNI编程详解

package com.example.testjni; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle

李棚的优快云专栏 9729

JNI核心头文件:jni.h与jni_md.h深入解析

jni.hjni.h定义了一系列关键数据类型和宏,以便开发者可以创建和操作Java对象。关键数据类型包括jclassjmethodIDjobjectjstring等,它们用于引用Java对象的类、方法、实例和字符串。宏定义则允许直接操作这些类型,例如获取对象的字段值,调用方法等操作。jni_md.hjni_md.h中定义了各种特定于平台的函数,这些函数使得开发者可以在不深入了解底层细节的情况下,实现本地代码与平台特有的功能之间的交互。例如,getpid()

weixin_42124497的博客 1342

LaTeX新手入门以及TeXlive和TeXstudio的安装使用

作为一个新手,LaTeX的了解和使用很是费了一番功夫,因此,在这里给找不到路的同志们稍微介绍一下。如有错误,敬请指正。本篇内容基于Win10系统,因此安装在Mac或者Linux上的同学可以再找找其他的介绍文章。 目录 LaTeX介绍 LaTeX环境配置 一、TeXlive下载 二、TeXlive安装 1. 安装main installer 2. 正式安装 三、TeXstudio安装...

zywhehe的博客 21万+

一篇文章教你完全掌握jni技术

通过这样一个简单的案例,将大部分jni相关的API都练习了一遍,不难看出,java层能实现的功能,在native层一样可以实现,但这里仅仅是为了练习jni,实际项目中不会把一些无关紧要的功能写在native层,比如UI操作,因为同样的功能,java代码要简洁得太多。在一些拥有复杂算法的场景(音视频编解码,图像绘制等),java的执行效率远低于C/C++的执行效率,使用jni技术,在java层调用C/C++代码,可以提高程序的执行效率,最大化利用机器的硬件资源。的内容,这个取决于实际的需求。

chuyouyinghe的专栏 3360

天龙八部源码深度解析:从宠物系统到副本机制(C++游戏开发实战)

本文深度解析《天龙八部》源码架构,重点剖析宠物系统状态机实现与副本动态加载机制等核心模块。通过C++代码示例展示宠物AI状态迁移逻辑和副本场景管理技术,为现代游戏开发提供兼容性改造方案与设计启示。适合游戏开发者学习经典MMORPG的工程化实现路径。

weixin_29011239的博客 386

Android JNI详解,让你彻底了解JNI (上)

Android开发中,随着对移动程序的安全性、性能等方面的重视,JNI技术也越发重要。如今,多数企业在招聘中、高级程序员时,基本上都要求熟悉JNI开发,所以,掌握JNI技术,也是我们迈进心仪企业的必备条件。本套课程共分为三部分,当前为第一部分,主要包括内容:1、JNI名词释义2、JNI环境搭建3、Native项目解析4、编译工具使用5、边学边实战,从易到难,循序渐进适合人群:1、有一定Android基础,零JNI基础的学员;2、接触过JNI,但却不甚了解的学员;3、使用过JNI,希望解其原理的学员;后面两部分课程,将带领大家深入了解JNI交互原理,Native实战开发,让你真正掌握JNI技术。

500多种目标检测数据集下载地址汇总(YOLO、VOC)

【目标检测】红外物体检测-人、自行车、公交、小车、摩托车、骑行者、动物等15000张YOLO+VOC(含部分截取增强图).zip。【目标检测】医用个人防护装备检测数据集981张5类别(口罩、工作服、护目镜、手套、面罩检测数据集)VOC+YOLO格式.zip。【目标检测】车辆分类数据集1880张7类VOC+YOLO(含摩托车、救护车、消防车、警车、警用摩托车、轿车、大车).zip。【目标检测】隐翅虫数据集386张VOC+YOLO【目标检测】隐翅虫数据集386张VOC+YOLO【目标检测】隐翅虫数据集。

2401_85863780的博客 9151

使用JAVA 的 POI 库 将DOC文件转为DOCX文件

java使用POI库,将doc文件转为docx文件

qq_45808266的博客 5706

JNI原理及常用方法概述

JNIEnv概念 :JNIEnv是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境。通过JNIEnv可以调用到一系列JNI系统函数。JNIEnv线程相关性:每个线程中都有一个 JNIEnv 指针。JNIEnv只在其所在线程有效, 它不能在线程之间进行传递。注意:在C++创建的子线程中获取JNIEnv,要通过调用JavaVM的AttachCurrentThread函数获得。

Easyhood的博客 3327

Win10 LTSC 2021 CPU占用率飙升?KB5017308补丁修复全攻略

本文针对Windows 10 LTSC 2021系统中因wsappx进程导致的CPU占用率异常飙升问题,提供了详细的解决方案。核心在于安装微软官方发布的KB5017308补丁,该补丁能从根本上修复系统组件缺陷,使CPU占用恢复正常。文章详解了补丁的下载、安装步骤、注意事项及安装后的验证方法,是解决此问题的权威指南。

python9snake的博客 1263

Android JNI详解

Android使用JNI的原理解析

疯过无痕的博客 8504

uniapp 全局弹窗

功能详解1.有的组件需要全局调用,不想要在一个页面里面一个个引入,现在就需要写入到全局中,直接全局触发。2.通过 $rechargePop就能全局触发 充值的弹窗实现步骤一、先把组件封装好,如 rechargePop组件位置 components/RechargePop/index.vue ,就定义一个methods open方法就可以 二、在同级别路径下定义一个 index.js 三、引入到main.js 四、全局调用this.$rechargePop(); 就能弹出那个弹窗

weixin_42066070的博客 657

国科大人工智能原理与算法-2024-第一次作业-雷震老师

1. 反射行动(比如从热炉子上缩回你的手)是理性的吗?它们是智能的吗? 2. 以下的计算机系统在何种程度上是人工智能的实例: - 超市条码扫描器 - 网络搜索引擎 - 语音激活的电话菜单 - 对网络状态动态响应的网络路由算法 3. 为什么进化会倾向于导致行为合理的系统?设计这样的系统想达到的目标是什么?

MatsumotoChrikk 2928

android jni (jni_onload方式)

JNI(Java Native Interface),Java本地接口,是为方便java调用C或者C++等本地代码所封装的一层接口。由于Java的跨平台性导致本地交互能力不好,一些和操作系统相关的特性Java无法完成,于是Java提供了JNI专门用于和本地代码交互。 NDK(Native Development Kit),是android提供的一个工具合集,帮助开发者快速开发C(或C++)的动态库,并能自动将.so和java应用一起打包成apk。NDK集成了交叉编译器(交叉编译器需要UNIX或LINUX系统

qq_33782617的博客 1万+

基于分水岭分割进行肺癌诊断

近年来,肺癌的发病率和病死率均迅速上升,目前已居所有癌症之首。随着肺癌病人数量的增加,医生对肺部 CT 图像进行研判的工作量也增加了不少,在这种情况下难免工作效率降低甚至会出现误诊。为了帮助医生减少重复性工作,对肺部 CT 图像进行计算机辅助检测的技术就被广泛应用于对肺癌的诊断和治疗过程中。医学 CT 图像处理主要是研究医学图像中的器官和组织之间的关系,并进行病理性分析。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 1568

【JNI】JNI基础语法

本文详细介绍了JNI(Java Native Interface)的核心语法与Java交互方法。首先对比了C和C++在JNI中的差异:C++需用extern "C"避免名称修饰,且JNIEnv的调用方式不同(C用(*env)->,C++用env->)。其次,讲解了JNI数据类型,包括基本类型(如jint、jboolean)和引用类型(如jobject、jstring),并区分了局部引用、全局引用和弱全局引用的使用场景。此外,文章详细说明了字符串和数组的操作方法,如NewStringUTF创建字符串、Ge

little_fat_sheep 1185

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

JNI/NDK入门指南之JNI访问数组

        JNI/NDK入门指南之JNI访问数组   在前面的章节JNI/NDK入门指南之JNI字符串处理中讲解了JNI对字符串的处理流程。今天我们继续向JNI的知识海洋进军讲解JNI对数组的处理。本章内容有点多哦! 前言 JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的,基本类型数组中的所有元素都是 JNI的基本数据类型,可以直接访问。而对象数组中的所有元素...

IT先森 4581

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【JNI编程】JNI异常

Java中异常处理非常简单,我们直接在Java代码中try…catch…即可。假设使用JNI技术在native代码中调用Java方法,而这个Java方法有可能抛出异常,如何在JNI中进行异常处理呢?我们又想在JNI中抛出异常具体怎样操作?这些问题都会在JNI编码中进行涉及。 一、API回顾 1.1 Throw jint Throw(JNIEnv *env, jthrowable obj); 导致...

洪伟的专栏 2129

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

JNI编程—JNI基础

什么是JNI,怎么使用 JNI——Java Native Interface,它是Java平台的一个特性(并不是Android系统特有的)。其实主要是定义了一些JNI函数,让开发者可以通过调用这些函数实现Java代码调用C/C++的代码,C/C++的代码也可以调用Java的代码,这样就可以发挥各个语言的特点了。那么怎么使用JNI呢,一般情况下我们首先是将写好的C/C++代码编译成对应平台的动态库(windows一般是dll文件,linux一般是so文件等),这里我们是针对Android平台,所以只讨论so

G的博客 1853

NDK 入门(三)—— JNI 注册与 JNI 线程

在 Native 方法中把 JNIEnv 和 jobject 封装在 MyContext 中,然后把 MyContext 传入 pthread_create() 的第四个参数,在 线程的入口方法 task 中接收 MyContext 使用两个封装的参数回调上层方法。默认的注册方式就是静态注册,上面提到的模板方法 stringFromJNI() 就是通过静态注册生成的 JNI 函数 Java_com_jni_register_MainActivity_stringFromJNI()。

tmacfrank 2145

JNI开发之JNI原理

在上一篇文章中对JNI简单介绍了,在这篇文章中将对JNI原理进行介绍。本篇文章将以JNI执行环境、JNI数据类型、JNI注册方式、JNI引用、JNI变量共享以及JNI调用方式来介绍JNI原理。   一、执行环境(Runtime)     在计算机中,每种编程语言都有一个执行环境(Runtime),执行环境用来解释执行语言的语句。     在JNI开发中有两个比较重要与执行环境Runtim

chewbee的专栏 1976

Android之JNI详解

JNI(Java Native Interface) 是 Java 提供的一种编程框架,用于在 Java 应用程序中调用和与用其他编程语言(如 C、C++ 等)编写的本地代码(Native Code)进行交互。它允许 Java 程序访问操作系统或硬件提供的底层功能。

weixin_39788742的博客 1330

jni是什么

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,...

随风而逝的博客 9136
上一篇: 表格特效制作
下一篇: [编辑本段]VIM文本编辑软件
pricks
博客等级 码龄18年 67粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值