You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio集成PJSIP连接SIP服务器问题求助

我之前在Android上集成PJSIP连接SIP服务器时也踩过不少坑,参考Csipsimple的时候也遇到过适配问题,给你整理一套实用的步骤和排查要点,应该能帮你解决连接难题:

一、Android Studio中PJSIP的基础集成步骤
  • 自行编译PJSIP库:预编译库容易出现兼容性问题,建议自己编译源码。先拉取PJSIP最新稳定版源码,配置Android Studio自带的NDK(推荐r21e~r25版本,太新可能不兼容),修改pjlib/include/pj/config_site.h开启Android支持、禁用不需要的模块(比如不需要视频功能可关闭)。执行编译命令:
    ./configure-android
    make dep && make
    
    编译完成后会得到libpjsua2.so等架构对应的SO文件,以及pjsua2.jar绑定类。
  • 导入项目配置:在app/src/main/jniLibs下创建对应架构文件夹(如armeabi-v7a、arm64-v8a),放入SO文件;将pjsua2.jar复制到app/libs,在Module级build.gradle添加依赖:
    implementation files('libs/pjsua2.jar')
    
    同时指定支持的ABI:
    android {
        defaultConfig {
            ndk {
                abiFilters 'armeabi-v7a', 'arm64-v8a'
            }
        }
    }
    
  • 配置权限:在AndroidManifest.xml添加必要权限,Android 6+还需动态申请录音等危险权限:
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
二、连接SIP服务器的核心代码实现

用pjsua2的面向对象API封装SIP管理逻辑(避免Csipsimple旧版低级API的适配问题):

import org.pjsip.pjsua2.*;
import android.util.Log;

public class SipManager {
    private Endpoint sipEndpoint;
    private Account sipAccount;

    public void initSipFramework() throws Exception {
        // 初始化Endpoint核心实例
        sipEndpoint = new Endpoint();
        sipEndpoint.libCreate();
        
        // 配置日志(开启详细日志方便排查)
        EpConfig epConfig = new EpConfig();
        epConfig.getLogConfig().setLevel(5);
        epConfig.getLogConfig().setConsoleLevel(5);
        sipEndpoint.libInit(epConfig);

        // 创建UDP传输通道(SIP默认用UDP)
        TransportConfig transportConfig = new TransportConfig();
        transportConfig.setPort(5060);
        sipEndpoint.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, transportConfig);

        // 启动SIP框架
        sipEndpoint.libStart();
    }

    public void registerSipAccount(String sipUri, String username, String password, String serverIp) throws Exception {
        AccountConfig accConfig = new AccountConfig();
        // 设置账号唯一标识
        accConfig.getIdUri().setUri(sipUri); // 格式:sip:your_name@server_ip
        // 设置注册服务器地址
        accConfig.getRegConfig().getRegistrarUri().setUri("sip:" + serverIp);
        // 添加认证信息(大部分服务器用digest认证)
        AuthCredInfo authCred = new AuthCredInfo("digest", "*", username, 0, password);
        accConfig.getSipConfig().getAuthCreds().add(authCred);
        // 设置代理服务器地址
        SipServerAddress proxyAddr = new SipServerAddress();
        proxyAddr.setUri("sip:" + serverIp + ":5060");
        accConfig.getSipConfig().getSipProxies().add(proxyAddr);

        // 创建账号并发起注册
        sipAccount = new Account();
        sipAccount.create(accConfig);
        Log.d("SIP_DEBUG", "注册请求已发送");
    }

    // 销毁时释放资源,避免内存泄漏
    public void releaseSipResources() {
        if (sipAccount != null) sipAccount.delete();
        if (sipEndpoint != null) {
            sipEndpoint.libDestroy();
            sipEndpoint.delete();
        }
    }
}

在Activity中调用(注意在子线程执行SIP操作,避免阻塞主线程):

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {
    private SipManager sipManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 动态申请录音权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 1001);
        } else {
            initSipConnection();
        }
    }

    private void initSipConnection() {
        new Thread(() -> {
            try {
                sipManager = new SipManager();
                sipManager.initSipFramework();
                // 替换为你的SIP服务器信息
                sipManager.registerSipAccount(
                        "sip:test_user@192.168.1.100",
                        "test_user",
                        "test_pass",
                        "192.168.1.100"
                );
            } catch (Exception e) {
                e.printStackTrace();
                Log.e("SIP_ERROR", "连接失败:" + e.getMessage());
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (sipManager != null) sipManager.releaseSipResources();
    }
}
三、连接失败的常见排查要点
  • 库兼容性问题:如果编译时用了不匹配的NDK版本,或架构不对,会导致运行时崩溃或连接无响应,建议严格按照PJSIP官方推荐的NDK版本编译。
  • SIP参数错误:先拿第三方SIP客户端(如Linphone)测试账号是否能正常注册,排除服务器端问题。重点检查:SIP URI格式是否正确、服务器IP/端口是否开放、认证密码是否匹配。
  • 网络问题:真机测试确保和服务器在同一网络,或服务器有公网IP且开放5060 UDP端口;模拟器测试用10.0.2.2替代本地IP。
  • 日志分析:开启PJSIP详细日志,查看错误信息:401 Unauthorized是认证信息错误、Connection refused是服务器地址/端口错误、DNS resolution failed是域名解析问题。
  • Csipsimple适配坑:Csipsimple用的是旧版pjsua低级API,和现在推荐的pjsua2面向对象API写法差异很大,不要直接照搬代码,需适配pjsua2的逻辑。
四、额外调试技巧

用Wireshark抓包分析SIP消息,查看注册请求是否正常发送、服务器返回的响应码,这是定位问题的高效方法;同时捕获PjException,根据错误码查PJSIP官方文档的对应说明。

内容的提问来源于stack exchange,提问作者Dhananjay Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:37:22