Android JNI如何获取android opengl es 3d的扩展?

2062人阅读
android(45)
在android中使用OpenGL ES需要三个步骤:
1. 创建GLSurfaceView组件,使用Activity来显示GLSurfaceView组建。
2. 为GLSurfaceView组建创建GLSurfaceView.Renderer实例,实现GLSurfaceView.Renderer类时需要实现该接口里的三个方法:
abstract void onDrawFrame(GL10 gl):Called to draw the current frame.abstract void onSurfaceChanged(GL10 gl, int width, int height):Called when the surface changed size.abstract void onSurfaceCreated(GL10 gl, EGLConfig config):Called when the surface is created or recreated.
3. 调用GLSurfaceView组建的setRenderer (GLSurfaceView.Renderer renderer) 方法指定Renderer对象,该对象将会完成GLSurfaceView里3D图形的绘制。
然后来看一个Demo,首先是主Activity:
package com.example.
import android.opengl.GLSurfaceV
import android.os.B
import android.app.A
import android.view.M
public class AndroidGLDemo extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView glView = new GLSurfaceView(this);
AndroidGLDemoRenderer renderer = new AndroidGLDemoRenderer();
glView.setRenderer(renderer);
setContentView(glView);
public boolean onCreateOptionsMenu(Menu menu) {
// I this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
然后是Renderer的实现:
package com.example.
import java.nio.ByteB
import java.nio.ByteO
import java.nio.FloatB
import java.nio.IntB
import javax.microedition.khronos.egl.EGLC
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.R
public class AndroidGLDemoRenderer implements Renderer {
float[] mTriangleData = new float[] {
0.1f, 0.6f, 0.0f,
-0.3f, 0.0f, 0.0f,
0.3f, 0.1f, 0.0f
int[] mTriangleColor = new int[] {
0, 0, 65535, 0,
float[] mRectData = new float[] {
0.4f, 0.4f, 0.0f,
0.4f, -0.4f, 0.0f,
-0.4f, 0.4f, 0.0f,
-0.4f, -0.4f, 0.0f
int[] mRectColor = new int[] {
0, 0, 65535, 0,
FloatBuffer mTriangleDataB
IntBuffer mTriangleColorB
FloatBuffer mRectDataB
IntBuffer mRectColorB
public AndroidGLDemoRenderer() {
mTriangleDataBuffer= bufferUtil(mTriangleData);
mTriangleColorBuffer = bufferUtil(mTriangleColor);
mRectDataBuffer = bufferUtil(mRectData);
mRectColorBuffer = bufferUtil(mRectColor);
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(-0.6f, 0.0f, -1.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mTriangleDataBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, mTriangleColorBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
gl.glLoadIdentity();
gl.glTranslatef(0.6f, 0.8f, -1.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mRectDataBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, mRectColorBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
gl.glFinish();
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
float ratio = (float) width /
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 0);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
public IntBuffer bufferUtil(int []arr){
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
qbb.order(ByteOrder.nativeOrder());
buffer = qbb.asIntBuffer();
buffer.put(arr);
buffer.position(0);
public FloatBuffer bufferUtil(float []arr){
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
qbb.order(ByteOrder.nativeOrder());
buffer = qbb.asFloatBuffer();
buffer.put(arr);
buffer.position(0);
注意构造函数中那些Buffer的创建方式。在这个地方,不能直接使用FloatBuffer/IntBuffer 的wrap() method。直接用这个method创建出来的buffer会导致JE:
02-26 23:12:08.945: E/OpenGLES(2750): Application com.example.androidgldemo (SDK target 17) called a GL11 Pointer method with an indirect Buffer.
02-26 23:12:08.968: W/dalvikvm(2750): threadid=11: thread exiting with uncaught exception (group=0x40d57930)
02-26 23:12:08.984: E/AndroidRuntime(2750): FATAL EXCEPTION: GLThread 16938
02-26 23:12:08.984: E/AndroidRuntime(2750): java.lang.IllegalArgumentException: Must use a native order direct Buffer
02-26 23:12:08.984: E/AndroidRuntime(2750):
at com.google.android.gles_jni.GLImpl.glVertexPointerBounds(Native Method)
02-26 23:12:08.984: E/AndroidRuntime(2750):
at com.google.android.gles_jni.GLImpl.glVertexPointer(GLImpl.java:1122)
02-26 23:12:08.984: E/AndroidRuntime(2750):
at com.example.androidgldemo.AndroidGLDemoRenderer.onDrawFrame(AndroidGLDemoRenderer.java:63)
02-26 23:12:08.984: E/AndroidRuntime(2750):
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1516)
02-26 23:12:08.984: E/AndroidRuntime(2750):
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:167170次
积分:1986
积分:1986
排名:第12824名
原创:27篇
转载:68篇
评论:29条
(2)(1)(1)(9)(3)(5)(6)(8)(3)(2)(5)(9)(2)(5)(1)(1)(2)(1)(3)(3)(1)(4)(3)(1)(1)(1)(5)(2)(1)(1)(1)(6)(1)trackbacks-0
AndroidManifest.xml
&?xml version="1.0" encoding="utf-8"?&
&manifest xmlns:android="/apk/res/android"
package="com.android.gl2jni"&
&application
android:label="@string/gl2jni_activity"&
&activity android:name="GL2JNIActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:configChanges="orientation|keyboardHidden"&
&intent-filter&
&action android:name="android.intent.action.MAIN" /&
&category android:name="android.intent.category.LAUNCHER" /&
&/intent-filter&
&/activity&
&/application&
&uses-feature android:glEsVersion="0x"/&
&uses-sdk android:minSdkVersion="5"/&
&/manifest&
首先,我们看&activity /&标签,定义了GL2JNIActivity
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 定义该Activity的主题,全屏无标题栏。
android:launchMode="singleTask"& 定义该Activity运行模式为&singleTask&。
android:configChanges="orientation|keyboardHidden"
默认情况下,Android每次屏幕旋转都会重新创建Activity,该属性将不会重新创建Activity,而只是调用onConfigurationChange(Configuration config)。
然后,我们看&users-feature android:glEsVersion="0x"&,定义使用的OpenGL ES版本为2.0。
GL2JNIActivity.java
package com.android.gl2
import android.app.A
import android.os.B
import android.util.L
import android.view.WindowM
import java.io.F
public class GL2JNIActivity extends Activity {
GL2JNIView mV
@Override protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mView = new GL2JNIView(getApplication());
setContentView(mView);
@Override protected void onPause() {
super.onPause();
mView.onPause();
@Override protected void onResume() {
super.onResume();
mView.onResume();
mView = new GL2JNIView(getApplication());
setContentView(mView);
新建一个GL2JNIView对象,并设置该GL2JNIView为Activity的Content View显示。
@Override protected void onPause() {&&& super.onPause();&&& mView.onPause();}@Override protected void onResume() {&&& super.onResume();&&& mView.onResume();}
GL2JNIView为GLSurfaceView子类:
A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients are required to call onPause() when the activity pauses and onResume() when the activity resumes. These calls allow GLSurfaceView to pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate the OpenGL display.
GL2JNIView.java
GL2JNIView.java
package com.android.gl2
import android.content.C
import android.graphics.PixelF
import android.opengl.GLSurfaceV
import android.util.AttributeS
import android.util.L
import android.view.KeyE
import android.view.MotionE
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLC
import javax.microedition.khronos.egl.EGLC
import javax.microedition.khronos.egl.EGLD
import javax.microedition.khronos.opengles.GL10;
* A simple GLSurfaceView sub-class that demonstrate how to perform
* OpenGL ES 2.0 rendering into a GL Surface. Note the following important
* details:
* - The class must use a custom context factory to enable 2.0 rendering.
See ContextFactory class definition below.
* - The class must use a custom EGLConfigChooser to be able to select
an EGLConfig that supports 2.0. This is done by providing a config
specification to eglChooseConfig() that has the attribute
EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag
set. See ConfigChooser class definition below.
* - The class must select the surface's format, then choose an EGLConfig
that matches it exactly (with regards to red/green/blue/alpha channels
bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
class GL2JNIView extends GLSurfaceView {
private static String TAG = "GL2JNIView";
private static final boolean DEBUG = false;
public GL2JNIView(Context context) {
super(context);
init(false, 0, 0);
public GL2JNIView(Context context, boolean translucent, int depth, int stencil) {
super(context);
init(translucent, depth, stencil);
private void init(boolean translucent, int depth, int stencil) {
/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
* If we want a translucent one, we should change the surface's
* format here, using PixelFormat.TRANSLUCENT for GL Surfaces
* is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
if (translucent) {
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
/* Setup the context factory for 2.0 rendering.
* See ContextFactory class definition below
setEGLContextFactory(new ContextFactory());
/* We need to choose an EGLConfig that matches the format of
* our surface exactly. This is going to be done in our
* custom config chooser. See ConfigChooser class definition
setEGLConfigChooser( translucent ?
new ConfigChooser(8, 8, 8, 8, depth, stencil) :
new ConfigChooser(5, 6, 5, 0, depth, stencil) );
/* Set the renderer responsible for frame rendering */
setRenderer(new Renderer());
private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
Log.w(TAG, "creating OpenGL ES 2.0 context");
checkEglError("Before eglCreateContext", egl);
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
checkEglError("After eglCreateContext", egl);
public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
egl.eglDestroyContext(display, context);
private static void checkEglError(String prompt, EGL10 egl) {
while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
mRedSize =
mGreenSize =
mBlueSize =
mAlphaSize =
mDepthSize =
mStencilSize =
/* This EGL config specification is used to specify 2.0 rendering.
* We use a minimum size of 4 bits for red/green/blue, but will
* perform actual matching in chooseConfig() below.
private static int EGL_OPENGL_ES2_BIT = 4;
private static int[] s_configAttribs2 =
EGL10.EGL_RED_SIZE, 4,
EGL10.EGL_GREEN_SIZE, 4,
EGL10.EGL_BLUE_SIZE, 4,
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
/* Get the number of minimally matching EGL configurations
int[] num_config = new int[1];
egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
int numConfigs = num_config[0];
if (numConfigs &= 0) {
throw new IllegalArgumentException("No configs match configSpec");
/* Allocate then read the array of minimally matching EGL configs
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
if (DEBUG) {
printConfigs(egl, display, configs);
/* Now return the "best" one
return chooseConfig(egl, display, configs);
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
for(EGLConfig config : configs) {
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
// We need at least mDepthSize and mStencilSize bits
if (d & mDepthSize || s & mStencilSize)
// We want an *exact* match for red/green/blue/alpha
int r = findConfigAttrib(egl, display, config,
EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config,
EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config,
EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config,
EGL10.EGL_ALPHA_SIZE, 0);
if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
return null;
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
return defaultV
private void printConfigs(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
int numConfigs = configs.
Log.w(TAG, String.format("%d configurations", numConfigs));
for (int i = 0; i & numC i++) {
Log.w(TAG, String.format("Configuration %d:\n", i));
printConfig(egl, display, configs[i]);
private void printConfig(EGL10 egl, EGLDisplay display,
EGLConfig config) {
int[] attributes = {
EGL10.EGL_BUFFER_SIZE,
EGL10.EGL_ALPHA_SIZE,
EGL10.EGL_BLUE_SIZE,
EGL10.EGL_GREEN_SIZE,
EGL10.EGL_RED_SIZE,
EGL10.EGL_DEPTH_SIZE,
EGL10.EGL_STENCIL_SIZE,
EGL10.EGL_CONFIG_CAVEAT,
EGL10.EGL_CONFIG_ID,
EGL10.EGL_LEVEL,
EGL10.EGL_MAX_PBUFFER_HEIGHT,
EGL10.EGL_MAX_PBUFFER_PIXELS,
EGL10.EGL_MAX_PBUFFER_WIDTH,
EGL10.EGL_NATIVE_RENDERABLE,
EGL10.EGL_NATIVE_VISUAL_ID,
EGL10.EGL_NATIVE_VISUAL_TYPE,
0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
EGL10.EGL_SAMPLES,
EGL10.EGL_SAMPLE_BUFFERS,
EGL10.EGL_SURFACE_TYPE,
EGL10.EGL_TRANSPARENT_TYPE,
EGL10.EGL_TRANSPARENT_RED_VALUE,
EGL10.EGL_TRANSPARENT_GREEN_VALUE,
EGL10.EGL_TRANSPARENT_BLUE_VALUE,
0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
EGL10.EGL_LUMINANCE_SIZE,
EGL10.EGL_ALPHA_MASK_SIZE,
EGL10.EGL_COLOR_BUFFER_TYPE,
EGL10.EGL_RENDERABLE_TYPE,
0x3042 // EGL10.EGL_CONFORMANT
String[] names = {
"EGL_BUFFER_SIZE",
"EGL_ALPHA_SIZE",
"EGL_BLUE_SIZE",
"EGL_GREEN_SIZE",
"EGL_RED_SIZE",
"EGL_DEPTH_SIZE",
"EGL_STENCIL_SIZE",
"EGL_CONFIG_CAVEAT",
"EGL_CONFIG_ID",
"EGL_LEVEL",
"EGL_MAX_PBUFFER_HEIGHT",
"EGL_MAX_PBUFFER_PIXELS",
"EGL_MAX_PBUFFER_WIDTH",
"EGL_NATIVE_RENDERABLE",
"EGL_NATIVE_VISUAL_ID",
"EGL_NATIVE_VISUAL_TYPE",
"EGL_PRESERVED_RESOURCES",
"EGL_SAMPLES",
"EGL_SAMPLE_BUFFERS",
"EGL_SURFACE_TYPE",
"EGL_TRANSPARENT_TYPE",
"EGL_TRANSPARENT_RED_VALUE",
"EGL_TRANSPARENT_GREEN_VALUE",
"EGL_TRANSPARENT_BLUE_VALUE",
"EGL_BIND_TO_TEXTURE_RGB",
"EGL_BIND_TO_TEXTURE_RGBA",
"EGL_MIN_SWAP_INTERVAL",
"EGL_MAX_SWAP_INTERVAL",
"EGL_LUMINANCE_SIZE",
"EGL_ALPHA_MASK_SIZE",
"EGL_COLOR_BUFFER_TYPE",
"EGL_RENDERABLE_TYPE",
"EGL_CONFORMANT"
int[] value = new int[1];
for (int i = 0; i & attributes. i++) {
int attribute = attributes[i];
String name = names[i];
if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
Log.w(TAG, String.format("
%s: %d\n", name, value[0]));
// Log.w(TAG, String.format("
%s: failed\n", name));
while (egl.eglGetError() != EGL10.EGL_SUCCESS);
// Subclasses can adjust these values:
protected int mRedS
protected int mGreenS
protected int mBlueS
protected int mAlphaS
protected int mDepthS
protected int mStencilS
private int[] mValue = new int[1];
private static class Renderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 gl) {
GL2JNILib.step();
public void onSurfaceChanged(GL10 gl, int width, int height) {
GL2JNILib.init(width, height);
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Do nothing.
重载构造方法:
public GL2JNIView(Context context)
public GL2JNIView(Context context, boolean translucent, int depth, int stencil)
构造方法调用
private void init(boolean translucent, int depth, int stencil)
进行初始化工作:
if (translucent) {&&& this.getHolder().setFormat(PixelFormat.TRANSLUCENT);}
By default GLSurfaceView will create a PixelFormat.RGB_565 format surface. If a translucent surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). The exact format of a TRANSLUCENT surface is device dependent, but it will be a 32-bit-per-pixel surface with 8 bits per component.
setEGLContextFactory(new ContextFactory());
setEGLConfigChooser( translucent ?&&& new ConfigChooser(8, 8, 8, 8, depth, stencil) :&&& new ConfigChooser(5, 6, 5, 0, depth, stencil) );&
setRenderer(new Renderer());
私有内部类Renderer定义如下:
private static class Renderer implements GLSurfaceView.Renderer {&& &public void onDrawFrame(GL10 gl) {&& &&& &GL2JNILib.step();&& &}&& &public void onSurfaceChanged(GL10 gl, int width, int height) {&& &&& &GL2JNILib.init(width, height);&& &}&& &public void onSurfaceCreated(GL10 gl, EGLConfig config) {&& &&& &// Do nothing.&& &}}
当surface改变尺寸,调用GL2JNILib.init(width, height);
当画当前帧的时候,调用GL2JNILib.step();
GL2JNILib.java
package com.android.gl2
// Wrapper for native library
public class GL2JNILib {
System.loadLibrary("gl2jni");
* @param width the current view width
* @param height the current view height
public static native void init(int width, int height);
public static native void step();
static {&& &System.loadLibrary("gl2jni");}
加载静态库gl2jni
public static native void init(int width, int height);public static native void step();
声明public static native方法,通过JNI调用C/C++的native方法。
阅读(...) 评论()题主根据下面的方式获取ES2.0的扩展&br&&br&void *&br&&br&void open_libgl(void)&br&{&br& libgl = dlopen(&libGLESv2.so&, RTLD_LAZY | RTLD_GLOBAL);&br&}&br&&br&void close_libgl(void)&br&{&br& dlclose(libgl);&br&}&br&&br&void* get_proc(const char *proc)&br&{&br& void *&br& res = dlsym(libgl, proc);&br& &br&}&br&&br&void load_procs()&br&{&br& open_libgl();&br&&br& glMapBufferOES = (PFNGLMAPBUFFEROESPROC)(get_proc(&glMapBufferOES&));&br& glUnmapBufferOES = (PFNGLUNMAPBUFFEROESPROC)(get_proc(&glUnmapBufferOES&));&br&&br& close_libgl();&br&}&br&但是真机运行时还是会报called unimplement API的错误
题主根据下面的方式获取ES2.0的扩展void *void open_libgl(void){ libgl = dlopen("libGLESv2.so", RTLD_LAZY | RTLD_GLOBAL);}void close_libgl(void){ dlclose(libgl);}void* get_proc(const char *proc){ void * res = dlsym(libgl, proc); }…
你这是什么鬼动态加载一个动态库 然后获取动态库的函数 这和扩展有什么关系?动态获得扩展函数用eglGetProcAddress获取之前必须先创建可能拥有该扩展的context 然后查询扩展后再获取libgles也没必要动态加载还有就是有的用扩展 为什么不直接用gles3.0呢现在绝大部分系统都是4.4以上并且最低端的机器也基本都支持3.0
已有帐号?
无法登录?
社交帐号登录
因残致郁 生无可恋Insider SDK / OpenGL ES 2.x SDK / Android JNI 下的编译环境搭建 Android+SDK+NDK+Eclipse+Cygwin+Ant | Imagination中文技术社区}

我要回帖

更多关于 android jni opengl 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信