3. 权限与基础配置:AndroidManifest权限声明、Camera与Audio权限动态申请、应用级配置与初始化

各位同学,咱们今天聊一个绕不开的话题——权限与基础配置。

说实话,做WebRTC开发,权限这块儿是第一个坑。我当年第一次跑通视频通话,结果发现对方听不到声音,折腾了半天,最后发现是麦克风权限没申请。嗯,这种低级错误,我相信你们不会犯,但该走的流程咱们还是得走一遍。

3.1 AndroidManifest中的权限声明

先说说清单文件。AndroidManifest.xml 是咱们App的身份证,所有权限都得在这儿先声明。

WebRTC视频通话需要哪些权限?我列个清单:

  • CAMERA — 打开摄像头,拍你帅气的脸
  • RECORD_AUDIO — 录音,让对方听到你的声音
  • INTERNET — 网络通信,WebRTC的核心
  • ACCESS_NETWORK_STATE — 检查网络状态,判断连接质量
  • MODIFY_AUDIO_SETTINGS — 调整音频设置,比如扬声器切换
  • BLUETOOTH — 蓝牙耳机支持(这个很多人会漏掉)

代码长这样:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
注意:Android 6.0(API 23)以上,危险权限需要动态申请。CAMERA和RECORD_AUDIO都属于危险权限。光在清单里声明是不够的,运行时还得弹窗问用户。

另外,如果你的App只做视频通话,不需要拍照功能,可以加个uses-feature声明:

<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />

这样Google Play会帮你过滤掉没有摄像头的设备。嗯,虽然现在几乎没有手机没摄像头了,但平板、电视盒子之类的设备还是有的。

3.2 Camera与Audio权限动态申请

好,清单文件写完了,接下来是运行时申请。这部分我建议你封装成一个工具类,别到处写重复代码。

我个人习惯用这种方式:

public class PermissionUtils {

    private static final int PERMISSION_REQUEST_CODE = 1001;

    public static boolean hasPermissions(Context context) {
        return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) 
                == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) 
                == PackageManager.PERMISSION_GRANTED;
    }

    public static void requestPermissions(Activity activity) {
        ActivityCompat.requestPermissions(activity,
                new String[]{
                        Manifest.permission.CAMERA,
                        Manifest.permission.RECORD_AUDIO
                },
                PERMISSION_REQUEST_CODE);
    }
}

然后在Activity里处理回调:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == PERMISSION_REQUEST_CODE) {
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (allGranted) {
            // 权限已获取,开始初始化WebRTC
            initWebRTC();
        } else {
            // 权限被拒绝,提示用户
            showPermissionDeniedDialog();
        }
    }
}
小技巧:如果用户拒绝了权限,并且勾选了"不再询问",下次再申请就不会弹窗了。这时候你应该引导用户去设置里手动开启。我一般会弹个Dialog,里面写清楚为什么需要这些权限,再加个"去设置"的按钮。

为什么会这样?因为Android从6.0开始,权限机制变得很严格。用户拒绝一次后,如果你还反复弹窗,Google Play可能会给你差评。所以,做好引导很重要。

3.3 应用级配置与初始化

权限搞定了,接下来是初始化。WebRTC的初始化其实不复杂,但顺序很重要。

我一般在Application的onCreate里做全局初始化:

public class WebRTCApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // 初始化WebRTC
        initWebRTC();
    }

    private void initWebRTC() {
        // 1. 初始化PeerConnectionFactory
        PeerConnectionFactory.InitializationOptions initOptions =
                PeerConnectionFactory.InitializationOptions.builder(this)
                        .setFieldTrials(FieldTrials.DEFAULT)
                        .setEnableInternalTracer(true)
                        .createInitializationOptions();
        PeerConnectionFactory.initialize(initOptions);

        // 2. 创建PeerConnectionFactory实例
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        PeerConnectionFactory factory = PeerConnectionFactory.builder()
                .setOptions(options)
                .createPeerConnectionFactory();

        // 3. 保存到全局单例
        WebRTCManager.getInstance().setFactory(factory);
    }
}

这里有个细节——初始化顺序。我记得有一次在项目中,同事把PeerConnectionFactory.initialize()PeerConnectionFactory.builder()写反了,结果崩溃了一整天。你想想看,初始化都没完成,你怎么创建工厂?

所以,记住这个顺序:

  1. 先调用PeerConnectionFactory.initialize()
  2. 再调用PeerConnectionFactory.builder().createPeerConnectionFactory()

另外,关于音频配置,我建议在初始化时就把音频模式设置好:

// 设置音频配置
AudioConstraints audioConstraints = new AudioConstraints();
audioConstraints.autoGainControl = true;
audioConstraints.echoCancellation = true;
audioConstraints.noiseSuppression = true;

这三个参数什么意思?

  • autoGainControl:自动增益控制。说白了就是让麦克风自动调节音量,避免你说话声音忽大忽小。
  • echoCancellation:回声消除。这个很重要,不然对方会听到自己的回声。
  • noiseSuppression:噪声抑制。过滤掉背景噪音,比如风扇声、键盘声。

我曾经在一个项目中,用户反馈说通话有回声,排查了半天,最后发现是echoCancellation没开。嗯,从那以后我每次初始化都会检查这个参数。

3.4 知识体系总览

下面这张图,帮你理清本章的知识结构:

权限与基础配置知识体系 AndroidManifest声明 运行时动态申请 应用级初始化 CAMERA / RECORD_AUDIO INTERNET / 网络状态 MODIFY_AUDIO_SETTINGS BLUETOOTH(蓝牙支持) checkSelfPermission 检查 requestPermissions 申请 onRequestPermissionsResult 拒绝后引导至设置页 PeerConnectionFactory初始化 AudioConstraints配置 回声消除 / 降噪 / 增益 全局单例管理Factory 权限是基础,配置是骨架,两者缺一不可

3.5 避坑指南

最后,分享几个我踩过的坑:

坑1:权限申请后不检查结果

我曾经见过有人直接调用requestPermissions,然后不管结果就开始初始化WebRTC。结果呢?摄像头打不开,音频录不了,整个通话都是黑的。所以,一定要在onRequestPermissionsResult里检查grantResults。

坑2:在子线程初始化PeerConnectionFactory

PeerConnectionFactory的初始化必须在主线程。如果你在子线程里调,会直接崩溃。我刚开始做的时候也犯过这个错,后来养成了习惯,所有WebRTC初始化都放在主线程。

坑3:忘记释放资源

WebRTC的PeerConnectionFactory、PeerConnection、VideoSource这些都是重量级对象。不用了记得释放,不然内存泄漏等着你。我一般在Activity的onDestroy里做清理。

好了,这一章的内容就这些。权限和配置是WebRTC开发的第一步,也是最重要的一步。把这些基础打牢了,后面的开发才会顺风顺水。


公众号:蓝海资料掘金营,微信deep3321