您的当前位置:首页ReactNative集成网易云信IM Demo(Android

ReactNative集成网易云信IM Demo(Android

2024-12-14 来源:哗拓教育

版本

1. 下载云信 IM demo 源码

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 
import 
import 
import 
import 
import 
import 
import 
import 
import 
import 
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 
                
                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>
    -->

    
        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"/>

    
        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 
    
    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");
    }
显示全文