美文网首页React-Native学习我爱编程
ReactNative集成网易云信IM Demo(Android

ReactNative集成网易云信IM Demo(Android

作者: 醉生夢死 | 来源:发表于2017-05-10 15:06 被阅读1058次

    版本

    本文已 ReactNative 集成 NIM_iOS_Demo_v3.6.0 为例。Android Studio版本为2.2.3
    本人是在已有的ReactNative(以下简称RN)工程下集成云信IM(其他各版本集成方式大同小异)

    1. 下载云信 IM demo 源码

    前往 网易云信 下载Android版 云信IM demo
    下载完成后解压缩,使用 Android Stuido导入项目,然后运行起来,确保下载下来是可以正常运行的。

    2. 拷贝 IM 源码到 RN 项目目录下

    1. 将下载下来的源码解压缩,拷贝源码中的nim_demo/uikit目录到RN工程的android目录下。
    2. 将 nim_demo/demo 目录下的源码和自己项目下的 android/app下合并。

    下载下来的 demo 和 rn 项目目录结构有点不一致,我这里已 rn 项目目录结构为主。
    按照 Demo 代码照猫画虎的把所有代码搬过去就行。
    此处省略一万字...

    3. RN 界面和原生界面跳转问题

    创建类 RN2NativeModule ,内容如下

    package com.yuexing.mymodule;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.widget.Toast;
    
    import com.alibaba.fastjson.JSONObject;
    import com.drew.lang.annotations.Nullable;
    import com.facebook.react.bridge.Arguments;
    import com.facebook.react.bridge.Promise;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import com.facebook.react.bridge.WritableMap;
    import com.facebook.react.modules.core.DeviceEventManagerModule;
    import com.netease.nim.uikit.cache.DataCacheManager;
    import com.netease.nimlib.sdk.NIMClient;
    import com.netease.nimlib.sdk.Observer;
    import com.netease.nimlib.sdk.RequestCallback;
    import com.netease.nimlib.sdk.StatusBarNotificationConfig;
    import com.netease.nimlib.sdk.auth.AuthService;
    import com.netease.nimlib.sdk.auth.LoginInfo;
    import com.netease.nimlib.sdk.msg.MessageBuilder;
    import com.netease.nimlib.sdk.msg.MsgService;
    import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
    import com.netease.nimlib.sdk.msg.model.IMMessage;
    import com.yuexing.DemoCache;
    import com.yuexing.MainActivity;
    import com.yuexing.R;
    import com.yuexing.config.preference.Preferences;
    import com.yuexing.config.preference.UserPreferences;
    import com.yuexing.login.LogoutHelper;
    import com.yuexing.session.SessionHelper;
    
    import java.util.List;
    
    /**
     * Created by andy on 2017/5/9.
     */
    
    public class RN2NativeModule extends ReactContextBaseJavaModule {
    
        private static final String MODULE_NAME = "RN2Native";
    
        private static final String MAIN_ACTIVITY_CLASSNAME = "com.yuexing.main.activity.MainActivity";
    
        public RN2NativeModule(ReactApplicationContext reactContext) {
            super(reactContext);
    
            // 注册消息监听事件
            this.registerReceiveMessage(reactContext, true);
        }
    
        @Override
        public String getName() {
            return MODULE_NAME;
        }
    
        /**
         * 注册/注销 观察者事件
         * @param reactContext React 上下文对象
         * @param isRegister true 为注册,false 为注销
         */
        private void registerReceiveMessage(final ReactApplicationContext reactContext, boolean isRegister) {
            // 创建观察中
            Observer<List<RecentContact>> messageObserver = new Observer<List<RecentContact>>() {
                @Override
                public void onEvent(List<RecentContact> recentContactList) {
                    WritableMap params = Arguments.createMap();
                    int count = NIMClient.getService(MsgService.class).getTotalUnreadCount();
                    params.putInt("unreadCount", count);
                    sendEvent(reactContext, "receiveMessage", params);
                }
            };
            NIMClient.getService(MsgServiceObserve.class).observeRecentContact(messageObserver, isRegister);
        }
    
        /**
         * 发送事件给 js 端
         * @param reactContext
         * @param eventName
         * @param params
         */
        private void sendEvent(ReactApplicationContext reactContext, String eventName, @Nullable WritableMap params) {
            if (reactContext != null) {
                reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
            }
        }
    
        /**
         * IM 登录
         * @param account 登录账号
         * @param password 登录密码
         * @param promise 登录的回调函数
         */
        @ReactMethod
        public void login(final String account, final String password, final Promise promise) {
            LoginInfo loginInfo = new LoginInfo(account, password);
            RequestCallback<LoginInfo> callback = new RequestCallback<LoginInfo>() {
                @Override
                public void onSuccess(LoginInfo loginInfo) {
                    // 缓存账号
                    DemoCache.setAccount(account);
                    Preferences.saveUserAccount(account);
                    Preferences.saveUserToken(password);
    
                    // 初始化消息提醒配置
                    initNotificationConfig();
    
                    // 初始化消息提醒
                    NIMClient.toggleNotification(UserPreferences.getNoticeContentToggle());
    
                    // 构建缓存
                    // DataCacheManager.buildDataCacheAsync();
    
                    JSONObject json = new JSONObject();
                    try {
                        int unreadCount = NIMClient.getService(MsgService.class).getTotalUnreadCount();
                        json.put("code", 200);
                        json.put("unreadCount", unreadCount);
                    } catch (Exception e) {
                        promise.reject(e);
                    }
                    promise.resolve(json.toJSONString());
                }
    
                @Override
                public void onFailed(int code) {
                    JSONObject json = new JSONObject();
                    // { code: 302 }
                    json.put("code", code);
                    if (code == 302 || code == 404) {
                        json.put("message", R.string.login_failed);
                    }
                    promise.resolve(json.toJSONString());
                }
    
                @Override
                public void onException(Throwable throwable) {
                    promise.reject(throwable);
                }
            };
            NIMClient.getService(AuthService.class).login(loginInfo).setCallback(callback);
        }
    
        /**
         * 初始化消息提醒
         */
        private void initNotificationConfig() {
            NIMClient.toggleNotification(UserPreferences.getNoticeContentToggle());
    
            // 加载状态栏配置
            StatusBarNotificationConfig statusBarNotificationConfig = UserPreferences.getStatusConfig();
    
            if (statusBarNotificationConfig == null) {
                statusBarNotificationConfig = DemoCache.getNotificationConfig();
                UserPreferences.setStatusConfig(statusBarNotificationConfig);
            }
    
            // 更新配置
            NIMClient.updateStatusBarNotificationConfig(statusBarNotificationConfig);
        }
    
        /**
         * IM 登出
         */
        @ReactMethod
        public void logout() {
            System.out.println("java后台    IM 注销");
            Preferences.saveUserToken("");
            NIMClient.getService(AuthService.class).logout();
            // 清理缓存&注销监听
            LogoutHelper.logout();
        }
    
        /**
         * 跳转到IM页
         */
        @ReactMethod
        public void toYunXinIM() {
            try {
                Activity currentActivity = getCurrentActivity();
    
                if (null != currentActivity) {
                    currentActivity.startActivity(new Intent(currentActivity, Class.forName(MAIN_ACTIVITY_CLASSNAME)));
                }
            } catch (Exception e) {
                Toast.makeText(new MainActivity(), "跳转失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    
        /**
         * 咨询客服
         * @param userId 用户id
         * @param textMsg 提醒内容
         */
        @ReactMethod
        public void chatWithCS(String userId, String textMsg) {
            IMMessage message = MessageBuilder.createTextMessage(userId, SessionTypeEnum.P2P, textMsg);
            // 第二个参数表示发送失败后重发,false为不重发
            NIMClient.getService(MsgService.class).sendMessage(message, true);
        }
    
    
        /**
         * 发送tip给指定用户
         * @param userId 用户id
         * @param textMsg 提醒内容
         */
        @ReactMethod
        public void p2pTipMsg(String userId, String textMsg) {
            IMMessage message = MessageBuilder.createTipMessage(userId, SessionTypeEnum.P2P);
            message.setContent(textMsg);
            // 第二个参数表示发送失败后重发,false为不重发
            NIMClient.getService(MsgService.class).sendMessage(message, false);
        }
    
    
        /**
         * 发起p2p聊天窗
         * @param userId 对方id
         */
        @ReactMethod
        public void toP2PChat(String userId) {
            try {
                SessionHelper.startP2PSession(getCurrentActivity(), userId);
            } catch (Exception e) {
                System.out.println("发起p2p聊天失败: " + e.getMessage());
                e.printStackTrace();
            }
        }
    
        /**
         * 跳转到指定页
         * @param activityClassName
         */
        @ReactMethod
        public void toActivity(String activityClassName) {
            try {
                Activity currentActivity = getCurrentActivity();
    
                if (null != currentActivity) {
                    Class clazz = Class.forName(activityClassName);
                    Intent intent = new Intent(currentActivity, clazz);
                    currentActivity.startActivity(intent);
                }
            } catch (Exception e) {
                Toast.makeText(new MainActivity(), "跳转失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    
    }
    
    
    

    创建对应的 package 类 RN2NativePackage, 内容如下

    package com.yuexing.mymodule;
    
    import com.facebook.react.ReactPackage;
    import com.facebook.react.bridge.JavaScriptModule;
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.uimanager.ViewManager;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * Created by andy on 2017/5/9.
     */
    
    public class RN2NativePackage implements ReactPackage {
    
        @Override
        public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
            return Arrays.<NativeModule>asList(new RN2NativeModule(reactContext));
        }
    
        @Override
        public List<Class<? extends JavaScriptModule>> createJSModules() {
            return Collections.emptyList();
        }
    
        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Collections.emptyList();
        }
    }
    
    

    MainApplication 中的 getPackages 添加如下代码

    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            // 添加如下代码
            new RN2NativePackage()
        );
    }
    

    当 RN 界面 调用 NativeModules.RN2Native.toYunXinIM 跳转到原生im界面时,如下所示。

    无法返回到原来的 rn 页面

    此时如果想回到跳转前的页面,我们发现没有返回按钮。因此接下来要做的就是添加返回按钮,返回到原来的 rn 页面。

    1. 修改 app/src/main/res/layout/main.xml,先复制<android.support.design.widget.AppBarLayout> 节点(将要粘贴到另一个文件里),然后注释或删除,此步操作完后,xml配置如下
    
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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:background="@drawable/skin_global_bg">
    <!--
        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay"
            app:elevation="0dp">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:titleTextAppearance="@style/Toolbar.TitleText"/>
        </android.support.design.widget.AppBarLayout>
        -->
    
        <com.yuexing.common.ui.viewpager.PagerSlidingTabStrip
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="@dimen/pager_sliding_tab_strip_height"
            android:layout_below="@id/app_bar_layout"
            android:background="@drawable/skin_global_bg"/>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/main_tab_pager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/tabs"/>
    
        <com.netease.nim.uikit.common.ui.drop.DropCover
            android:id="@+id/unread_cover"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible"
            />
    
    </RelativeLayout>
    
    
    1. 打开文件 app/src/main/res/layout/activity_main_tab.xml,你会发现这个配置文件里除了 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:id="@+id/welcome_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        
        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay"
            app:elevation="0dp">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:titleTextAppearance="@style/Toolbar.TitleText" />
        </android.support.design.widget.AppBarLayout>
        
    </LinearLayout>
    
    1. 打开文件 app/src/main/java/com/yuexing/main/activity/MainActivity.java ,在 onCreate 函数中添加三行代码,另外注释掉 onBackPressed 函数。完成后如下代码所示。
        //...
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main_tab);
    
            // 添加如下三行代码
            ToolBarOptions toolBarOptions = new ToolBarOptions();
            toolBarOptions.titleId = R.string.app_name;
            setToolBar(R.id.toolbar, toolBarOptions);
    
            requestBasicPermission();
    
            onParseIntent();
    
            //...
        }
    
        // ... 
    
    //    @Override
    //    public void onBackPressed() {
    //        if (mainFragment != null) {
    //            if (mainFragment.onBackPressed()) {
    //                return;
    //            } else {
    //                moveTaskToBack(true);
    //            }
    //        } else {
    //            super.onBackPressed();
    //        }
    //    }
    

    完成上述操作后,再次在使用 Android Studio 运行,然后跳转到云信节目,跳转后如下图,多了个返回按钮,点击返回则返回到 原来的 RN 页面。

    完后上述操作后,返回到 RN 界面的按钮就出来了

    4. 去除无用功能和替换logo图片操作

    • 替换 app/src/main/res/drawable-hdpi 目录下的 about.logo.pngactionbar_dark_logo_icon.pngactionbar_white_logo_icon.pngic_logo.pngic_multiport_detail.pngic_stat_notify_msg.pnglogo.png。删除 login_bg.pngwelcome_bg.pngroom_cover_*.png,删除图片后,代码或配置文件里有引用到图片的可以选择删除或注释相关代码块。

    • 替换 app/src/main/res/drawable-mdpi 目录下的 ic_logo.pngic_stat_notify_msg.png

    • 替换 app/src/main/res/drawable-xdpi 目录下的 about_logo.pngactionbar_dark_logo_icon.pngactionbar_white_logo_icon.pngic_logo.pngic_multiport_detail.pngic_stat_notify_msg.pnglogo.pngwelcome_bg.png

    • 替换 app/src/main/res/drawable-xxhdpi 目录下的 ic_logo.pngic_stat_notify_msg.png

    • 替换 uikit/res/drawable-hdpi 目录下的 nim_actionbar_dark_logo_icon

    • 替换 uikit/res/drawable-xhdpi 目录下的 nim_actionbar_dark_logo_icon

    • 注释掉 app/src/main/java/com/yuexing/main/activity/SettingsActivity.java 中的以下行

    // ...
    
    //        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    //            items.add(new SettingTemplate(TAG_NRTC_SETTINGS, getString(R.string.nrtc_settings)));
    //            items.add(SettingTemplate.addLine());
    //            items.add(new SettingTemplate(TAG_NRTC_NET_DETECT, "音视频通话网络探测"));
    //            items.add(SettingTemplate.makeSeperator());
    //        }
    
    // ...
    
    //items.add(SettingTemplate.addLine());
    //items.add(new SettingTemplate(TAG_JS_BRIDGE, getString(R.string.js_bridge_demonstration)));
    
    // ...
    
    • 注释掉 app/src/main/java/com/yuexing/main/activity/SettingsActivity.java 下的
    // ...
    //Toast.makeText(SettingsActivity.this, "收到multiport push config:" + aBoolean, Toast.LENGTH_SHORT).show();
    // ...
    
        private void initUI() {
            initItems();
            listView = (ListView) findViewById(R.id.settings_listview);
    //        View footer = LayoutInflater.from(this).inflate(R.layout.settings_logout_footer, null);
    //        listView.addFooterView(footer);
    
            initAdapter();
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    SettingTemplate item = items.get(position);
                    onListItemClick(item);
                }
            });
    //        View logoutBtn = footer.findViewById(R.id.settings_button_logout);
    //        logoutBtn.setOnClickListener(new View.OnClickListener() {
    //            @Override
    //            public void onClick(View v) {
    //                logout();
    //            }
    //        });
        }
    
    // ...
    
    • 修改 uikit/src/com/netease/nim/uikit/common/ui/drop/DropManager.java 中的 destroy ,修改如下
        public void destroy() {
            this.isTouchable = false;
            this.statusBarHeight = 0;
            // 判断 this.dropCover 是否为空
            if (this.dropCover != null) {
                this.dropCover.removeAllDropCompletedListeners();
                this.dropCover = null;
            }
            this.currentId = null;
            this.textPaint = null;
            this.textYOffset = 0;
            this.circlePaint = null;
            this.enable = false;
            LogUtil.i(TAG, "destroy DropManager");
        }
    

    相关文章

      网友评论

        本文标题:ReactNative集成网易云信IM Demo(Android

        本文链接:https://www.haomeiwen.com/subject/qhibtxtx.html