环信插件开发

这个例子实际的将环信做成了一个插件,并在宿主中使用,主要侧重展现一下插件间的通信交互。

对于插件间通信基础知识不了解的话,先看这里 RPC通信方式

一、插件开发

首先我们对这个插件的需求是:能在一个有用户系统的宿主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之间的互相调用。所以在宿主登陆的时候,同时要登陆插件,并对用户透明。因此插件需要提供一个登陆接口。如下是所有功能的总接口,包括登陆

public interface IEaseInstance {
    void loginByCode(String userName, String password, Action2<Boolean, String> callback);
    void loginByView(String userName, String password, Action2<Boolean, String> callback);
    boolean addFriend(String userName);
    List<String> getContents();
    boolean regist(String userName, String password);
}

登陆接口的实现:

public void loginByCode(String userName, String passWord, final Action2<Boolean, String> callback) {

    if(userName == null || passWord == null){
        //dispatchAgent.reply(getMsgId(),false,new Exception("your username or password is null"));
        callback.call(false,"your username or password is null");
        return;
    }

    EMClient.getInstance().login(userName, passWord, new EMCallBack() {
        @Override
        public void onSuccess() {
            //dispatchAgent.reply(getMsgId(),true,"success");
            callback.call(true,"success");
        }

        @Override
        public void onError(int i, String s) {
            //dispatchAgent.reply(getMsgId(),false,s);
            callback.call(false,s);
        }

        @Override
        public void onProgress(int i, String s) {

        }
    });
}

这样一个登陆接口就做好了。

3 跟宿主应用统一用户系统,环信插件里的用户和聊天的好友一定是宿主应用用户系统中的用户及好友,所以哪些人是用户,那些人跟哪些人是好友,需要宿主告诉环信插件。

首先需要提供注册接口,宿主用户注册时,同步注册环信插件,映射关系随便定义:

public boolean regist(String userName, String password) {
    if(userName == null || password == null){
        //dispatchAgent.reply(getMsgId(),false,new Exception("username or password is null"));
        return false;
    }
    try {
        EMClient.getInstance().createAccount(userName,password);
        //dispatchAgent.reply(getMsgId(),true,"success");
        return true;
    } catch (HyphenateException e) {

// dispatchAgent.reply(getMsgId(),false,e); e.printStackTrace(); } return false; }

好友的同步有两种实现方式:

1 直接由宿主调用插件接口,设置插件的好友列表

2 宿主只管好友添加,插件自己去服务器拿好友列表

貌似方法2要好很多,但是需要服务端的对接,只有服务端可以直接添加好友,客户端添加好友的接口,最多只能等对方登陆时候才添加成功。

demo里这两种我都实现了,跟上面登陆接口一样,我实现了一个直接给好友列表界面添加好友的接口,只供参考,我并没有调用。我用的第二种方式,当宿主的用户系统添加好友时,同时给环信添加好友,因此我对外提供了添加好友的接口。

public boolean addFriend(String username) {
    try {
        EMClient.getInstance().contactManager().addContact(username, "you have to accept");
        //dispatchAgent.reply(getMsgId(),true,"success");
        return true;
    } catch (HyphenateException e) {

// dispatchAgent.reply(getMsgId(),false,e); e.printStackTrace(); return false; } }

一系列你希望宿主用到的接口实现好后,就可以对外注册了,plugin.xml中添加:

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 登陆插件,我已经注册过了用户,这里不再调用,调用方式相同。

BundleRPCAgent agent = new BundleRPCAgent(FrameworkFactory.getInstance().getFrame().getSystemBundleContext());
    try {
        final IEaseInstance easeInstance = agent.syncCall("apkplug://measeplug/rpc/instance",IEaseInstance.class);
        easeInstance.loginByCode("apkplug", "lbh131206", new Action2<Boolean, String>() {
            @Override
            public void call(Boolean aBoolean, String s) {
                if(!aBoolean){
                    return;
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(textView.getText()+"\n环信登陆成功");
                    }
                });
                startChatActivity("com.apkplug.easemobplug.ui.LoginActivity");
            }
        });
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }

登陆后就随时可以进行界面跳转了

Intent intent = new Intent();
    intent.setClassName(MainActivity.this, className);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent)

demo地址:

https://github.com/apkplug/plugspace/tree/master/EasePlugUser