融云SDK集成实现单聊App


本人菜鸟一枚,自学不才,在Android开发圈子也是摸打滚爬,3,4年,之前没有写博客的习惯,只是自己总结一些,没有分享。当然好多还是参考的大神们分享的东西,去github fork  别人的代码。。

话不多说,开干!

【1】SDK的集成套路,注册登录SDK官网点击打开链接,下载SDK,看集成文档。

【2】特别值得一提的是,融云的SDK,模块化很好,下载SDK时,可以先择需要集成的模块,见下图:

IMKit SDK是融云做好的UI库,java代码封装成了jar,布局文件暴露,可以自己修改颜色样式。IMKit Module依赖IMLibModule。

【3】导入AndroidStudio时,将IMKit及IMLIb都以Module的方式导入。



然后app项目添加依赖module,只添加IMKit,即可。app的dependencies如下:


【4】在IMLib的AndroidManifest.xml中将RONG_CLOUD_APP_KEY修改为自己在官网创建项目生成的appkey。

(集成过SDK的都明白这一步!!!不赘述)


【5】在自己项目的AndroidManifest.xml文件中添加FileProvider

 
  
  1. <provider  
  2.     android:name=“android.support.v4.content.FileProvider”  
  3.     android:authorities=“com.bj.rongyundemo.FileProvider”  
  4.     android:exported=“false”  
  5.     android:grantUriPermissions=“true”>  
  6.     <meta-data  
  7.         android:name=“android.support.FILE_PROVIDER_PATHS”  
  8.         android:resource=“@xml/rc_file_path” />  
  9. </provider>  
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.bj.rongyundemo.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/rc_file_path" />
</provider>


 
  
需要注意的是authorities的值修改为"自己的包名.FileProvider"。至此融云的配置基本完成,可以进行开发了。
【6】融云初始化,在自己的Application中定义。代码如下:
 
  
 
  
  1. public class App extends Application {  
  2.     @Override  
  3.     public void onCreate() {  
  4.         super.onCreate();  
  5.         if (getApplicationInfo().packageName.equals(getCurProcessName(getApplicationContext())) ||  
  6.                 “io.rong.push”.equals(getCurProcessName(getApplicationContext()))) {  
  7.   
  8.             /**  
  9.              * IMKit SDK调用第一步 初始化  
  10.              */  
  11.             RongIM.init(this);  
  12.         }  
  13.     }  
  14.     /**  
  15.      * 获得当前进程的名字  
  16.      *  
  17.      * @param context  
  18.      * @return 进程号  
  19.      */  
  20.     public static String getCurProcessName(Context context) {  
  21.   
  22.         int pid = android.os.Process.myPid();  
  23.   
  24.         ActivityManager activityManager = (ActivityManager) context  
  25.                 .getSystemService(Context.ACTIVITY_SERVICE);  
  26.   
  27.         for (ActivityManager.RunningAppProcessInfo appProcess : activityManager  
  28.                 .getRunningAppProcesses()) {  
  29.   
  30.             if (appProcess.pid == pid) {  
  31.                 return appProcess.processName;  
  32.             }  
  33.         }  
  34.         return null;  
  35.     }  
  36. }  
public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (getApplicationInfo().packageName.equals(getCurProcessName(getApplicationContext())) ||
                "io.rong.push".equals(getCurProcessName(getApplicationContext()))) {

            /**
             * IMKit SDK调用第一步 初始化
             */
            RongIM.init(this);
        }
    }
    /**
     * 获得当前进程的名字
     *
     * @param context
     * @return 进程号
     */
    public static String getCurProcessName(Context context) {

        int pid = android.os.Process.myPid();

        ActivityManager activityManager = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);

        for (ActivityManager.RunningAppProcessInfo appProcess : activityManager
                .getRunningAppProcesses()) {

            if (appProcess.pid == pid) {
                return appProcess.processName;
            }
        }
        return null;
    }
}


【7】我写了一个有底部导航栏的主页面,一个好友列表,一个消息列表。效果如下:
 
  
 
  
 
  
1----->>问友界面只有发消息的点击事件跟融云有关系,核心代码如下:

 
  
  1. holder.tv_send.setOnClickListener(new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View view) {  
  4.  // start chat acitivity  
  5.         if(RongIM.getInstance()!=null){  
  6.             RongIM.getInstance().startPrivateChat(mContext,”10010”,”联通”);  
  7.         }  
  8.     }  
  9. });  
holder.tv_send.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
 // start chat acitivity
        if(RongIM.getInstance()!=null){
            RongIM.getInstance().startPrivateChat(mContext,"10010","联通");
        }
    }
});



startPrivateChart(a,b)是开启单聊的方式,参数a是聊天对方的id,参数b是聊天对方的昵称。
2---->开始聊天之前必须先连接融云服务器,连接融云服务器的方式是必须拿到该APP的登录帐号在融云的token。
这个token的获取需要通过ServerAPI来获取(此步需要APP服务端来做,省略)。这里我们通过融云的API调试,获取Token。
先讲一下如何官网进行API调试,如下,点击已创建的应用,点API调试,点获取Toekn,拉到最下面输入用户id即可。
 
  
 
  
 
  

获取到token之后,就可以进行连接融云服务器了。代码如下:

 
  
  1. /**  
  2.  * 建立与融云服务器的连接  
  3.  *  
  4.  * @param token  
  5.  */  
  6. private void connect(String token) {  
  7.   
  8.     if (getApplicationInfo().packageName.equals(App.getCurProcessName(getApplicationContext()))) {  
  9.   
  10.         /**  
  11.          * IMKit SDK调用第二步,建立与服务器的连接  
  12.          */  
  13.         RongIM.connect(token, new RongIMClient.ConnectCallback() {  
  14.   
  15.             /**  
  16.              * Token 错误,在线上环境下主要是因为 Token 已经过期,您需要向 App Server 重新请求一个新的 Token  
  17.              */  
  18.             @Override  
  19.             public void onTokenIncorrect() {  
  20.   
  21.                 Log.e(“tag”, ”–onTokenIncorrect”);  
  22.             }  
  23.   
  24.             /**  
  25.              * 连接融云成功  
  26.              * @param userid 当前 token  
  27.              */  
  28.             @Override  
  29.             public void onSuccess(String userid) {  
  30.                 Log.e(“tag”, ”–onSuccess” + userid);  
  31.             }  
  32.             /**  
  33.              * 连接融云失败  
  34.              * @param errorCode 错误码,可到官网 查看错误码对应的注释  
  35.              */  
  36.             @Override  
  37.             public void onError(RongIMClient.ErrorCode errorCode) {  
  38.   
  39.                 Log.e(“tag”, ”–onError” + errorCode);  
  40.             }  
  41.         });  
  42.     }  
  43. }  
/**
 * 建立与融云服务器的连接
 *
 * @param token
 */
private void connect(String token) {

    if (getApplicationInfo().packageName.equals(App.getCurProcessName(getApplicationContext()))) {

        /**
         * IMKit SDK调用第二步,建立与服务器的连接
         */
        RongIM.connect(token, new RongIMClient.ConnectCallback() {

            /**
             * Token 错误,在线上环境下主要是因为 Token 已经过期,您需要向 App Server 重新请求一个新的 Token
             */
            @Override
            public void onTokenIncorrect() {

                Log.e("tag", "--onTokenIncorrect");
            }

            /**
             * 连接融云成功
             * @param userid 当前 token
             */
            @Override
            public void onSuccess(String userid) {
                Log.e("tag", "--onSuccess" + userid);
            }
            /**
             * 连接融云失败
             * @param errorCode 错误码,可到官网 查看错误码对应的注释
             */
            @Override
            public void onError(RongIMClient.ErrorCode errorCode) {

                Log.e("tag", "--onError" + errorCode);
            }
        });
    }
}


 
  

连接成功后,会返回用户的id。

3——–>>>>>>>>>>消息列表界面,使用IMKit提供的UI。初始化一个Fragment即可。如下:

 
  
  1. /**  
  2.  * 初始化会话列表  
  3.  * @return  
  4.  */  
  5. private Fragment initConversationList() {  
  6.     if (frag_msg == null) {  
  7.         ConversationListFragment listFragment = new ConversationListFragment();  
  8.         Uri uri=Uri.parse(“rong://”+getApplicationInfo().packageName).buildUpon()  
  9.                 .appendPath(“conversationlist”)  
  10.                 .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), “false”) //设置私聊会话是否聚合显示  
  11.                 .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), “false”)//群组  
  12.                 .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), “false”)//系统  
  13.                 .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), “false”)  
  14.                 .build();  
  15.         listFragment.setUri(uri);  
  16.         return  listFragment;  
  17.     }else{  
  18.        return frag_msg;  
  19.     }  
  20. }  
/**
 * 初始化会话列表
 * @return
 */
private Fragment initConversationList() {
    if (frag_msg == null) {
        ConversationListFragment listFragment = new ConversationListFragment();
        Uri uri=Uri.parse("rong://"+getApplicationInfo().packageName).buildUpon()
                .appendPath("conversationlist")
                .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //设置私聊会话是否聚合显示
                .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "false")//群组
                .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "false")//系统
                .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), "false")
                .build();
        listFragment.setUri(uri);
        return  listFragment;
    }else{
       return frag_msg;
    }
}


【8】聊天界面编写,布局文件activity_chat.xml

 
  
  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     xmlns:app=“http://schemas.android.com/apk/res-auto”  
  4.     android:layout_width=“match_parent”  
  5.     android:layout_height=“match_parent”  
  6.     android:orientation=“vertical”>  
  7.     <android.support.v7.widget.Toolbar  
  8.         android:id=“@+id/toolbar”  
  9.         android:layout_width=“match_parent”  
  10.         app:layout_collapseMode=“pin”  
  11.         android:layout_height=“?attr/actionBarSize”  
  12.         android:background=“#4ececf”  
  13.         app:contentInsetStart=“0dp”>  
  14.   
  15.         <TextView  
  16.             android:id=“@+id/tv_title”  
  17.             android:layout_width=“wrap_content”  
  18.             android:layout_height=“wrap_content”  
  19.             android:layout_gravity=“center”  
  20.             android:text=“聊天”  
  21.             android:textColor=“@android:color/white”  
  22.             android:textSize=“20sp” />  
  23.     </android.support.v7.widget.Toolbar>  
  24.     <fragment  
  25.         android:id=“@+id/conversation”  
  26.         android:name=“io.rong.imkit.fragment.ConversationFragment”  
  27.         android:layout_width=“match_parent”  
  28.         android:layout_height=“match_parent” />  
  29. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        app:layout_collapseMode="pin"
        android:layout_height="?attr/actionBarSize"
        android:background="#4ececf"
        app:contentInsetStart="0dp">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="聊天"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
    </android.support.v7.widget.Toolbar>
    <fragment
        android:id="@+id/conversation"
        android:name="io.rong.imkit.fragment.ConversationFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>



ChatActivity.class如下:

 
  
  1. public class ChatActivity extends FragmentActivity {  
  2.     @Bind(R.id.tv_title)  
  3.     TextView tvTitle;  
  4.     @Bind(R.id.toolbar)  
  5.     Toolbar toolbar;  
  6.   
  7.     @Override  
  8.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.activity_chat);  
  11.         ButterKnife.bind(this);  
  12.         //IM  
  13.         RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒  
  14.         RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息数目  
  15.         String targetId = getIntent().getData().getQueryParameter(“targetId”);  
  16.         String title = getIntent().getData().getQueryParameter(“title”);//必须实现内容提供者才能获取到title  
  17.         if(!TextUtils.isEmpty(title)){  
  18.             tvTitle.setText(title);  
  19.         }else{  
  20.             //根据id,去服务端请求用户信息,再设置  
  21.         }  
  22.   
  23.     }  
  24. }  
public class ChatActivity extends FragmentActivity {
    @Bind(R.id.tv_title)
    TextView tvTitle;
    @Bind(R.id.toolbar)
    Toolbar toolbar;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        ButterKnife.bind(this);
        //IM
        RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒
        RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息数目
        String targetId = getIntent().getData().getQueryParameter("targetId");
        String title = getIntent().getData().getQueryParameter("title");//必须实现内容提供者才能获取到title
        if(!TextUtils.isEmpty(title)){
            tvTitle.setText(title);
        }else{
            //根据id,去服务端请求用户信息,再设置
        }

    }
}


因为我们使用的是IMKit UI库的界面,只需要的在activity中包含一个conversationFragment即可。

android:name="io.rong.imkit.fragment.ConversationFragment"

 RongIM.getInstance().enableNewComingMessageIcon(true);//显示新消息提醒
 RongIM.getInstance().enableUnreadMessageIcon(true);//显示未读消息
这两行代码是是聊天界面的消息提示。
 String targetId = getIntent().getData().getQueryParameter("targetId");
 String title = getIntent().getData().getQueryParameter("title");//必须实现内容提供者才能获取到title
targetid和title是问友界面,发送消息的事件中,startPrivate()方法中传过来的值。
最后还有最关键的一步,在自己应用的AndroidManifest.xml中配置ChatActivity。
配置如下:

 
  
  1. <activity android:name=“.ChatActivity”  
  2.     android:screenOrientation=“portrait”  
  3.     android:windowSoftInputMode=“stateHidden|adjustResize”>  
  4.     <intent-filter>  
  5.         <action android:name=“android.intent.action.VIEW” />  
  6.         <category android:name=“android.intent.category.DEFAULT” />  
  7.         <data  
  8.             android:host=“com.bj.rongyundemo”  
  9.             android:pathPrefix=“/conversation/”  
  10.             android:scheme=“rong” />  
  11.     </intent-filter>  
  12. </activity>  
<activity android:name=".ChatActivity"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateHidden|adjustResize">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data
            android:host="com.bj.rongyundemo"
            android:pathPrefix="/conversation/"
            android:scheme="rong" />
    </intent-filter>
</activity>



host修改为自己的包名。至此已经能够正常收发消息了。
【9】消息推送,融云有自己的消息推送,当然也可以集成第三方推送,融云官方均有介绍。这里我们只做融云推送。
1---->>写WenwenNotificationReceiver 
 
  
 
  
  1. public class WenwenNotificationReceiver extends PushMessageReceiver {  
  2.     @Override  
  3.     public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {  
  4.         Log.e(“tag”,”——-message——”);  
  5.         return false; // 返回 false, 会弹出融云 SDK 默认通知; 返回 true, 融云 SDK 不会弹通知, 通知需要由您自定义。  
  6.     }  
  7.   
  8.     @Override  
  9.     public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {  
  10.         Log.e(“tag”,”——-message——”);  
  11.         return false; // 返回 false, 会走融云 SDK 默认处理逻辑, 即点击该通知会打开会话列表或会话界面; 返回 true, 则由您自定义处理逻辑。  
  12.     }  
  13. }  
public class WenwenNotificationReceiver extends PushMessageReceiver {
    @Override
    public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {
        Log.e("tag","-------message------");
        return false; // 返回 false, 会弹出融云 SDK 默认通知; 返回 true, 融云 SDK 不会弹通知, 通知需要由您自定义。
    }

    @Override
    public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {
        Log.e("tag","-------message------");
        return false; // 返回 false, 会走融云 SDK 默认处理逻辑, 即点击该通知会打开会话列表或会话界面; 返回 true, 则由您自定义处理逻辑。
    }
}


2------->>在自己的清单文件中配置WenwenNotificationReceiver :
 
  
 
  
  1. <receiver  
  2.     android:exported=“true”  
  3.     android:name=“.receiver.WenwenNotificationReceiver”>  
  4.     <intent-filter>  
  5.         <action android:name=“io.rong.push.intent.MESSAGE_ARRIVED” />  
  6.         <action android:name=“io.rong.push.intent.MI_MESSAGE_ARRIVED” />  
  7.         <action android:name=“io.rong.push.intent.MESSAGE_CLICKED” />  
  8.         <action android:name=“io.rong.push.intent.MI_MESSAGE_CLICKED” />  
  9.     </intent-filter>  
  10. </receiver>  
<receiver
    android:exported="true"
    android:name=".receiver.WenwenNotificationReceiver">
    <intent-filter>
        <action android:name="io.rong.push.intent.MESSAGE_ARRIVED" />
        <action android:name="io.rong.push.intent.MI_MESSAGE_ARRIVED" />
        <action android:name="io.rong.push.intent.MESSAGE_CLICKED" />
        <action android:name="io.rong.push.intent.MI_MESSAGE_CLICKED" />
    </intent-filter>
</receiver>


【10】消息数量的监听。在消息列表Fragment的底部tab,我们要显示收到未读消息的数量。这个需要的MainActivity中添加监听即可。
 
  
/
  1. **  
  2.  * 融云未读消息监听  
  3.  */  
  4. private void initUnreadCountListener() {  
  5.     final Conversation.ConversationType[] conversationTypes = {Conversation.ConversationType.PRIVATE, Conversation.ConversationType.DISCUSSION,  
  6.             Conversation.ConversationType.GROUP, Conversation.ConversationType.SYSTEM,  
  7.             Conversation.ConversationType.PUBLIC_SERVICE};  
  8.   
  9.     Handler handler = new Handler();  
  10.     handler.postDelayed(new Runnable() {  
  11.         @Override  
  12.         public void run() {  
  13.             RongIM.getInstance().setOnReceiveUnreadCountChangedListener(mCountListener, conversationTypes);  
  14.         }  
  15.     }, 500);  
  16. }  
  17. public RongIM.OnReceiveUnreadCountChangedListener mCountListener = new RongIM.OnReceiveUnreadCountChangedListener() {  
  18.     @Override  
  19.     public void onMessageIncreased(int count) {  
  20.        Log.e(“tag”,”count:” + count);  
  21.         if (count == 0) {  
  22.             tvTabBadge.setVisibility(View.GONE);  
  23.         } else if (count > 0 && count < 100) {  
  24.             tvTabBadge.setVisibility(View.VISIBLE);  
  25.             tvTabBadge.setText(count + ”“);  
  26.         } else {  
  27.             tvTabBadge.setVisibility(View.VISIBLE);  
  28.             tvTabBadge.setText(“99+”);  
  29.         }  
  30.     }  
  31. };  
**
 * 融云未读消息监听
 */
private void initUnreadCountListener() {
    final Conversation.ConversationType[] conversationTypes = {Conversation.ConversationType.PRIVATE, Conversation.ConversationType.DISCUSSION,
            Conversation.ConversationType.GROUP, Conversation.ConversationType.SYSTEM,
            Conversation.ConversationType.PUBLIC_SERVICE};

    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            RongIM.getInstance().setOnReceiveUnreadCountChangedListener(mCountListener, conversationTypes);
        }
    }, 500);
}
public RongIM.OnReceiveUnreadCountChangedListener mCountListener = new RongIM.OnReceiveUnreadCountChangedListener() {
    @Override
    public void onMessageIncreased(int count) {
       Log.e("tag","count:" + count);
        if (count == 0) {
            tvTabBadge.setVisibility(View.GONE);
        } else if (count > 0 && count < 100) {
            tvTabBadge.setVisibility(View.VISIBLE);
            tvTabBadge.setText(count + "");
        } else {
            tvTabBadge.setVisibility(View.VISIBLE);
            tvTabBadge.setText("99+");
        }
    }
};

onCreate()方法中调用即可。
【11】用户头像和昵称修改成用户自己的头像和昵称。两种方式。
第一种方式是内容提供者:
1-->在MainActivity的onCreate()方法中添加:
 
  
RongIM.setUserInfoProvider(this,true);
2—->实现该方法
 
  
/**
 * 融云实现内容提供者,将用户信息提交给融云,融云在使用自己的SDK时,会加载自己的头像。昵称
 * @param userId
 * @return
 */
  1. @Override  
  2. public UserInfo getUserInfo(String userId) {  
  3.     for (Friend  friend:friends){  
  4.         if(friend.getUserId().equals(userId)){  
  5.             return  new UserInfo(friend.getUserId(),friend.getUserName(),Uri.parse(friend.getAvator()));  
  6.         }  
  7.     }  
  8.     return null;  
  9. }  
@Override
public UserInfo getUserInfo(String userId) {
    for (Friend  friend:friends){
        if(friend.getUserId().equals(userId)){
            return  new UserInfo(friend.getUserId(),friend.getUserName(),Uri.parse(friend.getAvator()));
        }
    }
    return null;
}


就是用户根据自己的id去获取用户昵称及头像,封装为融云的UserInfo即可。
 
  
第二种方式是在用户连接融云服务器成功后,执行如下代码:
 
  
 
  
  1. if(RongIM.getInstance()!=null){  
  2.     //1、设置当前用户信息  
  3.     RongIM.getInstance().setCurrentUserInfo(new UserInfo(userid,friends.get(0).getUserName(),Uri.parse(friends.get(0).getAvator())));  
  4.     //2  
  5.     RongIM.getInstance().setMessageAttachedUserInfo(true);  
  6. }  
if(RongIM.getInstance()!=null){
    //1、设置当前用户信息
    RongIM.getInstance().setCurrentUserInfo(new UserInfo(userid,friends.get(0).getUserName(),Uri.parse(friends.get(0).getAvator())));
    //2
    RongIM.getInstance().setMessageAttachedUserInfo(true);
}

//当A.b互通消息时,各端接收到消息后,SDK会自动从消息中取出用户信息放入用户信息缓存中,并刷新UI显示
设置当前的用户信息,UserInfo是融云定义好的实体。
 
  
 
  
-------------------至此,融云的单聊模式功能全部结束------------------------


源代码及Apk下载

接下来一段时间,会搞环信SDK,环信坑比较多。。。。



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值