ワンクリックで
review-case
// Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
// Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
| name | review-case |
| description | Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly. |
| compatibility | ["Cursor","Kiro","Windsurf","Claude","Copilot"] |
| license | MIT |
| metadata | {"author":"APIExample Team","version":"1.0.0","platform":"Windows"} |
Use this skill when you need to:
Check:
InitializeAgoraEngine() or similarRtcEngineContextleaveChannel() is called before release()release() is called in the case's real cleanup pathCorrect Pattern A — standalone dialog teardown:
BOOL CExampleDlg::OnInitDialog() {
CDialogEx::OnInitDialog();
InitializeAgoraEngine(); // Create once
return TRUE;
}
void CExampleDlg::PostNcDestroy() {
LeaveChannel();
if (m_rtcEngine) {
m_rtcEngine->release();
m_rtcEngine = nullptr;
}
CDialogEx::PostNcDestroy();
delete this;
}
void CExampleDlg::JoinChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->joinChannel(token, channelName, "", 0);
}
void CExampleDlg::LeaveChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->leaveChannel();
}
Correct Pattern B — scene-switching dialog teardown:
bool CExampleDlg::InitAgora() {
m_rtcEngine = createAgoraRtcEngine();
// initialize once when the scene becomes active
return m_rtcEngine != nullptr;
}
void CExampleDlg::UnInitAgora() {
if (!m_rtcEngine) return;
if (m_joinChannel) {
m_rtcEngine->leaveChannel();
}
m_rtcEngine->release(nullptr);
m_rtcEngine = nullptr;
}
void CExampleDlg::OnShowWindow(BOOL bShow, UINT nStatus) {
CDialogEx::OnShowWindow(bShow, nStatus);
if (!bShow) {
UnInitAgora();
}
}
Accept either pattern as long as the dialog follows one lifecycle consistently and does not leak the engine across scene switches.
Incorrect Pattern:
See references/incorrect-lifecycle.cpp for common mistakes.
Check:
PostMessage()Correct Pattern:
// Event handler (may be called from background thread)
void CExampleRtcEngineEventHandler::onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
if (m_hMsgHandler) {
// Post message to main thread
::PostMessage(m_hMsgHandler, WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), (WPARAM)uid, 0);
}
}
// Message handler (runs on main thread)
LRESULT CExampleDlg::OnMsgEngineEvent(WPARAM wParam, LPARAM lParam) {
// Safe to update UI here
m_statusText.SetWindowText(_T("Joined channel"));
return 0;
}
Incorrect Pattern:
See references/incorrect-thread-safety.cpp for common mistakes.
Check:
enableAudio()enableVideo()Correct Pattern:
void CExampleDlg::InitializeAgoraEngine() {
m_rtcEngine = createAgoraRtcEngine();
if (!m_rtcEngine) return;
RtcEngineContext context;
context.appId = CConfig::GetAppId();
context.eventHandler = &m_eventHandler;
m_eventHandler.SetMsgReceiver(m_hWnd);
m_rtcEngine->initialize(context);
// Check device availability
if (m_rtcEngine->enableVideo() == 0) {
// Video enabled successfully
}
if (m_rtcEngine->enableAudio() == 0) {
// Audio enabled successfully
}
}
Check:
joinChannel() return value checkedonError() callback implementedCorrect Pattern:
void CExampleDlg::JoinChannel() {
if (!m_rtcEngine) return;
const char* token = CConfig::GetToken("test");
int ret = m_rtcEngine->joinChannel(token, "test", "", 0);
if (ret != 0) {
// Handle error
MessageBox(_T("Failed to join channel"), _T("Error"));
}
}
void CExampleRtcEngineEventHandler::onError(int err) {
if (m_hMsgHandler) {
::PostMessage(m_hMsgHandler, WM_MSGID(EID_ERROR), (WPARAM)err, 0);
}
}
LRESULT CExampleDlg::OnMsgEngineEvent(WPARAM wParam, LPARAM lParam) {
if (wParam == EID_ERROR) {
int errorCode = (int)lParam;
// Handle error
}
return 0;
}
Check:
C<ExampleName>DlgC<ExampleName>RtcEngineEventHandlerm_ prefixCorrect Pattern:
// Header: CScreenShareDlg.h
class CScreenShareRtcEngineEventHandler : public IRtcEngineEventHandler {
// ...
};
class CScreenShareDlg : public CDialogEx {
DECLARE_DYNAMIC(CScreenShareDlg)
private:
IRtcEngine* m_rtcEngine = nullptr;
CScreenShareRtcEngineEventHandler m_eventHandler;
uid_t m_remoteUid = 0;
bool m_isJoined = false;
BEGIN_MESSAGE_MAP(CScreenShareDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_JOIN, &CScreenShareDlg::OnBnClickedButtonJoin)
END_MESSAGE_MAP()
};
Check:
Correct Pattern:
// Correct order: create -> initialize -> enable -> join
m_rtcEngine = createAgoraRtcEngine();
m_rtcEngine->initialize(context);
m_rtcEngine->enableVideo();
m_rtcEngine->enableAudio();
m_rtcEngine->joinChannel(token, channelName, "", 0);
Incorrect Pattern:
// ❌ Wrong order
m_rtcEngine->joinChannel(...); // Join first
m_rtcEngine->enableVideo(); // Enable after join (too late)
Check:
Correct Pattern:
void CExampleDlg::LeaveChannel() {
if (!m_rtcEngine) return;
m_rtcEngine->stopAudioMixing(); // Stop audio
m_rtcEngine->stopScreenCapture(); // Stop screen share
m_rtcEngine->leaveChannel();
m_isJoined = false;
}
void CExampleDlg::PostNcDestroy() {
LeaveChannel();
if (m_rtcEngine) {
m_rtcEngine->release();
m_rtcEngine = nullptr;
}
CDialogEx::PostNcDestroy();
delete this;
}
When reviewing, provide feedback in this format:
## Review Results
### ✅ Passed
- Engine lifecycle correctly managed
- Thread safety ensured with message map pattern
- Error handling implemented for joinChannel
### ⚠️ Issues Found
**[HIGH] Thread Safety Issue**
- File: `CScreenShareDlg.cpp`
- Line: 45
- Issue: Direct UI update in event handler without PostMessage
- Suggestion: Use PostMessage to post event to main thread
**[MEDIUM] Missing Error Handling**
- File: `CScreenShareDlg.cpp`
- Line: 78
- Issue: joinChannel() return value not checked
- Suggestion: Check return value and handle errors
### 🔧 Recommendations
- Add logging for debugging
- Consider adding retry logic for network failures
Check:
Correct Pattern:
// Windows: Use MFC
#include "stdafx.h"
#include "APIExample.h"
class CExampleDlg : public CDialogEx {
DECLARE_DYNAMIC(CExampleDlg)
BEGIN_MESSAGE_MAP(CExampleDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_JOIN, &CExampleDlg::OnBnClickedButtonJoin)
END_MESSAGE_MAP()
};
Incorrect Pattern:
// ❌ Non-MFC patterns
using namespace std; // Avoid in MFC
auto ptr = std::make_unique<IRtcEngine>(); // Modern C++ not typical in MFC
Do NOT accept:
leaveChannel() before release()APIExample/APIExample/[Basic|Advanced]/ structureUse this checklist when reviewing an example:
Lifecycle:
leaveChannel() called before release()release() called in PostNcDestroy()Thread Safety:
PostMessage()Permissions:
Error Handling:
Code Quality:
API Usage:
Resources:
Platform:
Cause: release() called without leaveChannel() first
Fix: Always call leaveChannel() before release()
Cause: Direct UI update from event handler Fix: Use PostMessage to post event to main thread
Cause: release() not called or engine recreated
Fix: Ensure release() in PostNcDestroy() and create engine once
Cause: No token refresh handling Fix: Implement token refresh in error handler
Cause: Device not available or not enabled
Fix: Check return values of enableAudio() / enableVideo()
APIExample/APIExample/Basic/JoinChannelVideoByToken/ for referenceCode review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including file structure, per-example storyboard creation, registration, and ARCHITECTURE.md updates.
Add a new API example or modify an existing one. Covers both creation and modification scenarios, including dialog class structure, registration in APIExampleDlg, localization wiring, and ARCHITECTURE.md updates.
Add a new API example case or modify an existing one in the APIExample Android demo — creates or updates Fragment class, XML layout, string resources, and nav_graph registration. Use when: adding a new Agora RTC API demo screen, modifying an existing case's implementation or registration, implementing a new feature example in Java + XML layouts, registering a new case via @Example annotation, subclassing BaseFragment for a new demo screen, or updating an existing case's strings, layout, or nav entry. Keywords: add case, modify case, update case, new fragment, nav_graph, @Example, BaseFragment, APIExample, new screen, demo case, RTC API example.
Add a new audio API example case or modify an existing one in the APIExample-Audio Android demo — creates or updates Fragment class, XML layout, string resources, and nav_graph registration. Use when: adding a new Agora audio API demo screen, modifying an existing case's implementation or registration, implementing a new audio feature example in Java + XML layouts, registering a new case via @Example annotation, subclassing BaseFragment for a new audio demo screen, or updating an existing case's strings, layout, or nav entry. This project uses voice-sdk — no video APIs available. Keywords: add case, modify case, update case, new fragment, nav_graph, @Example, BaseFragment, APIExample-Audio, audio case, voice-sdk, new screen, audio demo, upsert case.
Add a new API example case or modify an existing one in the APIExample-Compose Android demo — creates or updates a Kotlin Composable file, registers or updates it in Examples.kt, and manages string resources. Use when: adding a new Agora RTC API demo screen in Jetpack Compose, modifying an existing case's implementation or registration, porting an existing APIExample case to Compose, implementing a new feature example in Kotlin + Compose UI, registering a new entry in BasicExampleList or AdvanceExampleList, or updating an existing case's strings or Examples.kt entry. Kotlin only — no XML layouts, no Fragments. Keywords: add case, modify case, update case, new composable, Examples.kt, BasicExampleList, AdvanceExampleList, APIExample-Compose, Compose case, new screen, Jetpack Compose, RTC API example, upsert case.