需求
即时音视频通话
解决方案
环信,官方地址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中需要的资源即可,不必要全部引入。避免包过大。
如果你不想抽取,可以直接下载我抽取好的。
增加权限
<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
<!-- 设置环信应用的appkey --><meta-data android:name="EASEMOB_APPKEY" android:value="lizhangqu#test" />
key的值在环信后台新建应用后可获得
声明所需组件
<!-- 声明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); }}
并在清单文件中完成注册
<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
<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
<?phpinclude_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
版权声明:本文为博主原创文章,未经博主允许不得转载。
- 3楼m751003133天前 21:18
- 然而并没什么卵用
- 2楼wtjandjay5天前 15:21
- 好文
- 1楼kui20155天前 13:48
- 没用,环信的即时语音只能看看,经常会有双方不能接通的情况,问了他们的客服,客服表示有时是这样的。。。