我们在使用WebRTC Android native SDK进行开发的时候,PeerConnection与PeerConnectionFactory是两个再熟悉不过的类了。他们的源码分别位于:

src\sdk\android\api\org\webrtc\PeerConnectionFactory.java
src\sdk\android\api\org\webrtc\PeerConnection.java

这两个类担负着所有和服务器建立连接的初始工作,以及LocalMediaStream、A/V Track、A/V Source等重要对象的创建。这里主要是简单描述一下PeerConnection(以下简称PC)和PeerConnectionFactory(以下简称PCF)的释放。

在描述释放过程之前,我们先来看一下PC和PCF的创建过程。首先,让我们从WebRTC自带的世界著名的例子程序AppRTC的源码开始,它的Android版本位于:

src\examples\androidapp\

其中,几乎所有的有关连接、媒体的关键代码,都在PeerConnectionClient.java中可以找到。它有一个成员变量,是这样的:

  // Executor thread is started once in private ctor and is used for all  // peer connection API calls to ensure new peer connection factory is  // created on the same thread as previously destroyed factory.  private static final ExecutorService executor = Executors.newSingleThreadExecutor();

所以我们看到,无论是创建PCF的方法 createPeerConnectionFactoryInternal() 还是创建PC的方法createPeerConnectionInternal(),都是通过调用 executor.execute() 来执行的,这样可以保证他们工作在同一个线程。

OK,让我们看看PCF和PC的创建内部。注:以下代码来源于WebRTC branch-heads/m73,因WebRTC代码变化较快,你看到的可能跟我的不是相同版本。

createPeerConnectionFactory() & PCF.createPeerConnection()

public PeerConnectionFactory createPeerConnectionFactory() {      checkInitializeHasBeenCalled();      return nativeCreatePeerConnectionFactory(ContextUtils.getApplicationContext(), options,          audioDeviceModule == null ? 0 : audioDeviceModule.getNativeAudioDeviceModulePointer(),          audioEncoderFactoryFactory.createNativeAudioEncoderFactory(),          audioDecoderFactoryFactory.createNativeAudioDecoderFactory(), videoEncoderFactory,          videoDecoderFactory,          audioProcessingFactory == null ? 0 : audioProcessingFactory.createNative(),          fecControllerFactoryFactory == null ? 0 : fecControllerFactoryFactory.createNative(),          mediaTransportFactoryFactory == null              ? 0              : mediaTransportFactoryFactory.createNativeMediaTransportFactory());    }
/**   * Internal helper function to pass the parameters down into the native JNI bridge.   */  @Nullable  PeerConnection createPeerConnectionInternal(PeerConnection.RTCConfiguration rtcConfig,      MediaConstraints constraints, PeerConnection.Observer observer,      SSLCertificateVerifier sslCertificateVerifier) {    checkPeerConnectionFactoryExists();    long nativeObserver = PeerConnection.createNativePeerConnectionObserver(observer);    if (nativeObserver == 0) {      return null;    }    long nativePeerConnection = nativeCreatePeerConnection(        nativeFactory, rtcConfig, constraints, nativeObserver, sslCertificateVerifier);    if (nativePeerConnection == 0) {      return null;    }    return new PeerConnection(nativePeerConnection);  }

显而易见,我们需要离开Java,走向JNI的世界:

src\sdk\android\src\jni\pc\peer_connection_factory.cc

static ScopedJavaLocalRefJNI_PeerConnectionFactory_CreatePeerConnectionFactory(    JNIEnv* jni,    const JavaParamRef& jcontext,    const JavaParamRef& joptions,    jlong native_audio_device_module,    jlong native_audio_encoder_factory,    jlong native_audio_decoder_factory,    const JavaParamRef& jencoder_factory,    const JavaParamRef& jdecoder_factory,    jlong native_audio_processor,    jlong native_fec_controller_factory,    jlong native_media_transport_factory) {  rtc::scoped_refptr audio_processor =      reinterpret_cast(native_audio_processor);  return CreatePeerConnectionFactoryForJava(      jni, jcontext, joptions,      reinterpret_cast(native_audio_device_module),      TakeOwnershipOfRefPtr(native_audio_encoder_factory),      TakeOwnershipOfRefPtr(native_audio_decoder_factory),      jencoder_factory, jdecoder_factory,      audio_processor ? audio_processor : CreateAudioProcessing(),      TakeOwnershipOfUniquePtr(          native_fec_controller_factory),      TakeOwnershipOfUniquePtr(          native_media_transport_factory));}

具体源码就不详细写了,代码比较清晰。其中它会创建3个重要native线程:NetworkThread、SignalingThread、WorkerThread,这3个线程自始至终都在底层默默地工作着,为我们的App提供各种服务。当然还有其他一些重要的Factory对象会创建出来,各负其责。当其就绪的时候,也会再通知到Java层。例如当WorkerThread就绪的时候,PeerConnectionFactory.java会收到onWorkerThreadReady()通知(这个是通过peer_connection_factory.cc 里的 NativeToScopedJavaPeerConnectionFactory方法实现的)。

static jlong JNI_PeerConnectionFactory_CreatePeerConnection(    JNIEnv* jni,    jlong factory,    const JavaParamRef& j_rtc_config,    const JavaParamRef& j_constraints,    jlong observer_p,    const JavaParamRef& j_sslCertificateVerifier) {    //代码太长就不贴了}

OK,PCF和PC的创建过程大致如此。那么来看本文的正题,即PCF和PC的释放。

有了上面的介绍,释放的代码就比较好找了:

PC的释放:

public void close() {    nativeClose();  }/**   * Free native resources associated with this PeerConnection instance.   *   * This method removes a reference count from the C++ PeerConnection object,   * which should result in it being destroyed. It also calls equivalent   * "dispose" methods on the Java objects attached to this PeerConnection   * (streams, senders, receivers), such that their associated C++ objects   * will also be destroyed.   *   * 

Note that this method cannot be safely called from an observer callback * (PeerConnection.Observer, DataChannel.Observer, etc.). If you want to, for * example, destroy the PeerConnection after an "ICE failed" callback, you * must do this asynchronously (in other words, unwind the stack first). See * bug * 3721 for more details. */ public void dispose() { close(); for (MediaStream stream : localStreams) { nativeRemoveLocalStream(stream.getNativeMediaStream()); stream.dispose(); } localStreams.clear(); for (RtpSender sender : senders) { sender.dispose(); } senders.clear(); for (RtpReceiver receiver : receivers) { receiver.dispose(); } for (RtpTransceiver transceiver : transceivers) { transceiver.dispose(); } transceivers.clear(); receivers.clear(); nativeFreeOwnedPeerConnection(nativePeerConnection); }

注意,PC的dispose()会执行自动调用 nativeClose()、LocalMediaStream.dispose()、RtpSender.dispose()、RtpReceiver.dispose()、RtpTransceiver.dispose()其中,MediaStream.dispose()还会继续释放掉所有AudioTracks、VideoTracks及其持有的nativeStream对象,同样地,RtpSender、RtpReceiver、RtpTransceiver等都是类似。而如果你的代码中,有其他地方使用了这些对象,一定要意识到,调用了PC.dispose(),这些对象都不存在了,避免再使用它们中的任意一个而产生异常调用。我就曾经因为这个原因,踩了不少坑。

PC.dispose()的native调用,参考 src\sdk\android\src\jni\pc\peer_connection.cc 里面的实现,代码就不贴了。

PCF的释放:

public void dispose() {    checkPeerConnectionFactoryExists();    nativeFreeFactory(nativeFactory);    networkThread = null;    workerThread = null;    signalingThread = null;    MediaCodecVideoEncoder.disposeEglContext();    MediaCodecVideoDecoder.disposeEglContext();    nativeFactory = 0;  }

nativeFreeFactory() 对应的JNI代码位于src\sdk\android\src\jni\pc\peer_connection_factory.cc:

static void JNI_PeerConnectionFactory_FreeFactory(JNIEnv*,                                                  jlong j_p) {  delete reinterpret_cast(j_p);  field_trial::InitFieldTrialsFromString(nullptr);  GetStaticObjects().field_trials_init_string = nullptr;}

实现很简单,但我曾经遇到过在执行 delete reinterpret_cast(j_p); 产生随机性崩溃的问题,也是因为Java层对native对象持有以及释放顺序不正确导致的。

网上还有一些关于PC.dispose()和PCF.dispose()会引起crash的问题描述,例如:

https://stackoverflow.com/questions/36191282/is-a-particular-threading-model-required-for-webrtc-native-android-app

他遇到的问题原因主要是使用了一个内部类来同时实现了 SDPObserver、PCObserver以及DataObserver。

最后,回到本文一开始引用AppRtc例子中的executor的目的,就是提醒大家,在dispose的时候,也一定要保持在相同线程进行,否则也有可能产生意想不到的后果。

总结一下,释放的关键点:

  • 同一个线程中创建及销毁
  • PC的销毁会自动释放大量的对象,不要在dispose()后再使用它们,尤其在多线程环境中
  • 实际应用中,对象模型往往是很复杂的,不会像AppRtc例子那么简单。但无论多么复杂,一定要保证释放顺序不要弄错:可以参考AppRtc例子中PeerConnectionClient.java 的 close()方法。
  • 关注Java持有的native对象的生命周期,如果发生native层crash,先找Java层的原因

 

 

 

 

 

 

更多相关文章

  1. 关于Android多项目依赖在Eclipse中无法关联源代码的问题解决 (an
  2. Android 7.1 触摸事件代码跟踪
  3. android 开发中将十六进制 颜色代码 转换为int类型数值 方法 :
  4. TextView --- 内容设置成上下滑动 和 代码设置字体颜色
  5. android微博客户端源代码
  6. Android 登录界面xml代码
  7. android 正三角,倒三角的实现代码
  8. Android上积累代码
  9. Android实用代码

随机推荐

  1. SAX进行实体解析XML(android)
  2. GestureDetector.OnGestureListener 详解
  3. Android ListView几个特别的属性
  4. Android 蓝牙知识
  5. Android环境离线安装--极速版
  6. android 源码下载错误: [Errno 110] Conne
  7. Android职业规划
  8. Android 属性动画的原理分析
  9. Android系统在新进程中启动自定义服务过
  10. android xml使用pull解析