有時(shí)候我們需要在應(yīng)用中接入即時(shí)通訊等功能,可是又苦于apk過(guò)大管跺、65535方法數(shù)等相關(guān)問(wèn)題义黎,這時(shí)候最好的方式就是用插件化的方式接入,下面是我用一個(gè)插件化框架接入環(huán)信的過(guò)程豁跑,跟大家分享一下廉涕。
這里我使用的是Apkplug這個(gè)插件化框架,這是一款我目前通過(guò)對(duì)比找到的最適合的插件化工具贩绕,在使用方便火的、運(yùn)行穩(wěn)定等方面均有良好表現(xiàn),還提供后臺(tái)托管淑倾,這一點(diǎn)也是極大的方便了開(kāi)發(fā)者馏鹤。
這里是這個(gè)框架的官方文檔可以參考
環(huán)信插件開(kāi)發(fā)
一、插件開(kāi)發(fā)
首先我們對(duì)這個(gè)插件的需求是:能在一個(gè)有用戶系統(tǒng)的宿主app里提供聊天功能娇哆。
進(jìn)一步細(xì)分為如下具體功能:
1 能跟宿主一起登陸
2 能跟宿主用戶系統(tǒng)有統(tǒng)一映射
3 能提供聊天界面
4 能提供好友界面
5 能提供最近會(huì)話界面
具體實(shí)現(xiàn):
1 首先要按照環(huán)信的文檔湃累,先進(jìn)行初始化和相關(guān)配置
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);
}
}
插件在宿主中啟動(dòng)時(shí)會(huì)自動(dòng)執(zhí)行application中的初始化代碼勃救。配置就不在這里貼了,可以參考環(huán)信文檔或下面的demo代碼治力。
2 跟宿主一起登陸蒙秒,是為了能夠跟宿主融合的更加融洽,如果點(diǎn)擊按鈕彈出一個(gè)登陸界面宵统,則像是兩個(gè)app之間的互相調(diào)用晕讲。所以在宿主登陸的時(shí)候,同時(shí)要登陸插件马澈,并對(duì)用戶透明瓢省。因此插件需要提供一個(gè)登陸接口。我用Dispatcher的方式聲明接口:
繼承一個(gè)com.apkplug.easemobplug.Processores類(lèi)痊班,實(shí)現(xiàn)Receive方法勤婚,在方法中調(diào)用環(huán)信登陸,宿主使用這個(gè)接口時(shí)可以把用戶名涤伐、密碼傳過(guò)來(lái)馒胆。參數(shù)的傳遞是靠人為約定的,宿主開(kāi)發(fā)者和插件開(kāi)發(fā)者按照約定取值凝果。DispatchAgent類(lèi)是管插件和宿主理通信的類(lèi)祝迂,在登陸完成后,我調(diào)用dispatchAgent.reply回調(diào)宿主器净,同樣液兽,給宿主發(fā)送結(jié)果的參數(shù)格式也是要人為約定好。
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) {
}
});
}
}
這樣一個(gè)登陸接口就做好了掌动。
3 跟宿主應(yīng)用統(tǒng)一用戶系統(tǒng),環(huán)信插件里的用戶和聊天的好友一定是宿主應(yīng)用用戶系統(tǒng)中的用戶及好友宁玫,所以哪些人是用戶粗恢,那些人跟哪些人是好友,需要宿主告訴環(huán)信插件欧瘪。
首先需要提供注冊(cè)接口眷射,宿主用戶注冊(cè)時(shí),同步注冊(cè)環(huán)信插件佛掖,映射關(guān)系隨便定義:
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);
}
}
}
好友的同步有兩種實(shí)現(xiàn)方式:
1 直接由宿主調(diào)用插件接口妖碉,設(shè)置插件的好友列表
2 宿主只管好友添加,插件自己去服務(wù)器拿好友列表
貌似方法2要好很多芥被,但是需要服務(wù)端的對(duì)接欧宜,只有服務(wù)端可以直接添加好友,客戶端添加好友的接口拴魄,最多只能等對(duì)方登陸時(shí)候才添加成功冗茸。
demo里這兩種我都實(shí)現(xiàn)了席镀,跟上面登陸接口一樣,我實(shí)現(xiàn)了一個(gè)直接給好友列表界面添加好友的接口夏漱,只供參考豪诲,我并沒(méi)有調(diào)用。我用的第二種方式挂绰,當(dāng)宿主的用戶系統(tǒng)添加好友時(shí)屎篱,同時(shí)給環(huán)信添加好友,因此我對(duì)外提供了添加好友的接口葵蒂。
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);
}
}
}
一系列你希望宿主用到的接口實(shí)現(xiàn)好后交播,就可以對(duì)外注冊(cè)了,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 提供聊天界面刹勃、好友界面堪侯、會(huì)話界面,環(huán)信提供一些直接可用的界面荔仁,稍加改動(dòng)就可以使用伍宦,宿主使用時(shí),只需要用Intent啟動(dòng)即可乏梁,需要注意的是次洼,一些manifest文件activity標(biāo)簽中配置的值,并不能同步到宿主遇骑,如果需要那些值卖毁,只能在宿主配置插件的activity,比如主界面落萎,android:windowSoftInputMode="adjustPan"
這個(gè)值不配置的話亥啦,輸入法彈出會(huì)壓縮界面控件,只在插件里配置是不起作用的练链。
<activity android:name=".ui.MainActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:windowSoftInputMode="adjustPan"
/>
提供了這些功能后翔脱,插件就開(kāi)發(fā)完成了。
二媒鼓、宿主開(kāi)發(fā)
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 登陸插件绿鸣,我已經(jīng)注冊(cè)過(guò)了用戶疚沐,這里不再調(diào)用,調(diào)用方式相同潮模。用DispatchAgent對(duì)象調(diào)用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開(kāi)始登陸環(huá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環(huán)信登陸成功");
chatInitandLogin(dispatchAgent);
}
@Override
public void timeout(URI uri) throws Exception {
}
@Override
public void Exception(URI uri, Throwable throwable) {
System.err.println(uri);
}
});
登陸后就隨時(shí)可以進(jìn)行界面跳轉(zhuǎn)了
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