作者:张风捷特烈
首先声明本文是Agora SDK入门的小白文章
![](https://img.haomeiwen.com/i15233854/84bde247d22ef68b.png)
一.集成
1.注册账号创建项目
其中最重要的要数 App ID 了
![](https://img.haomeiwen.com/i15233854/d879d3f1d264357f.png)
2.下载Agora SDK
![](https://img.haomeiwen.com/i15233854/025d2c75d29002ae.png)
二、学会看示例代码(可跳过)
1.整体了解项目结构(1v1的视频通信示例)
以前看一个Android项目先看
AndroidManifest.xml
,我更喜欢先把文件夹内的结构树打印出来
打印文件夹内的结构树可详见:杂篇-从整理文件发起的杂谈[-File-]
|---app
|---.gitignore
|---build.gradle
|---libs
|---PLACEHOLDER
|---proguard-rules.pro
|---src
|---main
|---AndroidManifest.xml
|---java
|---io
|---agora
|---tutorials1v1vcall
|---VideoChatViewActivity.java
|---jniLibs
|---arm64-v8a
|---PLACEHOLDER
|---armeabi-v7a
|---PLACEHOLDER
|---x86
|---PLACEHOLDER
|---res
|---drawable-xxxhdpi
|---btn_end_call.png
|---btn_mute.png
|---btn_switch_camera.png
|---btn_video.png
|---btn_voice.png
|---ic_launcher.png
|---layout
|---activity_video_chat_view.xml
|---values
|---colors.xml
|---dimens.xml
|---strings.xml
|---styles.xml
|---build.gradle
|---gradle
|---wrapper
|---gradle-wrapper.jar
|---gradle-wrapper.properties
|---gradle.properties
|---gradlew
|---gradlew.bat
|---images
|---ActivityViewChat.png
|---LICENSE.md
|---README.md
|---README.zh.md
|---settings.gradle
2.查看最项目的settings.gradle
和build.gradle
(最外层)
如果你想导入AS中查看,可以看一下
com.android.tools.build:gradle
的版本修改一下
---->[settings.gradle]----------------看一下项目包含的模块------------
include ':app'
---->[build.gradle]----------------看一下项目的一些信息------------
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
3.查看模块下的build.gradle
---->[app/build.gradle]----------------看一下项目的具体信息------------
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "io.agora.tutorials1v1vcall"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {//这里是jniLibs的目录
main {
jniLibs.srcDirs = ['../../../libs']
}
}
}
dependencies {//这里是依赖
implementation fileTree(dir: '../../../libs', include: ['*.jar']) // DO NOT CHANGE, CI may needs it when packaging
implementation 'com.android.support:appcompat-v7:26.1.0'
}
4.查看AndroidManifest.xml
,得到入口Activity
可见示例的入口是
VideoChatViewActivity
,并看一下权限
<activity
android:name=".VideoChatViewActivity"
android:screenOrientation="sensorPortrait"
android:theme="@style/FullScreenVideoTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
|--- 权限 ------------
<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--录音权限-->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<!--更改录音设置-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<!--网络状态权限-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!--相机权限-->
<uses-permission android:name="android.permission.CAMERA"/>
<!--蓝牙权限-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--SD卡写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
三、创建IChat项目
![](https://img.haomeiwen.com/i15233854/eebc0b82f882d490.png)
![](https://img.haomeiwen.com/i15233854/c3460a36f11fa71a.png)
1、配置项目
项目的配置如图,将依赖包以及.so文件放在对应位置
![](https://img.haomeiwen.com/i15233854/ddbc57f360f0d23c.png)
为了方便些,将res文件夹的资源拷贝一下
2、配置APP ID
![](https://img.haomeiwen.com/i15233854/df25346baef00234.png)
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;//引擎置空
}
/**
* 离开频道
*/
private void leaveChannel() {
mRtcEngine.leaveChannel();
}
最后
小编整理了一些关于这方面的深入讲解的视频,如需要的话可以加群免费领取。
本人Java开发4年Android开发5年,定期分享Android高级技术及经验分享,欢迎大家关注~(分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!)
最后文末放上一个技术交流群:Android IOC架构设计
群内有许多技术大牛,有任何问题,欢迎广大网友一起来交流,群内还不定期免费分享高阶Android学习视频资料和面试资料包~
网友评论