以前看一个Android项目先看
AndroidManifest.xml
,我更喜欢先把文件夹内的结构树打印出来
打印文件夹内的结构树可详见:
杂篇-从整理文件发起的杂谈[-File-]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2.查看最项目的settings.gradle
和build.gradle
(最外层)
如果你想导入AS中查看,可以看一下com.android.tools.build:gradle
的版本修改一下
include ':app'
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 {
main {
jniLibs.srcDirs = ['../../../libs']
dependencies {
implementation fileTree(dir: '../../../libs', include: ['*.jar'])
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"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
三、创建IChat项目
1、配置项目
项目的配置如图,将依赖包以及.so文件放在对应位置
为了方便些,将res文件夹的资源拷贝一下
2、配置APP ID
3.视频通话Activity的分析
一共也就200多行,还包括一大坨权限申请的代码,这里权限申请的代码单独拎出来,就当复习一下。
3.1:权限申请(非要点,可忽略)
---->[成员变量]----------------------------------
private static final int PERMISSION_REQ_ID = 22;
private static final String[] REQUESTED_PERMISSIONS = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
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();
setupVideoProfile();
setupLocalVideo();
joinChannel();
* 初始化Agora引擎
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("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);
SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext());
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);
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);
View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk);
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);
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());
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());
* 切换摄像头
* @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();
[番外]:我与网络(个人感触,不想看,可略过,嘻嘻...
)
1.我与网络的初遇
记得第一次接触网络是在高二的时候(2011年),那时候手机还是键盘式的,貌似每月有100M流量
印象很深,当时用手机上网查了"GPRS流量"是什么意思? 看得我一头雾水, 一直没弄明白。
那时好像超过1M收一元钱,所以流量对我来说很宝贵。通过不断测试(用网+短信查询):
1.它和网络有关
2.只要上网,GPRS流量就会减少
3.文字消耗的流量很少,图片消耗的流量较多
4.下载多大的文件就会消耗多大的GPRS流量
5.进制1M=1024KB
2.时代的变迁
那时主要的沟通方式就是短信,一个月500条免费的短信都不够发
当时觉得用手机可以打电话,发短信是一件多么神奇和美好的事
也曾幻想过用手机视频通话,但感觉就像在痴人说梦,然而技术的发展往往令人惊叹。
高考之后(2012年)的暑假,在一个好友家里建了一个QQ号,起名"张风杰特烈"(后更名"张风捷特烈")
从此之后我的手机基本和打电话,发短信无缘了。可以说是打开了新世界的大门吧。
之后手机换了一部又一部,屏幕越来越大,流量也从100M变成了200M,之后 500M, 2G, 流量无限量
有了自己的笔记本电脑,自己捯饬连上了路由器,自此WIFI成了相依为命的伙伴,流量也不是我
一年前还是个连流量都不知道是什么的人,却能在短时间内融入这个网络时代,也许就是年轻人的优势吧
很快,QQ就支持视频通话了,那遥不可及的梦如梦般降临,而我就这么幸运的站在梦中
由于我的专业需要使用很多软件,所以电脑玩的还算比较6的。但看到很早就接触电脑的人用起来却非常生涩
我曾对一个人说过:我抓住了时代的尾巴,正一点点先前攀爬,而身处时代中的人已停止不前
3.我与游戏
说到网络就不得不说游戏,这也是网络的双刃之处,很多人沉迷其中无法自拔
可以说作为一个94年的小伙子,我接触网络算非常晚的,因此什么魔兽世界,星际争霸都与我无缘
英雄联盟,王者荣耀什么的,看到小人跑我头都晕,一点兴趣都没有
大二吧,比较喜欢玩QQ飞车,玩的挺6的。后来被几个班里的"后起之秀"虐了,也逐渐没什么兴趣了
从小我就喜欢《游戏王》,所以卡牌对我来说是很有诱惑力的,特别是集卡和策略
所以玩过不少策略卡牌类的手游,[圣火英雄传],[召唤师对决],[六界],[神之刃]等
我一般都是V3,一段时间后,基本都是V9以下无敌手,然后成区霸,最后游戏倒闭, 关门大吉。
经历几次后,感觉也看淡了,都是些数据而已,也没必要去较真,也就不玩游戏了
4.真正踏入网络时代
作为一名使用者,我可以贪婪的享受着网络中的一切便利,但我越来越感到,只是这样是不够的
网络对我来说仅是一个黑盒,它为我提供服务,我却对它一无所知,这让我感到困惑和恐惧
并不仅为此
,我决定踏上编程之路,想要更深入一点去看待这个时代,而不止于使用者
当我的网站连同之时,整个互联网中有了一个属于我节点。也许是我真正踏入网络时代大门的那一刻
从那时,世界网络中[二进制流]的输入与输出便成了我对网络时代的认知,眼中的一切似乎都有所不同
打开一个网址,浏览器和服务器通过Http协议用请求与响应传输数据,数据在流动中加工,反馈,展现
通过前端、后端、移动端的涉猎,基本明白了是怎么回事。这并非神迹,而是大概有三个要点:
接口(协议)、逻辑(代码)、数据(bit流)
复制代码
Agora SDK 使用体验征文大赛 | 掘金技术征文,征文活动正在进行中