网易云通信安卓 Demo 结构说明

来源:互联网 发布:java 高斯扩散模型 编辑:程序博客网 时间:2024/06/06 03:40

网易云通信安卓 Demo 结构说明

网易云通信 Demo 工程基于网易云通信 SDK,演示了 SDK 聊天、群组、白板、实时音视频等功能接口的使用方法。Demo 工程依赖于 UIKit 工程,UIKit 实现了基本的消息收发,群组服务以及通讯录等功能,包含有完整的界面显示。开发者可以直接调用UIKit 中的接口,来进行功能开发,加快开发速度。用户可参照该 Demo,将网易云通信 SDK 接入自己的 APP。

工程导入指引

  • Eclipse导入Demo
  • Eclipse导入UIKit
  • Android Studio导入Demo
  • Android Studio导入UIKit
  • UIKit集成示例
  • Android视频教程源码及说明

下载编译 Demo

用户可在下载页面进行下载 Demo 源码工程。

总体环境需求:

  • 我们的部分代码会针对不同系统版本做优化,在新的平台上使用新的 API。编译运行该 Demo 的 build tools最低版本要求为19。
  • 由于 Demo 中使用到了部分 java7 以上的特性,因此 JDK 请使用 JDK7 或以上版本。

如果你使用的 IDE 是 Android Studio,可直接在 IDE 中打开 Demo 工程,然后将工程目录下 gradle.properties 文件按照注释修改,就可以直接编译运行。

  • 如果你是第一次使用 Android Studio,导入时会从 gradle 网站下载 gradle 发布包,在国内下载可能会比较慢。这时你也可以通过 Android Studio 设置,不使用 gradle wrapper,改为使用 local gradle distribution。

如果你使用的 IDE 是 Eclipse,可直接在 IDE 中打开工程,做如下修改后,即可编译运行。

  • 由于在 Eclipse 配置 libs 外的 jar 包比较麻烦,且不能配置 jni 库的位置,因此你需要把 libs-sdk 的所有文件夹移动到 libs 目录下(Demo 1.5版本及以后不需要)。
  • 注释掉 AboutActivity 中 initViewData() 函数的函数体。这行使用 Android Studio 编译期添加 BuildConfig 字段的特性,在 Eclipse 上无法完成,直接注释掉即可。
  • 如果你需要试验发送地理位置功能,请将 AndroidManifest 中的 {AMAP_KEY} 替换为 demo/build.gradle 文件中 buildTypes 内对应的值。在 Android Studio 上会在编译器自动替换。
  • 由于需要使用 JDK7 编译,旧版本的 ADT 插件仅支持到1.6,因此你需要使用最新的 ADT 版本(23.0及以上)才能正确编译 Demo。
  • 将 UIKit 下 AndoridManifeset 文件下所有 Activity 声明复制到 APP 工程的 AndoridManifeset 文件中。
  • 将 UIKit 下 assets 目录下所有资源复制到 APP工程的 assets 目录下。
    由于 google 已经将 Android Studio 作为官方开发工具,不再提供 ADT Bundle 下载,继续使用 Eclipse 开发会让你错过很多新特性,因此我们强烈建议你使用 Android Studio 开发基于网易云通信 SDK 的 APP,该 Demo的很多自动化特性也将只有 Android Studio 才能体验到。

源码结构

由于 Demo 依赖于 UIKit 进行开发。分为 Demo 工程和 UIKit 工程。分别介绍这两个工程的源码结构。
  • Demo日志地址:/{外卡根目录}/{app_package_name}/log/

Demo源码结构

  • Application 入口:NimApplication, 包含 SDK 的初始化,UIKit的初始化以及配置示例。
  • 登录相关:login 包,包含一个比较典型的从第三方 APP 授权,然后登录到网易云通信服务器的例子。
  • 主界面:main 包,包含最近联系人列表和好友/群组列表。该包作为各个功能的入口点,内含获取和管理最近联系人,获取群组列表,收发自定义通知等 SDK 接口使用示例。
  • 消息相关:session 包,包含消息历史,聊天信息和搜索消息界面。消息的具体展示和收发都在 UIKit 中。可以使用 NimUIKit 直接展示和使用。也可以使用 SessionHelper 进行消息界面的定制。
  • 群组相关:team 包,包含搜索群组和加入群组界面。包含创建高级群和讨论组的接口使用示例。
  • 音视频通话相关:avchat 包,包含音视频通话界面。内含网络通话功能的 SDK 接口使用示例,以及一个完整的网络通话流程示例,开发者可直接参考开发音视频通话功能。
  • 实时会话(白版):rts 包,包含白板教学的示例,支持实时音频、白板数据收发。

UIKit源码结构

  • UIKit 调用接口:NimUIKit,包含构建好友和群的缓存,打开聊天窗口,打开联系人选择器,打开群资料。
  • 基础组件相关:common 包, 包含 Activity、Fragment 和 Adapter的基类,一些自定义 UI 控件以及系统工具类。
  • 消息相关:session包,包含单聊/群聊界面,内含收发消息,上传下载消息附件,使用高清语音,管理消息历史等 SDK 接口使用示例。该包还有消息展示,使用 emoji 表情,发送图片等示例代码,可供开发者参考。
  • 群组相关:team 包,包含群组信息界面。内含展示群资料,修改群资料,展示群成员列表,管理群成员列表等 SDK 接口使用示例。
  • 最近联系人相关:recent 包,包含最近联系人列表界面
  • 通讯录相关:contact_selector 包和 contact 包,包含联系人选择器和通讯录。内含用户信息等 SDK 接口使用示例。

修改Demo为己用

网易云通信 Demo 实现了一个 IM 软件的所有基础功能,开发者可直接以 Demo 为基础开发自己的 IM 软件,也可以稍作修改,用于前期流程验证,也可以作为 SDK 开发的参考和指南。

  • 如果你已经在网易云通信官网上注册了 APP,你需要修改 AndroidManifest 中的 “com.netease.nim.appKey” 为你自己的 appkey,否则登录会失败。

聊天界面代码说明

结构说明

  • BaseMessageActivity:聊天界面基类。封装了 MessageFragment 和 actionbar 相关操作。
  • MessageFragment:聊天界面基类 Fragment。
  • MessageListPanel:消息收发模块。包括收发消息的显示,消息列表相关操作。
  • InputPanel:底部文本编辑,语音等模块。包括文本,语音的消息发送和更多中的 action 操作。
  • ActionsPanel:更多操作模块。
  • P2PMessageActivity:点对点聊天界面。
  • TeamMessageActivity: 群聊界面。
  • SessionCustomization:聊天界面定制化参数。
  • MessageLoader:MessageListPanel 中的内部类,用于加载消息。
  • MsgItemEventListener: MessageListPanel 中的内部类,用于重发消息,长按消息相关操作等。

音视频代码说明

结构说明

  • AVChatActivity::音视频界面
  • AVChatUI:音视频管理类, 音视频相关功能管理,内含音频管理,视频管理,视频绘制管理。还包括了网络连接状态等监听事件的处理,具体见开发手册。
  • AVChatAudio:音频管理, 音频界面初始化和界面相关操作管理
  • AVChatVideo:视频管理, 视频界面初始化和界面相关操作管理
  • AVChatSurface:视频绘制管理
  • AVChatNotification:音视频聊天通知栏
  • AVChatUIListener:音视频界面操作监听

初始化

AVChatActivity 的 oncreate 中,进行管理器的初始化工作

avChatUI = new AVChatUI(this, root, this);if (!avChatUI.initiation()) {    this.finish();    return;}public boolean initiation() {        AVChatProfile.getInstance().setAVChatting(true);        avChatAudio = new AVChatAudio(root.findViewById(R.id.avchat_audio_layout), this, this);        avChatVideo = new AVChatVideo(context, root.findViewById(R.id.avchat_video_layout), this, this);        avChatSurface = new AVChatSurface(context, this, root.findViewById(R.id.avchat_surface_layout));        return true;    }

拨打

主流程:

1、传入参数,对方帐号和拨打的类型(AVChatType.AUDIO 或 AVChatType.VIDEO)。

avChatUI.outGoingCalling(receiverId, AVChatType.typeOfValue(state));

2、通知界面刷新,详见界面刷新 一节。

if (callTypeEnum == AVChatType.AUDIO) {    onCallStateChange(CallStateEnum.OUTGOING_AUDIO_CALLING);} else {    onCallStateChange(CallStateEnum.OUTGOING_VIDEO_CALLING);}

3、发起通话

/*** 发起通话* account 对方帐号* callTypeEnum 通话类型:语音、视频* videoParam 发起视频通话时传入,发起音频通话传null* AVChatCallback 回调函数,返回AVChatInfo*/AVChatManager.getInstance().call(account, callTypeEnum, videoParam, new AVChatCallback<AVChatData>() {            @Override            public void onSuccess(AVChatData data) {               ...            }            @Override            public void onFailed(int code) {                ...            }            @Override            public void onException(Throwable exception) {                ...            }        });

接听

1、传入参数 AVChatData

avChatUI.inComingCalling(avChatData);

2、通知界面刷新,详见界面刷新 一节。

if (callTypeEnum == AVChatType.AUDIO) {    onCallStateChange(CallStateEnum.OUTGOING_AUDIO_CALLING);} else {    onCallStateChange(CallStateEnum.OUTGOING_VIDEO_CALLING);}

界面刷新

界面刷新,详细流程如下。
1、调用 onCallStateChange
2、如果界面没有进行过初始化,则进行界面初始化 findViews,并为各个按钮添加响应事件。
3、根据 CallStateEnum 判断界面布局设置和显隐性。

// 有来电,界面状态更新onCallStateChange(CallStateEnum.INCOMING_AUDIO_CALLING);// 判断来电类型,是音频或视频if(CallStateEnum.isAudioMode(state))    findViews();// 设置信息显示和界面布局switch (state){    ...    case INCOMING_AUDIO_CALLING://免费通话请求        setSwitchVideo(false);        showProfile();//对方的详细信息        showNotify(R.string.avchat_audio_call_request);        setMuteSpeakerHangupControl(false);        setRefuseReceive(true);        receiveTV.setText(R.string.avchat_pickup);        break;    ...}

按钮响应事件

AVChatAudio 和 AVChatVideo 中包含了挂断,拒绝,接受,禁音,开启扬声器,音视频切换和摄像头切换的操作。 按钮的点击响应事件,通过 AVChatUIListener 统一交给 AVChatUI 进行管理。示例如下:

// 初始化挂断按钮hangup = mute_speaker_hangup.findViewById(R.id.avchat_audio_hangup);hangup.setOnClickListener(this);// 按钮响应事件处理public void onClick(View v) {    switch (v.getId()) {        case R.id.avchat_audio_hangup:            listener.onHangUp();        break;    ...    }}// 在AVChatUI的AVChatUIListener实现中,实现挂断或取消接口。public void onHangUp() {    if (isCallEstablish.get()) {        hangUp(AVChatExitCode.HANGUP);    } else {        hangUp(AVChatExitCode.CANCEL);    }}

聊天室代码说明

结构说明

  • activity包:界面相关
  • adapter包:适配器相关
  • constant包:常量定义
  • fragment包:界面相关。其中包括 tab 包,定义了聊天室框架中的 tab fragment。
  • helper包:缓存管理,通知类消息字段管理等辅助功能。
  • module包:模块化相关。包括聊天室收发消息模块。
  • thirdparty包:第三方实现相关。包括网易云通信 demo 聊天室 http 客户端。
  • viewholder包:界面相关 viewholder 展示。
  • widget包:聊天室相关控件

重点类说明

  • ChatRoomsFragment:直播间列表 fragment。包含向网易云通信Demo应用服务器请求聊天室列表操作。
  • ChatRoomActivity:聊天室界面。封装了 ChatRoomFragment。包括聊天室的进入/离开的操作,监听聊天室在线状态和监听聊天室被踢出状态。
  • ChatRoomTabFragment:聊天室内 tab fragment 的基类。
  • ChatRoomFragment:聊天室 Activity 包含的顶层 Fragment。包括界面上方界面和下方的 viewpager。viewpager 包含3个 tab fragment,分别是:MessageTabFragment (直播互动 tab), MasterTabFragment (主播 tab), OnlinePeopleTabFragment (在线成员 tab)。
  • MessageTabFragment:直播互动基类 fragment。内嵌 ChatRoomMessageFragment (直播互动 fragment)。
  • MasterTabFragment:主播基类 fragment。内嵌 MasterFragment(主播 fragment )。
  • OnlinePeopleTabFragment:在线成员基类 fragment。内嵌 OnlinePeopleFragment (在线成员 fragment)
  • ChatRoomMessageFragment:聊天室直播互动 fragment。包括消息的收发相关操作。
  • MasterFragment: 聊天室主播 fragment。包括获取聊天室资料等操作。
  • OnlinePeopleFragment:聊天室在线人数 fragment。包括获取聊天室成员信息等操作。
  • ChatRoomMsgViewHolderFactory: 聊天室消息项展示ViewHolder工厂类。包括消息展示 viewholder 的注册操作。

新老版本兼容问题

群通知相关

问题:群通知新增的通知消息类型,可能会造成老版本崩溃。
原因:TeamNotificationHelper#buildUpdateTeamNotification 的 a.getUpdatedFields() 的 size 为0,造成 sb 的 length为0,会抛出 StringIndexOutOfBoundsException 错误。
解决方案:判断 sb 的length,参考demo。

Android 6.0 权限管理

网易云通信 demo 提供 Android 6.0 权限管理示例。相关方法的实现,在 uikit 的 permission 包中。

在需要相关权限的地方,发起申请并等待用户操作后的返回结果。具体实现方法:

private void requestBasicPermission() {    MPermission.with(MainActivity.this)                     .addRequestCode(BASIC_PERMISSION_REQUEST_CODE)        .permissions(            Manifest.permission.WRITE_EXTERNAL_STORAGE,            Manifest.permission.READ_EXTERNAL_STORAGE,            // ……        )        .request();}@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {    MPermission.onRequestPermissionsResult(this, requestCode, permissions, grantResults);}@OnMPermissionGranted(BASIC_PERMISSION_REQUEST_CODE)public void onBasicPermissionSuccess(){    Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();}@OnMPermissionDenied(BASIC_PERMISSION_REQUEST_CODE)public void onBasicPermissionFailed(){    Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();}