Skip to content

Repository files navigation

Net.Agora.Android

NuGet Targets: net8.0 | net9.0 | net10.0 full-rtc-basic 4.6.3 Licence: MIT

.NET for Android and .NET MAUI bindings for Agora's native Android SDKs.

Seven products are bound, from net8.0-android through net10.0-android:

Package Native artifact Use it when
Net.Agora.Video.Android io.agora.rtc:full-rtc-basic The app shows or sends video (also carries the full audio surface).
Net.Agora.Voice.Android io.agora.rtc:voice-rtc-basic Audio only — the same engine built without the video pipeline, a ~20 MB smaller .aar.
Net.Agora.Signaling.Android io.agora:agora-rtm Realtime messaging (Signaling / RTM 2.x, its own 2.2.x version line) — coexists with either RTC package from 2.2.6.3; earlier versions break RtcEngine.Create() in an app that also references one (see the 2.2.6.3 release note).
Net.Agora.Chat.Android io.agora.rtc:chat-sdk Persistent messaging (Chat / IM 1.x, its own version line) — coexists with everything else here.
Net.Agora.Whiteboard.Android com.github.netless-io:whiteboard-android The Interactive Whiteboard (2.16.x). A WebView SDK from JitPack, not Maven Central — it carries no native code, so it coexists with everything else here.
Net.Agora.Fastboard.Android com.github.netless-io:fastboard-android netless's ready-made UI over the whiteboard (1.8.x) — a board with a working toolbar rather than a bare canvas. Depends on the row above.
Net.Agora.IoT.Android io.agora:iotsdk Agora IoT devices (1.3.x). Android only — there is no iOS SDK, so no cross-platform client — and exclusive with every package above, whose native artifacts it bundles its own copies of.

Alongside them, twelve Net.Agora.Extensions.<Name>.Android packages carry the RTC SDK's optional features — AI noise suppression, virtual background, spatial audio, the video enhancement filters, the software encoders and the rest. They are native payload only: the switch that turns each one on already exists on Agora.Rtc.RtcEngine, and what these packages add is the .so the engine loads when it is flipped. Add one alongside either RTC package — they depend on neither, so they do not force a flavour. See src/Agora.Extension.md for the full list and for what is deliberately absent.

dotnet add package Net.Agora.Video.Android   # or Net.Agora.Voice.Android

Pick one of the RTC pair: both .aars carry the same Java classes (io.agora.rtc2.*), so referencing both fails the build at dex merge — mirroring Agora's own artifacts, where an app depends on the full or the voice SDK, never both. For the same reason both bindings expose the same Agora.Rtc namespace (renamed from io.agora.rtc2 to match Agora's own C# / Unity SDK naming), so an app can switch packages without touching code.

These are raw platform bindings — the full class-parse-generated surface, Agora.Rtc.RtcEngine and friends. Most apps want the cross-platform clients instead: Net.Agora.Video / Net.Agora.Voice, which wrap these packages and their iOS siblings behind one API. Reach for a binding directly only when you need something the cross-platform client does not expose.

using Agora.Rtc;

var config = new RtcEngineConfig { MContext = context, MAppId = "<APP_ID>" };
var engine = RtcEngine.Create(config);

engine.EnableVideo();
engine.JoinChannel(token: null, channelId: "my-channel", optionalInfo: null, uid: 0);

How this repository works

This repository is the only thing that binds Agora's Android SDKs: Net.Agora (the cross-platform façade) and Net.Agora.iOS (the iOS binding) are separate repositories, each with their own release cadence. Each package's version is <native artifact version>.<binding revision> — see Directory.Build.props for why the Android and iOS lines don't share a version number. The package set lives in build/packages.tsv; adding a package means adding a row there and a project under src/.

What is bound, and why not full-sdk / voice-sdk

io.agora.rtc:full-sdk (and its voice counterpart voice-sdk) is a POM-only aggregator over optional plugin .aars — AI noise suppression, face detection, virtual background, screen sharing, and so on. Binding all of it pulls in features these packages expose no API for, and Android's Java dependency verification (XA4241) refuses to build unless every one of those plugins is present too. io.agora.rtc:full-rtc-basic / voice-rtc-basic are Agora's own base artifacts: RtcEngine and the core surface these packages bind, with exactly one dependency (io.agora.infra:aosl).

Each project's Transforms/Metadata.xml removes the few internal implementation classes that class-parse cannot bind cleanly on its own — for Video the default camera/screen capturer, for both the raw video-frame/EGL/texture pipeline and the spatial-audio impl class — none of which a consumer of RtcEngine calls directly. (The voice .aar ships the same shared Java API layer, video types included; only the native pipeline differs.)

Building locally

dotnet build src/Net.Agora.Video.Android/Net.Agora.Video.Android.csproj -f net9.0-android35.0
./build/BuildNugets.sh
dotnet test tests/Net.Agora.Android.PackageTests

Nothing is fetched or committed: AndroidMavenLibrary resolves the .aar straight from Maven Central for net9.0-android35.0/net10.0-android36.0, cached under ~/.cache/dotnet-android/MavenCacheDirectory. net8.0-android34.0 uses a different path — that SDK pack has no AndroidMavenLibrary support at all, so src/Agora.Binding.props downloads the same .aar directly with an MSBuild DownloadFile target instead. See the comments there for why this matters: the unsupported item is silently ignored rather than erroring, so a build using it on net8 "succeeds" with an empty few-KB binding assembly and no error anywhere in the log.

No single .NET SDK builds net8, net9 and net10 for Android, so BuildNugets.sh packs twice (the installed SDK's band, then a net10 pass from a scratch global.json) and merges the results — see build/merge-packages.py.

Tests

Everything runs against the packed .nupkg in artifacts/, not the build output, because the failure modes worth catching here are packaging ones — most importantly the net8 "empty shell" trap above, which builds with 0 errors and 0 warnings.

  • tests/Net.Agora.Android.PackageTests (plain xUnit, runs anywhere) asserts the package layout — a real binding assembly and the native .aars for every target framework — and, through the metadata reader, the API itself: the core Agora.Rtc types exist, the IO.Agora.Rtc2 → Agora.Rtc rename left nothing behind, and RtcEngine still exposes the channel lifecycle entry points. A binding that failed to generate still packs cleanly; these are what notice.
  • tests/Net.Agora.Android.DeviceTests is a bare Android app (no MAUI, no test framework) that consumes the packed package and drives the raw binding on an emulator. One app holds one product, chosen with -p:AgoraDevicePackage=, and each product has its own suite behind its own define — six of them: Video/Voice create the engine, enable and disable video and audio and run the local camera preview; Signaling creates the client and drives the async adapters' fault path; Chat checks its three .so files are in the app, initialises the SDK and has a send refused through the SDK's own callback; Whiteboard checks the JavaScript bundle shipped as an asset, that WhiteboardView really extends DSBridge's DWebView, and builds the SDK against a live board view on the UI thread; Fastboard checks all three of its .aars and its merged toolbar resources, then inflates the board and reaches the whiteboard view through it. No Agora credentials are involved anywhere — identifiers are syntactically valid but unregistered, which is enough for everything short of joining, and every wait is bounded. Each run reports a single AGORA_E2E_DONE PASS/FAIL line to logcat, which .github/scripts/run-emulator-tests.sh turns into an exit code — the same marker the Net.Agora façade's own device tests use.

In CI (.github/workflows/build.yml) the validate job runs the package tests and the e2e job runs the emulator suite per package — Video, Voice, Chat, Whiteboard and Fastboard, one leg each since a single app can hold only one — on net8.0-android34.0 and net10.0-android36.0, the two extremes: net8's .aar arrives through the DownloadFile fallback, and net10's assets are grafted in by the merge step, so those are the two that could each break alone. Signaling gets one leg of its own with Java shrinking (R8) turned on, which is where the keep rules the packages ship in buildTransitive/ are exercised, and one more leg holds Video and Signaling in the same app — the only check anywhere that the two products coexist at runtime, and the regression test for the conflict signaling-v2.2.6.3 fixes. The sample job additionally links every sample that exists here for android-arm64 in Release — trimmed, AOT-compiled, R8-shrunk and packaged into a signed APK — which is the only check in the repository that covers the configuration a shipping app is built in.

Run the emulator suite locally, with an emulator already booted. Each product has its own version line, so pass its own (build/pins.sh resolves them):

./build/BuildNugets.sh
. ./build/pins.sh
export AGORA_DEVICE_RID=android-arm64          # an arm64 emulator, e.g. on Apple silicon
./.github/scripts/run-emulator-tests.sh "$AGORA_VIDEO_PACKAGE_VERSION" net9.0-android35.0
./.github/scripts/run-emulator-tests.sh "$AGORA_VOICE_PACKAGE_VERSION" net9.0-android35.0 Voice
./.github/scripts/run-emulator-tests.sh "$AGORA_CHAT_PACKAGE_VERSION" net10.0-android36.0 Chat

Sample

samples/Net.Agora.Sample.Android is a MAUI app built straight against the packages — no cross-platform façade — that creates Agora.Rtc.RtcEngine, joins a channel and renders the first remote user's video into a second view next to the local preview: an App ID entry, an optional channel/token pair, camera/microphone permission handling, and a SurfaceView behind a small MAUI handler (see its AgoraVideoView.cs for why a custom handler rather than a wrapped view).

samples/Net.Agora.Sample.Voice.Android is its audio-only sibling against Net.Agora.Voice.Android: capture, mute, speakerphone routing and the who-is-speaking volume reports, with no camera permission anywhere.

samples/Net.Agora.Sample.Signaling.Android drives Agora.Rtm.RtmClient on its own — a tiny chat room (log in, subscribe, publish, receive) written against the raw callback API, to show what the façade hides.

samples/Net.Agora.Sample.Chat.Android drives Agora.Chat.ChatClient on its own — the same tiny two-user chat shape as Signaling's sample, against IO.Agora.ICallBack rather than an [Async] counterpart, because that is what this binding generates: unlike Signaling and Voice, nothing here is hand-wrapped into Task-returning Additions. Incoming messages still arrive as an ordinary .NET event, ChatManager.MessageReceived.

samples/Net.Agora.Sample.Fastboard.Android drives Agora.Fastboard.Fastboard/FastRoom on its own: an Agora.Fastboard.FastboardView the app creates and hands to the SDK, which draws its own toolbar on top — join, read-only toggle, and driving the board from code (a red pencil) alongside that toolbar.

samples/Net.Agora.Sample.Whiteboard.Android drives Agora.Whiteboard.WhiteSdk/Room on its own: an Agora.Whiteboard.WhiteboardView the app creates, joined through netless's own IPromise shape rather than an awaitable Task, with a hand-rolled toolbar (pencil, eraser, undo/redo, clear) since — unlike Fastboard — the plain SDK draws nothing of its own.

The full join/publish/subscribe flows, wrapped behind one cross-platform API, are Net.Agora's samples.

It consumes the packed Net.Agora.Video.Android package from ./artifacts (see NuGet.config), so pack first. It targets net10.0-android36.0 and needs the .NET 10 SDK with the maui-android workload, which this repository's global.json does not select — hence the scratch directory:

./build/BuildNugets.sh
cd /tmp && dotnet new globaljson --sdk-version 10.0.100 --force
dotnet build <repo>/samples/Net.Agora.Sample.Android/Net.Agora.Sample.Android.csproj

Net.Agora.Android.sln deliberately contains the binding project and the tests but not the sample, so dotnet build Net.Agora.Android.sln does not require the MAUI workload.

Licence

MIT — see LICENSE. Agora's own SDK is distributed under Agora's SDK licence terms.

About

.NET for Android and .NET MAUI bindings for Agora's native Android SDKs — RTC video and voice, Signaling (RTM), Chat and the Interactive Whiteboard, from C#.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages