您的位置:首页 > 移动开发 > Android开发

Android 即时音视频解决方案1——环信

2015-09-29 10:39 507 查看

需求

即时音视频通话

解决方案

环信,官方地址http://www.easemob.com/

SDK下载

http://downloads.easemob.com/downloads/easemob-sdk-2.2.2.zip

SDK集成

解压下载的文件,将libs下的easemobchat_2.2.2.jar拷到Android Studio项目中的libs中,并在main目录下新建jniLibs目录,将so文件拷到其中。如图



代码抽取

我们只需要即时音视频的功能,因此环信提供的Demo中有多余的代码,我们需要进行提取。

将Demo中需要的资源文件复制到项目中



values目录下带huanxin前缀的文件都是原文件内容的子集。

将下方的类从Demo中复制出来并进行修改,使其不报错



报错的都是在资源上,我们抽取Demo中需要的资源即可,不必要全部引入。避免包过大。

如果你不想抽取,可以直接下载我抽取好的。

增加权限

" data-snippet-id="ext.3c3cff1dbbf0a451802d5a34f230fffe" data-snippet-saved="false" data-csrftoken="0GvnS2MS-dardynUHip4t90lolUrB003bIfM" data-codota-status="done">[code]    <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />


设置key

" data-snippet-id="ext.7dabfee438877e6e05d45a63e5d86432" data-snippet-saved="false" data-codota-status="done">[code]<!-- 设置环信应用的appkey -->
<meta-data
android:name="EASEMOB_APPKEY"
android:value="lizhangqu#test" />


key的值在环信后台新建应用后可获得



声明所需组件

" data-snippet-id="ext.4a4dc2c666003bb1291cd18b0ced5226" data-snippet-saved="false" data-codota-status="done">[code]<!-- 声明sdk所需的service -->
<service android:name="com.easemob.chat.EMChatService"/>
<!-- 声明sdk所需的receiver -->
<receiver android:name="com.easemob.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<!-- 语音通话 -->
<activity
android:name=".activity.VoiceCallActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:theme="@style/nornal_style" >
</activity>
<!-- 视频通话 -->
<activity
android:name=".activity.VideoCallActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:theme="@style/horizontal_slide" >
</activity>


新建一个App类继承Application类,从Demo中的Application子类中复制部分需要的代码,主要就是注册一个BroadcastReceiver

public class App extends Application{
public static Context applicationContext;
private CallReceiver callReceiver;
@Override
public void onCreate() {
super.onCreate();
applicationContext = this;
EMChat.getInstance().init(this);
EMChat.getInstance().setDebugMode(true);
IntentFilter callFilter = new IntentFilter(EMChatManager.getInstance().getIncomingCallBroadcastAction());
if(callReceiver == null){
callReceiver = new CallReceiver();
}
//注册通话广播接收者
this.registerReceiver(callReceiver, callFilter);
}

}


并在清单文件中完成注册

...
" data-snippet-id="ext.e0cb896ec35c90e7d373c1e16c86d28a" data-snippet-saved="false" data-csrftoken="6LG6IiVh-vCcMjolrrvmAb64RTCXhjiX0HXU" data-codota-status="done">[code]  <application
android:name=".app.App"
>
...
</application>


后面会用到一段json,需要一个实体类,用于进行注册的逻辑操作

public class RegisterModel implements Serializable{
private int status;
private String message;

public int getStatus() {
return status;
}

public void setStatus(int status) {
this.status = status;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return "RegisterModel{" +
"status=" + status +
", message='" + message + '\'' +
'}';
}
}


需要访问网络以及json解析,添加gradle依赖库

compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.google.code.gson:gson:2.3.1'


然后编写入口Activity,即MainActivity,记得在清单文件中声明,并添加IntentFilter

" data-snippet-id="ext.1a8aa16b35aa2f1917ee3a775bff760d" data-snippet-saved="false" data-codota-status="done">[code]<activity android:name=".activity.MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>


环信音视频原理

环信有一套用户体系,当我们的客户端向我们自己的服务器进行账号注册时,在我们自己的服务器上成功注册后,我们还需要向环信的服务器进行账号注册,该接口是Restful Api,见文档http://docs.easemob.com/doku.php?id=start:100serverintegration:20users,因此我们还需要服务器的注册逻辑,这里简单实现一下,用的是PHP

accreditRegister($account);
echo $result;
}else{
$res['status']=404;
$res['message']="params is not right";
echo json_encode($res);
}

?>" data-snippet-id="ext.298312bc8cd09388f1be9e22c044b5f2" data-snippet-saved="false" data-codota-status="done">[code]<?php
include_once('Easemob.class.php');

$options['client_id']="YXA6bI0xUFa1EeWjzYFxAxSayQ";
$options['client_secret']="YXA6Rlyaq7MK9i5L0luXKC00EJowt74";
$options['org_name']="lizhangqu";
$options['app_name']="test";
$easemob=new Easemob($options);

if(isset($_POST['username']) && isset($_POST['password'])){
$account['username']=$_POST['username'] ;
$account['password']=$_POST['password'];
//这里处理自己服务器注册的流程
//自己服务器注册成功后向环信服务器注册
$result=$easemob->accreditRegister($account);
echo $result;
}else{
$res['status']=404;
$res['message']="params is not right";
echo json_encode($res);
}

?>


其中Easemob.class.php是环信提供的web端的示例中有的,不过可能需要修改一下,具体怎么修改看报错信息吧,如果你不想改也可以直接使用我的。

web的端的示例代码见https://github.com/easemob/emchat-server-examples

可以很清晰的看到我们除了在自己服务器上注册,还向环信服务器发起了请求进行注册。

客户端的注册逻辑也很简单,向我们的服务器发送账号密码即可,这里不考虑特殊情况,这里就需要用到我们之前建的Bean类了

private void register() {
String u=username.getText().toString();
String p=password.getText().toString();
if (TextUtils.isEmpty(u)||TextUtils.isEmpty(p)){
Toast.makeText(getApplicationContext(),"账号或密码不能为空!",Toast.LENGTH_LONG).show();
return ;
}

RequestBody requestBody= new FormEncodingBuilder()
.add("username",u)
.add("password",p)
.build();
String url="http://10.0.0.24/huanxin/index.php";
Request request=new Request.Builder().url(url).post(requestBody).build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
Log.e("TAG","Error,register failure.");
}
@Override
public void onResponse(Response response) throws IOException {
String result=response.body().string();
RegisterModel bean=gson.fromJson(result,RegisterModel.class);
Message message=Message.obtain();
message.obj=bean;
message.what=REGISTER;
mHandler.sendMessage(message);
}
});
}


值得注意的是,只有注册成功了,环信服务器才会发送响应码200,否则都是注册失败

private static final int REGISTER=0x01;
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case REGISTER:
RegisterModel bean= (RegisterModel) msg.obj;
if (bean.getStatus()==200){
Toast.makeText(getApplicationContext(),"注册成功!",Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getApplicationContext(),"注册失败!"+bean.getMessage(),Toast.LENGTH_LONG).show();
}
break;
default:
break;
}
super.handleMessage(msg);
}
};


注册成功后就可以进行登陆了。

private void login() {
String u=username.getText().toString();
String p=password.getText().toString();
if (TextUtils.isEmpty(u)||TextUtils.isEmpty(p)){
Toast.makeText(getApplicationContext(),"账号或密码不能为空!",Toast.LENGTH_LONG).show();
return ;
}
//这里先进行自己服务器的登录操作
//自己服务器登录成功后再执行环信服务器的登录操作
EMChatManager.getInstance().login(u, p, new EMCallBack() {//回调
@Override
public void onSuccess() {
runOnUiThread(new Runnable() {
public void run() {
EMGroupManager.getInstance().loadAllGroups();
EMChatManager.getInstance().loadAllConversations();
Toast.makeText(MainActivity.this, "登陆聊天服务器成功", Toast.LENGTH_SHORT).show();
Log.e("TAG", "登陆聊天服务器成功!");
}
});
}

@Override
public void onProgress(int progress, String status) {
Log.e("TAG", "登陆聊天服务器中 " + "progress:" + progress + " status:" + status);
}

@Override
public void onError(int code, String message) {
Log.e("TAG", "登陆聊天服务器失败!");
}
});
}


登陆操作也一样,首先在自己的服务器上进行登陆,然后使用环信的sdk在环信服务器上进行登陆。

同理的,登出操作也是一样,首先在自己服务器上进行退出操作,再在环信服务器上进行退出操作

private void logout() {
//这里先进行自己服务器的退出操作
//自己服务器登录成功后再执行环信服务器的退出操作

//此方法为异步方法
EMChatManager.getInstance().logout(new EMCallBack() {
@Override
public void onSuccess() {
Log.e("TAG", "退出聊天服务器成功!");
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "退出聊天服务器成功", Toast.LENGTH_SHORT).show();
Log.e("TAG", "退出聊天服务器成功!");
}
});
}

@Override
public void onProgress(int progress, String status) {
Log.e("TAG", "退出聊天服务器中 " + " progress:" + progress + " status:" + status);

}

@Override
public void onError(int code, String message) {
Log.e("TAG", "退出聊天服务器失败!" + " code:" + code + " message:" + message);

}
});
}


登陆完成就可以直接进行音视频通话了,但是通话总得有个发起方和接收方吧,发起方显然是我们自己,接收方就需要自己制定了,我们通过EditText输入获得。

private void voice() {
if (!EMChatManager.getInstance().isConnected())
Toast.makeText(this, "未连接到服务器", Toast.LENGTH_SHORT).show();
else{
String toUser=to.getText().toString();
if (TextUtils.isEmpty(toUser)){
Toast.makeText(MainActivity.this, "请填写接受方账号", Toast.LENGTH_SHORT).show();
return ;
}
Intent intent = new Intent(MainActivity.this, VoiceCallActivity.class);
intent.putExtra("username", toUser);
intent.putExtra("isComingCall", false);
startActivity(intent);
}

}

private void video() {
if (!EMChatManager.getInstance().isConnected()) {
Toast.makeText(MainActivity.this, "未连接到服务器", Toast.LENGTH_SHORT).show();
}
else {
String toUser=to.getText().toString();
if (TextUtils.isEmpty(toUser)){
Toast.makeText(MainActivity.this, "请填写接受方账号", Toast.LENGTH_SHORT).show();
return ;
}
Intent intent = new Intent(MainActivity.this, VideoCallActivity.class);
intent.putExtra("username", toUser);
intent.putExtra("isComingCall", false);
startActivity(intent);
}
}


音视频通话的关键都是直接调现成的Activity,需要添加username和isComingCall属性。

Intent intent = new Intent(MainActivity.this, VoiceCallActivity.class);
intent.putExtra("username", toUser);
intent.putExtra("isComingCall", false);
startActivity(intent);


而VoiceCallActivity和VideoCallActivity都是环信提供的Demo里的现成的Activity,基本上不用做任何修改即可使用。这样,基本上该做的工作都做了,可以试试跑不跑得通

要跑得通,你需要两台有摄像头的手机。然后效果就是这样的









静态图看不过瘾,那就看看动态图

登陆注册



音频



视频



源码下载

http://download.csdn.net/detail/sbsujjbcy/9139601

Github

https://github.com/lizhangqu/VoiceAndVideo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息