有时候我们需要在应用中接入即时通讯等功能,可是又苦于apk过大、65535方法数等相关问题,这时候最好的方式就是用插件化的方式接入,下面是我用一个插件化框架接入环信的过程,跟大家分享一下。
这里我使用的是Apkplug这个插件化框架,这是一款我目前通过对比找到的最适合的插件化工具,在使用方便、运行稳定等方面均有良好表现,还提供后台托管,这一点也是极大的方便了开发者。
这里是这个框架的官方文档可以参考
环信插件开发
一、插件开发
首先我们对这个插件的需求是:能在一个有用户系统的宿主app里提供聊天功能。
进一步细分为如下具体功能:
1 能跟宿主一起登陆
2 能跟宿主用户系统有统一映射
3 能提供聊天界面
4 能提供好友界面
5 能提供最近会话界面
具体实现:
1 首先要按照环信的文档,先进行初始化和相关配置
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
EMOptions options = new EMOptions();
EaseUI.getInstance().init(this, options);
//xugai
EMClient.getInstance().setDebugMode(false);
}
}
插件在宿主中启动时会自动执行application中的初始化代码。配置就不在这里贴了,可以参考环信文档或下面的demo代码。
2 跟宿主一起登陆,是为了能够跟宿主融合的更加融洽,如果点击按钮弹出一个登陆界面,则像是两个app之间的互相调用。所以在宿主登陆的时候,同时要登陆插件,并对用户透明。因此插件需要提供一个登陆接口。我用Dispatcher的方式声明接口:
继承一个com.apkplug.easemobplug.Processores类,实现Receive方法,在方法中调用环信登陆,宿主使用这个接口时可以把用户名、密码传过来。参数的传递是靠人为约定的,宿主开发者和插件开发者按照约定取值。DispatchAgent类是管插件和宿主理通信的类,在登陆完成后,我调用dispatchAgent.reply回调宿主,同样,给宿主发送结果的参数格式也是要人为约定好。
public class EaseLogin extends Processor {
BundleContext context;
DispatchAgent dispatchAgent;
public EaseLogin(BundleContext context) {
super(context);
this.context = context;
dispatchAgent = new DispatchAgent(context);
}
@Override
public void Receive(URI uri, HashMap<String, Object> hashMap) {
String userName = (String) hashMap.get("UserName");
String passWord = (String) hashMap.get("Password");
if(userName == null || passWord == null){
dispatchAgent.reply(getMsgId(),false,new Exception("your username or password is null"));
return;
}
EMClient.getInstance().login(userName, passWord, new EMCallBack() {
@Override
public void onSuccess() {
dispatchAgent.reply(getMsgId(),true,"success");
}
@Override
public void onError(int i, String s) {
dispatchAgent.reply(getMsgId(),false,s);
}
@Override
public void onProgress(int i, String s) {
}
});
}
}
这样一个登陆接口就做好了。
3 跟宿主应用统一用户系统,环信插件里的用户和聊天的好友一定是宿主应用用户系统中的用户及好友,所以哪些人是用户,那些人跟哪些人是好友,需要宿主告诉环信插件。
首先需要提供注册接口,宿主用户注册时,同步注册环信插件,映射关系随便定义:
public class EaseCreateAccount extends BaseProcessor {
public EaseCreateAccount(BundleContext context) {
super(context);
}
@Override
public void Receive(URI uri, HashMap<String, Object> hashMap) {
String userName = (String) hashMap.get("UserName");
String password = (String) hashMap.get("Password");
if(userName == null || password == null){
dispatchAgent.reply(getMsgId(),false,new Exception("username or password is null"));
return;
}
try {
EMClient.getInstance().createAccount(userName,password);
dispatchAgent.reply(getMsgId(),true,"success");
} catch (HyphenateException e) {
dispatchAgent.reply(getMsgId(),false,e);
}
}
}
好友的同步有两种实现方式:
1 直接由宿主调用插件接口,设置插件的好友列表
2 宿主只管好友添加,插件自己去服务器拿好友列表
貌似方法2要好很多,但是需要服务端的对接,只有服务端可以直接添加好友,客户端添加好友的接口,最多只能等对方登陆时候才添加成功。
demo里这两种我都实现了,跟上面登陆接口一样,我实现了一个直接给好友列表界面添加好友的接口,只供参考,我并没有调用。我用的第二种方式,当宿主的用户系统添加好友时,同时给环信添加好友,因此我对外提供了添加好友的接口。
public class EaseAddFriend extends BaseProcessor {
public EaseAddFriend(BundleContext context) {
super(context);
}
@Override
public void Receive(URI uri, HashMap<String, Object> hashMap) {
String username = (String) hashMap.get("UserName");
try {
EMClient.getInstance().contactManager().addContact(username, "you have to accept");
dispatchAgent.reply(getMsgId(),true,"success");
} catch (HyphenateException e) {
dispatchAgent.reply(getMsgId(),false,e);
}
}
}
一系列你希望宿主用到的接口实现好后,就可以对外注册了,plugin.xml中添加:
<processor
uri="http://apkplug.plug.com/meseplug/login"
className="com.apkplug.easemobplug.Processores.EaseLogin"
/>
<processor
uri="http://apkplug.plug.com/meseplug/init"
className="com.apkplug.easemobplug.Processores.EaseInit"
/>
<processor
uri="http://apkplug.plug.com/meseplug/regist"
className="com.apkplug.easemobplug.Processores.EaseCreateAccount"
/>
<processor
uri="http://apkplug.plug.com/meseplug/contect"
className="com.apkplug.easemobplug.Processores.EaseContectsProcessor"
/>
<processor
uri="http://apkplug.plug.com/meseplug/addfriend"
className="com.apkplug.easemobplug.Processores.EaseAddFriend"
/>
<processor
uri="http://apkplug.plug.com/meseplug/deletefriend"
className="com.apkplug.easemobplug.Processores.EaseDeleteFriend"
/>
4 提供聊天界面、好友界面、会话界面,环信提供一些直接可用的界面,稍加改动就可以使用,宿主使用时,只需要用Intent启动即可,需要注意的是,一些manifest文件activity标签中配置的值,并不能同步到宿主,如果需要那些值,只能在宿主配置插件的activity,比如主界面,android:windowSoftInputMode="adjustPan"
这个值不配置的话,输入法弹出会压缩界面控件,只在插件里配置是不起作用的。
<activity android:name=".ui.MainActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:windowSoftInputMode="adjustPan"
/>
提供了这些功能后,插件就开发完成了。
二、宿主开发
1 安装插件,为了方便,我这里直接用了本地安装
PlugManager.getInstance().installAssets("app-debug.apk", "1.0.0", new OnInstallListener() {
@Override
public void onDownloadProgress(String url, String filePath, long bytesWritten, long totalBytes, PlugInfo plugInfo) {
}
@Override
public void onInstallSuccess(final org.osgi.framework.Bundle bundle, PlugInfo plugInfo) {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(textView.getText()+"\n插件安装成功");
}
});
startChat();
}
@Override
public void onInstallFailuer(int i, PlugInfo plugInfo, String errorMsg) {
}
@Override
public void onDownloadFailure(String errorMsg) {
}
});
}
2 登陆插件,我已经注册过了用户,这里不再调用,调用方式相同。用DispatchAgent对象调用call方法,
final DispatchAgent dispatchAgent=new DispatchAgent(PlugManager.getInstance().getBundleContext());
HashMap<String,Object> params2 = new HashMap<String, Object>();
params2.put("UserName","apkplug");
params2.put("Password","lbh131206");
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(textView.getText()+"\n开始登陆环信");
}
});
dispatchAgent.call("http://apkplug.plug.com/meseplug/login", params2, new WorkerCallback() {
@Override
public void reply(URI uri, Object... objects) throws Exception {
if(!(Boolean) objects[0]){
return;
}
textView.setText(textView.getText()+"\n环信登陆成功");
chatInitandLogin(dispatchAgent);
}
@Override
public void timeout(URI uri) throws Exception {
}
@Override
public void Exception(URI uri, Throwable throwable) {
System.err.println(uri);
}
});
登陆后就随时可以进行界面跳转了
Intent intent = new Intent();
intent.setClassName(MainActivity.this, className);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent)
demo地址:
宿主:
https://github.com/apkplug/SDKDemo/tree/master/EasePlugUser
插件:
https://github.com/apkplug/SDKDemo/tree/master/EasemobPlug