Skip to content

Android Java/XML 接入

本页默认使用 Java、CompletableFuture、Listener 和 XML/ViewBinding,现有 MVP、MVVM 或传统 Activity/Fragment 工程可以直接接入,不要求改成 Kotlin Coroutine 或 Jetpack Compose。

完成本页后,你会连接一个测试账号、监听数据变化并向另一个账号发送文字消息。

不想从空白项目开始时,直接运行 platforms/android/demo-java。该工程 只有 Java + XML 业务代码,已经包含 Repository、ViewModel、会话列表、聊天页和 应用自带矢量图标。

bash
cd platforms/android
JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" \
ANDROID_HOME="$HOME/Library/Android/sdk" \
./gradlew :demo-java:assembleDebug -Pxhim.skipNativeBuild=true

xhim.skipNativeBuild=true 只用于单独检查 Java 源码;真实运行时必须消费完整 Maven AAR,或用包含目标 ABI Core/JNI 的正式构建。

text
准备 Server 和测试账号
  -> 添加 Maven 依赖
  -> 连接当前账号
  -> 注册 Listener
  -> 发送第一条消息
  -> 验证登出和换号

1. 准备接入信息

向服务端负责人领取 Server URL、App ID、当前用户 ID 和一个对端用户 ID。 正式环境还需要应用自己的后台提供短期 XHIM 凭证。客户端不保存管理员密钥, 也不需要自行管理 WebSocket 或 SDK 数据库。

2. 安装

Groovy build.gradle

groovy
dependencies {
    implementation "com.xihansoftware.xhim:xhim-sdk:<version>"
}

Kotlin DSL 项目也可以保留 Java 业务源码:

kotlin
dependencies {
    implementation("com.xihansoftware.xhim:xhim-sdk:<version>")
}

xhim-sdk 不依赖 Compose。只有宿主已经使用 Compose 界面时才加 xhim-ui-compose。Minimum SDK 为 API 24,建议 JDK 17 与 Android Gradle Plugin 8.x 的稳定版组合。

3. Development 连接

Development Server 明确启用免密登录时:

java
import com.xihansoftware.xhim.XHIMJavaClient;
import java.util.concurrent.CompletableFuture;

CompletableFuture<XHIMJavaClient> connecting =
        XHIMJavaClient.connectDevelopment(
                getApplicationContext(),
                "https://im.customer.example",
                "alice");

connecting.thenAccept(client -> repository.attach(client))
        .exceptionally(error -> {
            repository.reportConnectionError(error);
            return null;
        });

Production 不使用 Development Login。让已有业务账号 Repository 提供 当前用户凭证:

java
XHIMJavaClient.connectWithCredentialProvider(
        getApplicationContext(),
        BuildConfig.XHIM_SERVER_URL,
        businessSession.currentUserId(),
        () -> accountRepository.fetchXHIMCredentialBlocking()
).thenAccept(repository::attach);

Credential Provider 会在 SDK IO 线程调用。它不能访问 Activity/View, 不能保存明文 Token,也不能向 UI 打印凭证。

4. Repository 中订阅事件

java
import java.io.Closeable;
import java.util.concurrent.Executor;

final class XHIMRepository implements Closeable {
    private final Executor mainExecutor;
    private XHIMJavaClient client;
    private Closeable events;

    XHIMRepository(Executor mainExecutor) {
        this.mainExecutor = mainExecutor;
    }

    void attach(XHIMJavaClient connected) {
        client = connected;
        events = connected.addEventListener(event -> {
            // 收到变化后,按事件中的会话或模块重新查询数据。
        }, mainExecutor);
    }

    @Override public void close() throws Exception {
        if (events != null) events.close();
        if (client != null) client.close();
    }
}

SDK 不强制 UI 线程框架。传给 addEventListenerExecutor 决定 事件交付线程。Activity 可以使用 ContextCompat.getMainExecutor(...), 单元测试可以传直接 Executor。

5. 发送文字并刷新消息

java
import java.util.UUID;

String clientMessageId = UUID.randomUUID().toString();
client.sendText(conversationId, draft, clientMessageId)
        .thenCompose(receipt -> client.messages(conversationId))
        .thenAcceptAsync(page -> viewModel.replaceMessages(page), mainExecutor)
        .exceptionally(error -> {
            viewModel.showStableError(error);
            return null;
        });

sendText 完成只表示本地可靠 Outbox 已受理。最终发送状态通过 messages(...) 查询;不要用回调 payload 在页面另维护一份消息库。

6. 已读、Presence 和 Typing

java
client.markConversationRead(conversationId, throughServerSequence);
client.publishPresence(XHIMPresenceStatus.ONLINE);
client.publishTyping(conversationId, true);

// 输入框清空、发送成功或页面离开时清除 Typing。
client.publishTyping(conversationId, false);

7. 编辑、撤回与历史消息

常用逻辑只组合 CompletableFuture,不需要在 Activity 中手写 Coroutine 或 JNI 回调:

java
String mutationId = UUID.randomUUID().toString();

client.recall(conversationId, serverMessageId, mutationId, messageRevision)
        .thenAccept(recalled -> viewModel.replaceMessage(recalled))
        .exceptionally(error -> {
            viewModel.showStableError(error);
            return null;
        });

client.getMessageHistory(conversationId)
        .thenAccept(page -> viewModel.prependHistory(page));

mutationId 只用于同一次逻辑操作的精确重试; messageRevision 使用最后一次权威消息快照中的 revision。

8. 好友与群组

java
client.friendships()
        .thenAccept(page -> contactRepository.replace(page.getItems()));

client.groups()
        .thenAccept(page -> groupRepository.replace(page.getItems()));

client.changeGroupMembers(
        conversationId,
        List.of("bob"),
        List.of(),
        groupRevision,
        UUID.randomUUID().toString())
        .thenAccept(change -> groupRepository.apply(change));

好友、群成员和用户资料都返回不可变模型。列表与搜索的 cursor 是不透明字节;业务层原样传回 SDK,不自行解码。

9. 取消、异常和关闭

java
CompletableFuture<XHIMMessagePage> request = client.messages(conversationId);

// Fragment/ViewModel 销毁时停止等待。
request.cancel(true);

request.exceptionally(error -> {
    Throwable cause = error instanceof java.util.concurrent.CompletionException
            ? error.getCause()
            : error;
    // cause 为 XHIMException 时按 stableCode 分支,不解析 message。
    return null;
});

client.shutdown();
  • CompletableFuture.cancel(...) 会把取消请求传给 SDK;
  • UI 不应根据异常 message 分支,而应使用 XHIMException.stableCode
  • close() 只做本地资源释放;正常账号退出先调 logout()
  • Application 退出或账号容器销毁时调 shutdown()/close()

10. Java 常用方法

模块Java 入口
连接connectDevelopment / connectWithAccessToken / connectWithCredentialProvider
会话conversations / searchConversations / directConversation / clearConversation / hideConversation / markConversationRead / markAllConversationsRead / totalUnreadCount / setLocalDraft
消息sendText / sendMessage / editText / recall / message / messages / messagesAfter / messageContext / getMessageHistory / searchMessages / deleteMessageForSelf / retryMessage / cancelMessage
用户currentUserProfile / userProfiles / resolveUserByPhone / updateCurrentUserProfile / publishPresence / publishTyping
好友friendRequests / friendships / searchFriendships / sendFriendRequest / resolveFriendRequest / deleteFriendship / setFriendRemark / setFriendPinned / blocks / setBlock
群组groups / groupMembers / searchGroups / searchGroupMembers / createGroup / changeGroupMembers / leaveGroup / dismissGroup
推送与设备registerPushDevice / disablePushDevice / listDeviceSessions / revokeDeviceSession
生命周期state / diagnostics / deviceSessionPolicy / compatibility / sessionIdentity / notifyNetworkAvailable / updateCredential / logout / shutdown

每个独立 API 页只有在 Java 入口真实存在时,才展示“Android · Java”。没有 Java 高层入口的能力会明确标为当前版本不支持,不要求应用调用 Kotlin 字节码接口。

XHIM 客户端 SDK 与服务端文档