# Join multiple channels (/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Agora Video SDK enables you to simultaneously join multiple channels. This capability allows you to receive and publish audio and video streams across multiple channels concurrently.


      
## Understand the tech

Video SDK's multi-channel functionality is based on two key components:

* `RtcConnection`

  The `RtcConnection` object identifies a connection. It contains the following information:

  * Channel name
  * User ID of the local user

  You create multiple `RtcConnection` objects, each with a different channel name and user ID. Each `RtcConnection` instance can independently publish multiple audio streams and a single video stream.

* `RtcEngineEx`

  The class contains methods tailored for interacting with a designated `RtcConnection` object.

  To join multiple channels, you call `joinChannelEx` method in the `RtcEngineEx` class multiple times, using a different `RtcConnection` instance each time.

When joining multiple channels:

* Ensure that the user ID for each `RtcConnection` object is unique and nonzero.

* Configure publishing and subscribing options for the `RtcConnection` object in `joinChannelEx`.

* Pass the `IRtcEngineEventHandler` object to the `eventHandler` parameter when calling the `joinChannelEx` method to receive multiple channel-related event notifications.

## Prerequisites

Ensure that you have implemented the [SDK quickstart](../../index.mdx) in your project.

## Implementation

This section explains how to join a second channel as a host after you have already joined the first channel.

1. Declare variables for `RtcEngineEx` and `RtcConnection` objects.

<CodeBlockTabs defaultValue="java">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="java">
      Java
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="kotlin">
      Kotlin
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="java">
    ```java
    private RtcEngineEx engine;
    private RtcConnection rtcConnection2 = new RtcConnection();
    ```
  </CodeBlockTab>

  <CodeBlockTab value="kotlin">
    ```kotlin
    private lateinit var engine: RtcEngineEx
    private val rtcConnection2 = RtcConnection()
    ```
  </CodeBlockTab>
</CodeBlockTabs>

2. Initialize the engine instance.

<CodeBlockTabs defaultValue="java">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="java">
      Java
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="kotlin">
      Kotlin
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="java">
    ```java
    engine = (RtcEngineEx) RtcEngine.create(config);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="kotlin">
    ```kotlin
    engine = RtcEngine.create(config) as RtcEngineEx
    ```
  </CodeBlockTab>
</CodeBlockTabs>

3. Join the channel using a random user ID.

<CodeBlockTabs defaultValue="java">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="java">
      Java
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="kotlin">
      Kotlin
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="java">
    ```java
    private boolean joinSecondChannel() {
       ChannelMediaOptions option = new ChannelMediaOptions();
       mediaOptions.autoSubscribeAudio = true;
       mediaOptions.autoSubscribeVideo = true;
       rtcConnection2.channelId = "channel-2";
       rtcConnection2.localUid = new Random().nextInt(512)+512;
       int ret = engine.joinChannelEx("your token", rtcConnection2, mediaOptions,
         iRtcEngineEventHandler2);
       return (ret == 0);
     }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="kotlin">
    ```kotlin
    private fun joinSecondChannel(): Boolean {
      val mediaOptions = ChannelMediaOptions().apply {
        autoSubscribeAudio = true
        autoSubscribeVideo = true
      }
      rtcConnection2.channelId = "channel-2"
      rtcConnection2.localUid = (Random().nextInt(512) + 512)
      val ret = engine.joinChannelEx("your token", rtcConnection2, mediaOptions, iRtcEngineEventHandler2)
      return ret == 0
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>

4. Listen for events in `rtcConnection2` and set up the remote video in the `onUserJoined` callback.

<CodeBlockTabs defaultValue="java">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="java">
      Java
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="kotlin">
      Kotlin
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="java">
    ```java
    private final IRtcEngineEventHandler iRtcEngineEventHandler2 = new IRtcEngineEventHandler() {
      @Override
      public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
        Log.i(TAG, String.format("channel2 onJoinChannelSuccess channel %s uid %d", channel2, uid));
        showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel2, uid));
      }
      @Override
      public void onUserJoined(int uid, int elapsed) {
        Log.i(TAG, "channel2 onUserJoined->" + uid);
        showLongToast(String.format("user %d joined!", uid));
        Context context = getContext();
        if (context == null) {
          return;
        }
        handler.post(() ->
        {
          // Display the remote video stream
          SurfaceView surfaceView = null;
          if (fl_remote2.getChildCount() > 0) {
            fl_remote2.removeAllViews();
          }
          // Create the rendering view through RtcEngine
          surfaceView = new SurfaceView(context);
          surfaceView.setZOrderMediaOverlay(true);
          // Add the view to the remote container
          fl_remote2.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

          // Set the remote view
          engine.setupRemoteVideoEx(new VideoCanvas(surfaceView, RENDER_MODE_FIT, uid), rtcConnection2);
        });
      }
    };
    ```
  </CodeBlockTab>

  <CodeBlockTab value="kotlin">
    ```kotlin
    private val iRtcEngineEventHandler2 = object : IRtcEngineEventHandler() {
      override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
        Log.i(TAG, String.format("channel2 onJoinChannelSuccess channel %s uid %d", channel2, uid))
        showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel2, uid))
      }

      override fun onUserJoined(uid: Int, elapsed: Int) {
        Log.i(TAG, "channel2 onUserJoined->$uid")
        showLongToast("user $uid joined!")

        val context = context
        if (context == null) {
          return
        }

        handler.post {
          // Display the remote video stream
          var surfaceView: SurfaceView? = null
          if (fl_remote2.childCount > 0) {
            fl_remote2.removeAllViews()
          }
          // Create the rendering view through RtcEngine
          surfaceView = SurfaceView(context)
          surfaceView?.zOrderMediaOverlay = true
          // Add the view to the remote container
          fl_remote2.addView(surfaceView, FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))

          // Set the remote view
          engine.setupRemoteVideoEx(VideoCanvas(surfaceView, RENDER_MODE_FIT, uid), rtcConnection2)
        }
      }
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

### Sample project

Agora provides the [JoinMultipleChannels](https://github.com/AgoraIO/API-Examples/blob/main/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/JoinMultipleChannel.java) open-source sample project for your reference. Download the project or view the source code for a more detailed example.

### API reference

* [`RtcEngineEx`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineex.html#class_irtcengineex)
* [`RtcConnection`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_rtcconnection.html)
* [`joinChannelEx`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineex.html#api_irtcengineex_joinchannelex)
* [`setupRemoteVideoEx`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineex.html#api_irtcengineex_setupremotevideoex)

    
  
      
  
      
  
      
  
      
  
      
  
      
  


## Platform-specific versions
- [Android](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/android.md)
- [iOS](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/ios.md)
- [macOS](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/macos.md)
- [Windows](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/windows.md)
- [Electron](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/electron.md)
- [React Native](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/react-native.md)
- [Unity](/en/realtime-media/interactive-live-streaming/build/connect-across-channels/join-multiple-channels/unity.md)
