Android端实现多人音视频聊天应用(一),2024历年华为跳动Android面试真题解析

IRtcEngineEventHandler接口类用于SDK向应用程序发送回调事件通知,应用程序通过继承该接口类的方法获取 SDK 的事件通知。

接口类的所有方法都有缺省(空)实现,应用程序可以根据需要只继承关心的事件。在回调方法中,应用程序不应该做耗时或者调用可能会引起阻塞的 API(如 SendMessage),否则可能影响 SDK 的运行。

private RtcEngine mRtcEngine;

/**

  • Tutorial Step 1
  • 初始化Agora,创建 RtcEngine 对象
    */
    private void initializeAgoraEngine() {
    try {
    mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
    } catch (Exception e) {
    Log.e(LOG_TAG, Log.getStackTraceString(e));

throw new RuntimeException(“Agora初始化失败了,检查一下是哪儿出错了\n” + Log.getStackTraceString(e));
}
}

private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
@Override
public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//设置远端视频显示属性
setupRemoteVideo(uid);
}
});
}

@Override
public void onUserOffline(int uid, int reason) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//其他用户离开当前频道回调
onRemoteUserLeft();
}
});
}

@Override
public void onUserMuteVideo(final int uid, final boolean muted) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//其他用户已停发/已重发视频流回调
onRemoteUserVideoMuted(uid, muted);
}
});
}
};

private void onRemoteUserLeft() {
FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container);
container.removeAllViews();

//文案可随意定制
View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk);
tipMsg.setVisibility(View.VISIBLE);
}

private void onRemoteUserVideoMuted(int uid, boolean muted) {
FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container);

SurfaceView surfaceView = (SurfaceView) container.getChildAt(0);

Object tag = surfaceView.getTag();
if (tag != null && (Integer) tag == uid) {
surfaceView.setVisibility(muted ? View.GONE : View.VISIBLE);
}
}

打开视频模式

enableVideo()方法用于打开视频模式。可以在加入频道前或者通话中调用,在加入频道前调用,则自动开启视频模式,在通话中调用则由音频模式切换为视频模式。调用 disableVideo() 方法可关闭视频模式。

setVideoProfile()方法设置视频编码属性(Profile)。每个属性对应一套视频参数,如分辨率、帧率、码率等。 当设备的摄像头不支持指定的分辨率时,SDK 会自动选择一个合适的摄像头分辨率,但是编码分辨率仍然用 setVideoProfile() 指定的。

该方法仅设置编码器编出的码流属性,可能跟最终显示的属性不一致,例如编码码流分辨率为 640x480,码流的旋转属性为 90 度,则显示出来的分辨率为竖屏模式。

/**

  • Tutorial Step 2
  • 打开视频模式,并设置本地视频属性
    */
    private void setupVideoProfile() {
    //打开视频模式
    mRtcEngine.enableVideo();
    //设置本地视频属性
    mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_360P, false);
    }

设置本地视频显示属性

setupLocalVideo( VideoCanvas local )方法用于设置本地视频显示信息。应用程序通过调用此接口绑定本地视频流的显示视窗(view),并设置视频显示模式。 在应用程序开发中,通常在初始化后调用该方法进行本地视频设置,然后再加入频道。退出频道后,绑定仍然有效,如果需要解除绑定,可以调用 setupLocalVideo(null) 。

/**

  • Tutorial Step 3
  • 设置本地视频显示属性
    */
    private void setupLocalVideo() {
    FrameLayout container = (FrameLayout) findViewById(R.id.local_video_view_container);
    SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());
    surfaceView.setZOrderMediaOverlay(true);
    container.addView(surfaceView);
    mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_ADAPTIVE, 0));
    }

加入一个频道

joinChannel(String token,String channelName,String optionalInfo,int optionalUid )方法让用户加入通话频道,在同一个频道内的用户可以互相通话,多个用户加入同一个频道,可以群聊。 使用不同 App ID 的应用程序是不能互通的。如果已在通话中,用户必须调用 leaveChannel() 退出当前通话,才能进入下一个频道。

/**

  • Tutorial Step 4
  • 加入一个频道
    */
    private void joinChannel() {
    //如果不指定UID,Agroa将自动生成并分配一个UID
    mRtcEngine.joinChannel(null, “demoChannel1”, “Extra Optional Data”, 0);
    }

设置远端视频显示属性

setupRemoteVideo( VideoCanvas remote)方法用于绑定远程用户和显示视图,即设定 uid 指定的用户用哪个视图显示。调用该接口时需要指定远程视频的 uid,一般可以在进频道前提前设置好。

如果应用程序不能事先知道对方的 uid,可以在 APP 收到 onUserJoined 事件时设置。如果启用了视频录制功能,视频录制服务会做为一个哑客户端加入频道,因此其他客户端也会收到它的 onUserJoined 事件,APP 不应给它绑定视图(因为它不会发送视频流),如果 APP 不能识别哑客户端,可以在 onFirstRemoteVideoDecoded 事件时再绑定视图。解除某个用户的绑定视图可以把 view 设置为空。退出频道后,SDK 会把远程用户的绑定关系清除掉。

/**

  • Tutorial Step 5
  • 设置远端视频显示属性
    */
    private void setupRemoteVideo(int uid) {
    FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container);

if (container.getChildCount() >= 1) {
return;
}

SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());
container.addView(surfaceView);
mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_ADAPTIVE, uid));

surfaceView.setTag(uid);
//文案可随意定制
View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk);
tipMsg.setVisibility(View.GONE);
}

离开当前频道

leaveChannel()方法用于离开频道,即挂断或退出通话。

当调用 joinChannel() API 方法后,必须调用 leaveChannel() 结束通话,否则无法开始下一次通话。 不管当前是否在通话中,都可以调用 leaveChannel(),没有副作用。该方法会把会话相关的所有资源释放掉。该方法是异步操作,调用返回时并没有真正退出频道。在真正退出频道后,SDK 会触发 onLeaveChannel 回调。

/**

  • Tutorial Step 6
  • 离开当前频道
    */
    private void leaveChannel() {
    mRtcEngine.leaveChannel();
    }

public void onEncCallClicked(View view) {
finish();
}

@Override
protected void onDestroy() {
super.onDestroy();

leaveChannel();
RtcEngine.destroy();
mRtcEngine = null;
}

管理摄像头

switchCamera()方法用于在前置/后置摄像头间切换。除此以外Agora还提供了一下管理摄像头的方法:例如setCameraTorchOn(boolean isOn)设置是否打开闪光灯、setCameraAutoFocusFaceModeEnabled(boolean enabled)设置是否开启人脸对焦功能等等。

/**

  • Tutorial Step 7
  • 切换前置/后置摄像头
    */
    public void onSwitchCameraClicked(View view) {
    mRtcEngine.switchCamera();
    }

将自己静音

muteLocalAudioStream(boolean muted)方法用于静音/取消静音。该方法可以允许/禁止往网络发送本地音频流。但该方法并没有禁用麦克风,不影响录音状态。

/**

  • Tutorial Step 8
  • 将自己静音
    */
    public void onLocalAudioMuteClicked(View view) {
    ImageView iv = (ImageView) view;
    if (iv.isSelected()) {
    iv.setSelected(false);
    iv.clearColorFilter();
    } else {
    iv.setSelected(true);
    iv.setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
    }

mRtcEngine.muteLocalAudioStream(iv.isSelected());
}

暂停本地视频流

muteLocalVideoStream(boolean muted)方法用于暂停发送本地视频流,但该方法并没有禁用摄像头,不影响本地视频流获取。

/**

  • Tutorial Step 9
  • 暂停本地视频流
    */
    public void onLocalVideoMuteClicked(View view) {
    ImageView iv = (ImageView) view;
    if (iv.isSelected()) {
    iv.setSelected(false);
    iv.clearColorFilter();
    } else {
    iv.setSelected(true);
    iv.setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
    }
    mRtcEngine.muteLocalVideoStream(iv.isSelected());
    FrameLayout container = (FrameLayout) findViewById(R.id.local_video_view_container);
    SurfaceView surfaceView = (SurfaceView) container.getChildAt(0);
    surfaceView.setZOrderMediaOverlay(!iv.isSelected());
    surfaceView.setVisibility(iv.isSelected() ? View.GONE : View.VISIBLE);
    }
    运行效果
    拿两部手机安装编译好的App,如果能看见两个自己,说明你成功了。
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

尾声

你不踏出去一步,永远不知道自己潜力有多大,千万别被这个社会套在我们身上的枷锁给捆住了,30岁我不怕,35岁我一样不怕,去做自己想做的事,为自己拼一把吧!不试试怎么知道你不行呢?

改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

不行呢?

改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-uZ0RRh7G-1712077845829)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值