Qt5.6 安卓下的WebView实现JavaScript通信

另一只更好的实现方式点此

Qt官方的Webview仅仅在Qtquick中支持安卓和ios,安卓的官方实现是调用安卓系统自带的浏览器API,但是Qt官方没有写js交互,于是研究了一通宵得出几个解决方案:

1.使用Qt官方的QML webview和HTML5的Websockt,在js中使用websockt,在qt qucik中使用websocket服务器,结果可行,可惜安卓很多版本不支持Websockt,于是乎3个小时过去了。

2.既然websockt行不通,就想了下socket.io库,能运行~可惜qt quick的Websockt服务器不支持socket.io的协议,于是乎 3小时又浪费了

3.使用HTML sockt或post,post要执行的函数,用QML的eval函数来执行字符串代码,于是乎用xhl来发送post数据,然后用C++建立TcpSocket来接收,但是我放弃了,解析HTML封包不划算,为了发送4个字符他会发一大堆HTML协议字符串…因为 socket由C++接收.websockt在 qt quick中,js post->sockt->qt quick 要跨3层交互才收的到数据,实在蛋疼…更重要的是还要避免意外情况,于是乎2小时又浪费了

4.最后花了5个小时,在国外看到一些开源代码,经过修改 实现了原生交互,依然是调用安卓的webview,内嵌到qt中

具体实现:c++ –> java –> js

因为安卓的浏览器和java的交互是最稳定的







先生成Qt 安卓项目,先编译一次,在目录下新建android文件夹(QT中可以创建) 和 assets文件夹

把androud-build中的src目录下全部文件复制到android目录,打开eclipse,在QtActivity包下添加一个类:
下面代码是参考过来的:

package org.qtproject.qt5.android.bindings;

public interface NativeCalls {
    public void createNewWebView(int tag);
    public void removeWebView(int tag);
    public void moveWebView(int tag, int x, int y);
    public void resizeWebView(int tag, int w, int h);
    public void attachWebViewToMainLayout(int tag);
    public void loadUrlAtWebView(int tag, String url);
    public void loadHtmlAtWebView(int tag, String html); 
}

然后再添加一个类,用来继承webview,定制一些功能,比如开启js和弹窗等,并且添加JavaScript交互接口,接口函数在C++中定义,在JAVA中声明、

package org.qtproject.qt5.android.bindings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.webkit.*;
import android.content.Context;
import android.util.Log;

public class Cjavascript  {

    public Cjavascript  ( ) {


    }

    public  native int  testcall2();
    public  native void  testcall3();
    public  native void  testcall4();
    public  void testcall()
    {
        Log.i("testcalltestcalltestcalltestcall", "call:");
    }
}
package org.qtproject.qt5.android.bindings;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;


public class MyWebView extends WebView {

    public MyWebView(Context context) {

        super(context); 
        init();

        // TODO 自动生成的构造函数存根
    }



    @SuppressLint("SetJavaScriptEnabled")
    public void  init() {
        this.getSettings().setJavaScriptEnabled(true);
        this.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
            //否则不会执行js的Alert
        this.setWebChromeClient(new WebChromeClient()
            {   public boolean onJsAlert(WebView view, String url, String message,
                        JsResult result)
                {
                    //Auto-generated method stub
                    return super.onJsAlert(view, url, message, result);
                }

            });
        this.setWebViewClient(new WebViewClient());
        this.setBackgroundColor(Color.BLACK); 

        this.addJavascriptInterface(new Cjavascript(), "obj");
    }
}

最后就是修改Qt自己生成的QtActivity类,在作用域中添加以下代码即可:
下面的代码大部分参考别人的,少量修改。官方源码中都是使用RunOnUiThread,下面的是用handle进行多线程界面操作。

 //-------------WEBVIEW----------------
    protected ArrayList<MyWebView> webViewsList = new ArrayList<MyWebView>();

    //-----------------------------------------------------------------------------
    protected MyWebView findWebViewByTag(int tag) {
        MyWebView webViewRes = null;
        for (int i = 0; i < webViewsList.size(); i++) {
            MyWebView wv = (MyWebView) webViewsList.get(i);
            if (((Integer)wv.getTag()).intValue() == tag) {
                webViewRes = wv;
                break;
            }
        }

        return webViewRes;
    }

    //-----------------------------------------------------------------------------
    @Override
    public void createNewWebView(int tag) {
        Message msg = new Message();
        msg.what = tag;

        createNewWebViewHandler.sendMessage(msg);
    }

    protected Handler createNewWebViewHandler = new Handler() {
        @SuppressLint("SetJavaScriptEnabled")
        @Override
        public void handleMessage(Message msg) {
            MyWebView webView = new MyWebView(QtActivity.this);

            webView.setTag(msg.what);
//              webView.getSettings().setJavaScriptEnabled(true);
//              webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
//              //否则不会执行js的Alert
//              webView.setWebChromeClient(new WebChromeClient()
//              {   public boolean onJsAlert(WebView view, String url, String message,
//                          JsResult result)
//                  {
//                      //Auto-generated method stub
//                      return super.onJsAlert(view, url, message, result);
//                  }
//
//              });
//              webView.setWebViewClient(new WebViewClient());
//              webView.setBackgroundColor(Color.BLACK); 

            webViewsList.add(webView); 

        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void removeWebView(int tag) {
        Message msg = new Message();
        msg.what = tag;

        removeWebViewHandler.sendMessage(msg);
    }

    protected Handler removeWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            MyWebView webView = findWebViewByTag(msg.what);
            if (webView != null) {
                webViewsList.remove(webView);
                FrameLayout mainLayout = (FrameLayout) findViewById(R.id.content);
                mainLayout.removeView(webView);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void moveWebView(int tag, int x, int y) {
        Message msg = new Message();
        msg.what = tag;
        msg.arg1 = x;
        msg.arg2 = y;

        moveWebViewHandler.sendMessage(msg);
    }


    protected Handler moveWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            View viewToMove = null;
            viewToMove = findWebViewByTag(msg.what);

            if (viewToMove != null) {
                FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) viewToMove
                        .getLayoutParams();
                params.leftMargin = msg.arg1;
                params.topMargin = msg.arg2;
                viewToMove.setLayoutParams(params);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void resizeWebView(int tag, int w, int h) {
        Message msg = new Message();
        msg.what = tag;
        msg.arg1 = w;
        msg.arg2 = h;

        resizeWebViewHandler.sendMessage(msg);
    }


    protected Handler resizeWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            View viewToResize = null;
            viewToResize = findWebViewByTag(msg.what);

            if (viewToResize != null) {
                FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) viewToResize
                        .getLayoutParams();
                params.width = msg.arg1;
                params.height = msg.arg2;
                viewToResize.setLayoutParams(params);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void attachWebViewToMainLayout(int tag) {
        Message msg = new Message();
        msg.what = tag;

        attachWebViewToMainLayoutHandler.sendMessage(msg);
    }

    protected Handler attachWebViewToMainLayoutHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            View viewToAttach = null;
            viewToAttach = findWebViewByTag(msg.what);

            if (viewToAttach != null) {
                FrameLayout mainLayout = (FrameLayout) findViewById(R.id.content);
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(100, 100);
                params.leftMargin = 0;
                params.topMargin = 0;

                mainLayout.addView(viewToAttach, params);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void loadUrlAtWebView(int tag, String url) {
        Log.i("url", "curid:"+Thread.currentThread().getId());
        Message msg = new Message();
        msg.what = tag;
        msg.obj = url;


         loadUrlAtWebViewHandler.sendMessage(msg);
    }

    protected Handler loadUrlAtWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.i("url", "mainid:"+Thread.currentThread().getId());
            MyWebView webView = findWebViewByTag(msg.what);
            if (webView != null) {
                webView.loadUrl(msg.obj.toString());
                Log.i("url", msg.obj.toString());
            }
            //Log.i("TestNative*********", "call:");
            //TestNative  testNative  = new TestNative();
            //testNative.Test();
        }
    };

    //-----------------------------------------------------------------------------

    @Override
    public void loadHtmlAtWebView(int tag, String html) {
        Message msg = new Message();
        msg.what = tag;
        msg.obj = html;

        loadHtmlAtWebViewHandler.sendMessage(msg);
    }

    protected Handler loadHtmlAtWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            MyWebView webView = findWebViewByTag(msg.what);
            if (webView != null) {
                webView.loadData(msg.obj.toString(), "text/html; charset=UTF-8", "UTF-8");

            }
        }
    };

C++层,注册JavaScript函数:

#ifndef JAVA_JAVASCTRIPTFUNC_H
#define JAVA_JAVASCTRIPTFUNC_H
#include <jni.h>
#include <QtAndroidExtras/QtAndroidExtras>
#include <QtCore/QString>

class JAVA_javasctriptFunc
{
public:
    JAVA_javasctriptFunc();
    bool regfuc();
};

#endif // JAVA_JAVASCTRIPTFUNC_H

#include "java_javasctriptfunc.h"
int cout=0;
static jint testcall2(JNIEnv *env, jobject thiz)
{
    cout++;
   qInfo() << "MY C+++: testcall2"<<QThread::currentThreadId() ;

   return cout;
       // QMessageBox::information(NULL,"HEHE","33");
}
static void testcall3(JNIEnv *env, jobject thiz)
{
   qInfo() << "MY C+++: testcall3"<<QThread::currentThreadId()  ;
       // QMessageBox::information(NULL,"HEHE","33");
}
static void testcall4(JNIEnv *env, jobject thiz)
{
   qInfo() << "MY C+++: testcall4"<<QThread::currentThreadId()  ;
       // QMessageBox::information(NULL,"HEHE","33");
}


JAVA_javasctriptFunc::JAVA_javasctriptFunc()
{


}


 bool JAVA_javasctriptFunc::regfuc()
 {

     JNINativeMethod methods[] {
         {"testcall2", "()I", (void*)testcall2},
         {"testcall3", "()V", (void*)testcall3},
         {"testcall4", "()V", (void*)testcall4}

     };
 //package org.qtproject.qt5.android.bindings;
     const char *classname = "org/qtproject/qt5/android/bindings/Cjavascript";
     jclass clazz;
     QAndroidJniEnvironment env;

     QAndroidJniObject javaClass(classname);
     clazz = env->GetObjectClass(javaClass.object<jobject>());
     qDebug() << "find ExtendsQtNative - " << clazz;
     bool result = false;
     if(clazz)
     {
         jint ret = env->RegisterNatives(clazz,
                                         methods,
                                         sizeof(methods) / sizeof(methods[0]));
         env->DeleteLocalRef(clazz);
         qDebug() << "RegisterNatives return - " << ret;
         result = ret >= 0;
     }
     if(env->ExceptionCheck()) env->ExceptionClear();
     return result;
 }

WEBVIEW的C++层实现:

#pragma once

#include <jni.h>
#include <QtAndroidExtras/QtAndroidExtras>
#include <QtCore/QString>

class CAndroidNativeCallsSender
{
public:
    CAndroidNativeCallsSender();
    ~CAndroidNativeCallsSender();

    void createNewWebView( int id ) const;
    void removeWebView( int id ) const;
    void attachWebViewToMainLayout( int id ) const;
    void moveWebView( int id, int x, int y ) const;
    void resizeWebView( int id, int w, int h ) const;
    void loadUrlAtWebView( int id, QString const& url ) const;
    void loadHtmlAtWebView( int id, QString const& html ) const;

protected:
    jmethodID m_createNewWebViewMID;
    jmethodID m_removeWebViewMID;
    jmethodID m_attachWebViewToMainLayoutMID;
    jmethodID m_moveWebViewMID;
    jmethodID m_resizeWebViewMID;
    jmethodID m_loadUrlAtWebViewMID;
    jmethodID m_loadHtmlAtWebViewMID;

    jobject m_objectRef;
};
#include "AndroidNativeCallsSender.h"
#include <QtAndroidExtras/QAndroidJniObject>
//-----------------------------------------------------------------------------
CAndroidNativeCallsSender::CAndroidNativeCallsSender()
{
    QAndroidJniEnvironment jniEnv;
    QAndroidJniObject qObjAct = QAndroidJniObject::callStaticObjectMethod( "org/qtproject/qt5/android/QtNative",
                                                                           "activity",
                                                                           "()Landroid/app/Activity;" );
    jobject objAct = qObjAct.object<jobject>();
    m_objectRef = jniEnv->NewGlobalRef( objAct );
    jclass cls = jniEnv->GetObjectClass( objAct );

    if( cls )
    {
        m_createNewWebViewMID = jniEnv->GetMethodID( cls, "createNewWebView", "(I)V" );
        m_removeWebViewMID = jniEnv->GetMethodID( cls, "removeWebView", "(I)V" );
        m_attachWebViewToMainLayoutMID = jniEnv->GetMethodID( cls, "attachWebViewToMainLayout", "(I)V" );
        m_moveWebViewMID = jniEnv->GetMethodID( cls, "moveWebView", "(III)V" );
        m_resizeWebViewMID = jniEnv->GetMethodID( cls, "resizeWebView", "(III)V" );
        m_loadUrlAtWebViewMID = jniEnv->GetMethodID( cls, "loadUrlAtWebView", "(ILjava/lang/String;)V" );
        m_loadHtmlAtWebViewMID = jniEnv->GetMethodID( cls, "loadHtmlAtWebView", "(ILjava/lang/String;)V" );
    }
}

//-----------------------------------------------------------------------------
CAndroidNativeCallsSender::~CAndroidNativeCallsSender()
{
    if( m_objectRef != NULL )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->DeleteGlobalRef( m_objectRef );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::createNewWebView( int id ) const
{
    if( m_createNewWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->CallVoidMethod( m_objectRef, m_createNewWebViewMID, id );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::removeWebView( int id ) const
{
    if( m_removeWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->CallVoidMethod( m_objectRef, m_removeWebViewMID, id );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::attachWebViewToMainLayout( int id ) const
{
    if( m_attachWebViewToMainLayoutMID )
    {
        QAndroidJniEnvironment jniEnv;

        jniEnv->CallVoidMethod( m_objectRef, m_attachWebViewToMainLayoutMID, id );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::moveWebView( int id, int x, int y ) const
{
    if( m_moveWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->CallVoidMethod( m_objectRef, m_moveWebViewMID, id, x, y );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::resizeWebView( int id, int w, int h ) const
{
    if( m_resizeWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;

        jniEnv->CallVoidMethod( m_objectRef, m_resizeWebViewMID, id, w, h );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::loadUrlAtWebView( int id, QString const& url ) const
{
    if( m_loadUrlAtWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jstring jurl = jniEnv->NewStringUTF( url.toUtf8().constData() );
        jniEnv->CallVoidMethod( m_objectRef,
                                m_loadUrlAtWebViewMID,
                                id,
                                jurl );
        jniEnv->DeleteLocalRef( jurl );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::loadHtmlAtWebView( int id, QString const& html ) const
{
    if( m_loadUrlAtWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jstring jhtml = jniEnv->NewStringUTF( html.toUtf8().constData() );
        jniEnv->CallVoidMethod( m_objectRef,
                                m_loadHtmlAtWebViewMID,
                                id,
                                jhtml );
        jniEnv->DeleteLocalRef( jhtml );
    }
}
#pragma once

#include <QtWidgets/QWidget>
#include <QtCore/QPoint>

#include "AndroidNativeCallsSender.h"

class QtCustomAndroidWebView : public QWidget
{
    Q_OBJECT
public:
    explicit QtCustomAndroidWebView( QWidget *parent = 0 );
    ~QtCustomAndroidWebView();

    void loadURL( QString const& url ) const;
    void loadHtmlData( QString const& data ) const;

    void move( int x, int y );
    void move( QPoint const& p );
    void resize( int w, int h );

    int androidID() const { return m_androidID; }

protected:
    static int sm_tag;
    int m_androidID;
    CAndroidNativeCallsSender m_nativeSender;

    int generateNewTag();

signals:

public slots:
};
#include "QtCustomAndroidWebView.h"

//#include <QtWidgets/QLayout>

//-----------------------------------------------------------------------------
int QtCustomAndroidWebView::sm_tag = 0;

//-----------------------------------------------------------------------------
QtCustomAndroidWebView::QtCustomAndroidWebView( QWidget *parent )
    : QWidget( parent )
    , m_androidID( generateNewTag() )
{
//    if( parent && parent->layout() )
//        parent->layout()->addWidget( this );

    m_nativeSender.createNewWebView( androidID() );
    m_nativeSender.attachWebViewToMainLayout( androidID() );
}

//-----------------------------------------------------------------------------
QtCustomAndroidWebView::~QtCustomAndroidWebView()
{
    m_nativeSender.removeWebView( androidID() );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::loadURL( QString const& url ) const
{
    m_nativeSender.loadUrlAtWebView( androidID(), url );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::loadHtmlData( QString const& data ) const
{
    m_nativeSender.loadHtmlAtWebView( androidID(), data );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::move( int x, int y )
{
    QWidget::move( x, y );
    m_nativeSender.moveWebView( androidID(), x, y );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::move( QPoint const& p )
{
    QtCustomAndroidWebView::move( p.x(), p.y() );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::resize( int w, int h )
{
    QWidget::resize( w, h );
    m_nativeSender.resizeWebView( androidID(), w, h );
}

//-----------------------------------------------------------------------------
int QtCustomAndroidWebView::generateNewTag()
{
    sm_tag++;
    return sm_tag;
}

测试代码

#include "mytest.h"
#include "QtCustomAndroidWebView.h"
#include<QScreen>
#include <QApplication>
#include<QMessageBox>
#include<jni.h>


MyTest::MyTest(QWidget *parent) : QWidget(parent)
{
    vbox = new QVBoxLayout(this);

    btn = new QPushButton("call");
    btn->setText("CALL");
    edit = new QTextEdit();
      vbox->addWidget(btn);
      vbox->addWidget(edit);

//    webview = new QtCustomAndroidWebView();

//    webview->resize(this->width(),this->height());
//   webview->move(0,0);
//   webview->loadURL("http://www.baidu.com");
//   vbox->addWidget(webview);

 QScreen *screen = QApplication::primaryScreen();

   webview = new QtCustomAndroidWebView( );

     webview->resize(this->width(), 200);
     webview->loadURL("file:///android_asset/index.html");

    //注册js函数
     javajavasctriptFunc = new JAVA_javasctriptFunc();
     if(javajavasctriptFunc->regfuc()==false)
     {
                qDebug()<<"error";
     } 
     vbox->addWidget(webview);

      connect(btn,SIGNAL(clicked(bool)),this,SLOT(onclick()));
}


 void  MyTest::onclick()
 {
     int cout=0;
     while (1) {
         QThread::msleep(200);
         QString s;
         s.sprintf("javascript:test2(%d)",cout);
         cout++;
         webview->loadURL(s);
//      执行js脚本 返回值可参考Qt android src中WebView的实现     webview->loadURL("javascript:test()");
     }

 }
$(function(){

    $("button").click(function(){
    var  x=obj.testcall2();

         $("#list1").append("<li><a href=\"#\">Inbox<span class=\"ui-li-count\">"+x+"</span></a></li>");
        $('#list1').listview("refresh");

  });


});


如何调用JavaScript能获取返回值,可参考Qt的源码,其实没啥必要,只需要注册一个函数给js调用,让他把返回值弄进来就是

 m_webSettingsSetDisplayZoomControls = webSettings.getClass().getMethod("setDisplayZoomControls", boolean.class);
                        if (Build.VERSION.SDK_INT > 18) {
                            m_webViewEvaluateJavascript = m_webView.getClass().getMethod("evaluateJavascript",
                                                                                         String.class,
                                                                                         ValueCallback.class);
                        }
    public void runJavaScript(final String script, final long callbackId)
    {
        if (script == null)
            return;

        if (Build.VERSION.SDK_INT < 19 || m_webViewEvaluateJavascript == null)
            return;

        m_activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    m_webViewEvaluateJavascript.invoke(m_webView, script, callbackId == -1 ? null :
                        new ValueCallback<String>() {
                            @Override
                            public void onReceiveValue(String result) {
                                c_onRunJavaScriptResult(m_id, callbackId, result);
                            }
                        });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是三种方式在Android中获取Webview中的JavaScript返回值或属性的示例代码: 1. 使用JavaScriptInterface接口: ```java // 创建一个Java对象,用于与JavaScript对象绑定 class MyJavaScriptInterface { @JavascriptInterface public void showValue(String value) { Log.d("JavaScript", "Value from JavaScript: " + value); } } // 将Java对象与JavaScript对象绑定 webView.addJavascriptInterface(new MyJavaScriptInterface(), "MyObject"); // 在JavaScript代码中调用Java对象的方法 var value = "Hello, world!"; MyObject.showValue(value); ``` 2. 使用evaluateJavascript方法: ```java // 执行JavaScript代码,并获取执行结果 webView.evaluateJavascript("document.getElementById('myText').value", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.d("JavaScript", "Value from JavaScript: " + value); } }); ``` 3. 使用WebChromeClient的onJsPrompt方法: ```java // 创建一个继承自WebChromeClient的类,重写onJsPrompt方法 class MyWebChromeClient extends WebChromeClient { @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { Log.d("JavaScript", "Value from JavaScript: " + message); // 将用户输入的数据返回给JavaScript代码 result.confirm("Input received"); return true; } } // 设置WebChromeClient webView.setWebChromeClient(new MyWebChromeClient()); // 在JavaScript代码中弹出对话框,并获取用户输入的数据 var value = prompt("Please input a value:", ""); ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值