Agora-SDK-在Android中的使用(在线视频通话),字节跳动算法工程师面试总结

本文详细介绍了如何在Android项目中配置AgoraEngine,处理视频通话功能,包括权限申请、初始化Agora引擎、连接频道,以及展示了关键的RtcEngine事件处理器和UI交互处理。
摘要由CSDN通过智能技术生成

4.查看AndroidManifest.xml,得到入口Activity

可见示例的入口是VideoChatViewActivity,并看一下权限






|— 权限 ------------


三、创建IChat项目


1、配置项目

项目的配置如图,将依赖包以及.so文件放在对应位置

为了方便些,将res文件夹的资源拷贝一下


2、配置APP ID


3.视频通话Activity的分析

一共也就200多行,还包括一大坨权限申请的代码,这里权限申请的代码单独拎出来,就当复习一下。

3.1:权限申请(非要点,可忽略)

---->[成员变量]----------------------------------
private static final int PERMISSION_REQ_ID = 22;
//WRITE_EXTERNAL_STORAGE 权限只是为了保存日志到SD卡
private static final String[] REQUESTED_PERMISSIONS = {
Manifest.permission.RECORD_AUDIO,//录音权限
Manifest.permission.CAMERA,//相机权限
Manifest.permission.WRITE_EXTERNAL_STORAGE//SD卡写权限
};

if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) {
//执行到此处说明已有权限成功
initAgoraEngineAndJoinChannel();
}

/**

  • 检查权限的方法
  • @param permission 权限
  • @param requestCode 请求码
  • @return 是否拥有权限
    */
    public boolean checkSelfPermission(String permission, int requestCode) {
    Log.i(LOG_TAG, "checkSelfPermission " + permission + " " + requestCode);
    if (ContextCompat.checkSelfPermission(this, permission)
    != PackageManager.PERMISSION_GRANTED) {
    //发送权限请求
    ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
    return false;
    }
    return true;
    }

@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.i(LOG_TAG, "onRequestPermissionsResult " + grantResults[0] + " " + requestCode);
switch (requestCode) {
case PERMISSION_REQ_ID: {//请求码
if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED ||
grantResults[2] != PackageManager.PERMISSION_GRANTED) {
//三个权限有任意的未被允许,弹吐司,退出
showLongToast("Need permissions " +
Manifest.permission.RECORD_AUDIO + “/” +
Manifest.permission.CAMERA + “/” +
Manifest.permission.WRITE_EXTERNAL_STORAGE);
finish();
break;
}
//执行到此处说明用户已允许权限
initAgoraEngineAndJoinChannel();
break;
}
}
}


4.初始化Agora引擎和连接频道

/**

  • 初始化Agora引擎和连接频道
    */
    private void initAgoraEngineAndJoinChannel() {
    initializeAgoraEngine();//初始化Agora引擎
    setupVideoProfile();//设置视频信息
    setupLocalVideo();//设置本地的视频窗
    joinChannel();//连接频道
    }

/**

  • 初始化Agora引擎
    */
    private void initializeAgoraEngine() {
    try {
    mRtcEngine = RtcEngine.create(//实例化Rtc引擎
    getBaseContext(),//传入Context
    getString(R.string.agora_app_id), //传入APP ID
    mRtcEventHandler);//RTC事件处理器
    } catch (Exception e) {//发生异常时捕获异常
    Log.e(LOG_TAG, Log.getStackTraceString(e));
    throw new RuntimeException(“NEED TO check rtc sdk init fatal error\n” + Log.getStackTraceString(e));
    }
    }

/**

  • 设置视频信息
    */
    private void setupVideoProfile() {
    mRtcEngine.enableVideo();//启用视屏
    mRtcEngine.setVideoEncoderConfiguration(//视频解码配置
    new VideoEncoderConfiguration(//实例化对象
    VideoEncoderConfiguration.VD_120x120,//尺寸
    VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,//帧率
    VideoEncoderConfiguration.STANDARD_BITRATE,//比特率
    VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT));//旋转模式
    }
    |—关于VideoEncoderConfiguration对象---->[VideoEncoderConfiguration构造方法]------------------
    public VideoEncoderConfiguration(
    VideoEncoderConfiguration.VideoDimensions dimensions, //尺寸
    VideoEncoderConfiguration.FRAME_RATE frameRate,//帧率
    int bitrate, //比特率
    VideoEncoderConfiguration.ORIENTATION_MODE orientationMode)//旋转模式

/**

  • 设置本地视频窗
    */
    private void setupLocalVideo() {
    FrameLayout container = findViewById(R.id.local_video_view_container);//FrameLayout视图
    SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());//创建SurfaceView
    surfaceView.setZOrderMediaOverlay(true);
    container.addView(surfaceView);
    mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));
    }

/**

  • 连接到频道
    */
    private void joinChannel() {
    mRtcEngine.joinChannel(null, “demoChannel1”, “Extra Optional Data”, 0);
    // 如果你不指定 uid(第四参), 我们会为你生成一个 uid
    }

5.RTC事件处理器:IRtcEngineEventHandler

IRtcEngineEventHandler是一个抽象类,定义了非常多的抽象方法还有一些静态内部类

private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
@Override//已完成远端视频首帧解码回调。
public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
runOnUiThread(() -> setupRemoteVideo(uid));
}
@Override//远端用户(通信模式)/主播(直播模式)离开当前频道回调。
public void onUserOffline(int uid, int reason) {
runOnUiThread(() -> onRemoteUserLeft());
}
@Override//远端用户静音回调。
public void onUserMuteVideo(final int uid, final boolean muted) {
runOnUiThread(() -> onRemoteUserVideoMuted(uid, muted));
}
};

/**

  • 根据uid设置远端视频
  • @param uid 唯一标识符
    */
    private void setupRemoteVideo(int uid) {
    FrameLayout container = 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_FIT, u
    surfaceView.setTag(uid); // 用uid为surfaceView打标签
    View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk); // 隐藏文字UI
    tipMsg.setVisibility(View.GONE);
    }

/**

  • 远端用户挂断
    */
    private void onRemoteUserLeft() {
    FrameLayout container = findViewById(R.id.remote_video_view_container);
    container.removeAllViews();
    View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk); // 显示文字UI
    tipMsg.setVisibility(View.VISIBLE);
    }

/**

  • 远端用户静音
  • @param uid 标识符
  • @param muted 是否静音
    */
    private void onRemoteUserVideoMuted(int uid, boolean muted) {
    FrameLayout container = 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);
    }
    }

6.几个点击事件

/**

  • 是否屏蔽视频
  • @param view
    */
    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());//核心的一句API,
    FrameLayout container = 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);
    }

/**

  • 是否静音
  • @param view
    */
    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());//核心的一句API,
    }

/**

  • 切换摄像头
  • @param view
    */
    public void onSwitchCameraClicked(View view) {
    mRtcEngine.switchCamera();
    }

/**

  • 关闭
  • @param view
    */
    public void onEncCallClicked(View view) {
    finish();
    }

@Override
protected void onDestroy() {
super.onDestroy();
leaveChannel();//离开频道
RtcEngine.destroy();//引擎销毁
mRtcEngine = null;//引擎置空
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

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

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

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

文末

不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊

小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。

其实看到身边很多朋友抱怨自己的工资很低,包括笔者也是一样的,其原因是在面试过程中没有给面试官一个很好的答案。所以笔者会持续更新面试过程中遇到的问题,也希望大家和笔者一起进步,一起学习。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

面试官怎么样一个知识点里往死里凿,你也能应付如流啊**

小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。

[外链图片转存中…(img-Zv1NtNQu-1712330743644)]

其实看到身边很多朋友抱怨自己的工资很低,包括笔者也是一样的,其原因是在面试过程中没有给面试官一个很好的答案。所以笔者会持续更新面试过程中遇到的问题,也希望大家和笔者一起进步,一起学习。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值