接着第九章节分析aout输出组件模块的加载和实现分析。
有第九章节第2小节中可知,加载的模块名为【“audio output”】
通过全局搜索有如下android端支持组件模块:

// 第1种方式
audiotrack.c (vlc\modules\audio_output) line 183 :     set_capability( "audio output", 180 )// 第2种方式
// 将pcm源数据保存到文件中
file.c (vlc\modules\audio_output) line 127 :     set_capability( "audio output", 0 )// 第3种方式
opensles_android.c (vlc\modules\audio_output) line 139 :     set_capability("audio output", 170)

由上面可知,默认采用第一种audiotrack方式输出音频。
audiotrack方式的音频输出模块组件声明如下:

// [vlc/modules/audio_output/audiotrack.c]vlc_module_begin ()set_shortname( "AudioTrack" )set_description( "Android AudioTrack audio output" )set_capability( "audio output", 180 )set_category( CAT_AUDIO )set_subcategory( SUBCAT_AUDIO_AOUT )add_integer( "audiotrack-session-id", 0,AUDIOTRACK_SESSION_ID_TEXT, NULL, true )change_private()add_shortcut( "audiotrack" )set_callbacks( Open, Close )
vlc_module_end ()

1、组件初始化入口方法:

// [vlc/modules/audio_output/audiotrack.c]
static int
Open( vlc_object_t *obj )
{audio_output_t *p_aout = (audio_output_t *) obj;aout_sys_t *p_sys;// 获取JNI环境变量JNIEnv *env = GET_ENV();// 初始化audio相关的java类相关方法、变量常量等信息引用为JNI字段以便后续获取和调用if( !env || !InitJNIFields( p_aout, env ) )return VLC_EGENERIC;p_sys = calloc( 1, sizeof (aout_sys_t) );if( unlikely( p_sys == NULL ) )return VLC_ENOMEM;p_sys->at_dev = AT_DEV_DEFAULT;vlc_mutex_init(&p_sys->lock);vlc_cond_init(&p_sys->aout_cond);vlc_cond_init(&p_sys->thread_cond);p_aout->sys = p_sys;// 见第2小节分析p_aout->start = Start;// 见第3小节分析p_aout->stop = Stop;// 见第4小节分析p_aout->play = Play;// 见第5小节分析p_aout->pause = Pause;// 见第6小节分析p_aout->flush = Flush;// 见第7小节分析p_aout->time_get = TimeGet;// 见第8小节分析p_aout->device_select = DeviceSelect;for( unsigned int i = 0; at_devs[i].id; ++i )// 该方法内部调用了【aout->event.hotplug_report(aout, id, name);】,// 而该方法的赋值在第九章节第2小节分析中赋值的,// 其功能为:上报设备的热插拔事件给java层aout_HotplugReport(p_aout, at_devs[i].id, at_devs[i].name);// 调节音量功能// 见第9小节分析p_aout->volume_set = VolumeSet;// 设置静音功能// 见第10小节分析p_aout->mute_set = MuteSet;// 默认正常系统音量p_sys->volume = 1.0f;// 默认不为静音p_sys->mute = false;return VLC_SUCCESS;
}// [vlc/modules/audio_output/audiotrack.c]
// 初始化加载java类【"android/media/AudioTrack"】、【"android/media/AudioTimestamp"】、
//【"android/media/AudioSystem"】、【"android/media/AudioFormat"】、
//【"android/media/AudioManager"】所有相关的方法或变量常量等JNI字段引用
static bool
InitJNIFields( audio_output_t *p_aout, JNIEnv* env )
{static vlc_mutex_t lock = VLC_STATIC_MUTEX;static int i_init_state = -1;bool ret;jclass clazz;jfieldID field;vlc_mutex_lock( &lock );if( i_init_state != -1 )goto end;// 方法宏定义#define CHECK_EXCEPTION( what, critical ) do { \if( (*env)->ExceptionCheck( env ) ) \{ \msg_Err( p_aout, "%s failed", what ); \(*env)->ExceptionClear( env ); \if( (critical) ) \{ \i_init_state = 0; \goto end; \} \} \
} while( 0 )
#define GET_CLASS( str, critical ) do { \clazz = (*env)->FindClass( env, (str) ); \CHECK_EXCEPTION( "FindClass(" str ")", critical ); \
} while( 0 )
#define GET_ID( get, id, str, args, critical ) do { \jfields.id = (*env)->get( env, clazz, (str), (args) ); \CHECK_EXCEPTION( #get "(" #id ")", critical ); \
} while( 0 )
#define GET_CONST_INT( id, str, critical ) do { \field = NULL; \field = (*env)->GetStaticFieldID( env, clazz, (str), "I" ); \CHECK_EXCEPTION( "GetStaticFieldID(" #id ")", critical ); \if( field ) \{ \jfields.id = (*env)->GetStaticIntField( env, clazz, field ); \CHECK_EXCEPTION( #id, critical ); \} \
} while( 0 )/* AudioTrack class init */GET_CLASS( "android/media/AudioTrack", true );jfields.AudioTrack.clazz = (jclass) (*env)->NewGlobalRef( env, clazz );CHECK_EXCEPTION( "NewGlobalRef", true );GET_ID( GetMethodID, AudioTrack.ctor, "<init>", "(IIIIIII)V", true );GET_ID( GetMethodID, AudioTrack.release, "release", "()V", true );GET_ID( GetMethodID, AudioTrack.getState, "getState", "()I", true );GET_ID( GetMethodID, AudioTrack.play, "play", "()V", true );GET_ID( GetMethodID, AudioTrack.stop, "stop", "()V", true );GET_ID( GetMethodID, AudioTrack.flush, "flush", "()V", true );GET_ID( GetMethodID, AudioTrack.pause, "pause", "()V", true );GET_ID( GetMethodID, AudioTrack.writeV23, "write", "([BIII)I", false );GET_ID( GetMethodID, AudioTrack.writeShortV23, "write", "([SIII)I", false );// ...省略其他代码return ret;
}

2、Start实现分析:【该方法在第9行3.1小节分析过】

// [vlc/modules/audio_output/audiotrack.c]
static int
Start( audio_output_t *p_aout, audio_sample_format_t *restrict p_fmt )
{aout_sys_t *p_sys = p_aout->sys;JNIEnv *env;int i_ret;bool b_try_passthrough;unsigned i_max_channels;if( p_sys->at_dev == AT_DEV_ENCODED ){// 播放编码数据的设备?b_try_passthrough = true;i_max_channels = AT_DEV_MAX_CHANNELS;}else{b_try_passthrough = var_InheritBool( p_aout, "spdif" );// 双声道设备则最大声道数为2,否则默认最大为8i_max_channels = p_sys->at_dev == AT_DEV_STEREO ? 2 : AT_DEV_MAX_CHANNELS;}if( !( env = GET_ENV() ) )return VLC_EGENERIC;p_sys->fmt = *p_fmt;// 打印音频采样【样本】格式aout_FormatPrint( p_aout, "VLC is looking for:", &p_sys->fmt );// 低延迟标志bool low_latency = false;if (p_sys->fmt.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS){// 立体环绕声道类型// 设置为位图类型p_sys->fmt.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;// 探测输出声道为立体声道/* TODO: detect sink channel layout */p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;// 根据当前音频采样格式参数,初始化其音频通道(声道)数、量化精度【每个采样点使用的位数bits】、// 每压缩帧数据占字节数大小、每压缩帧数据包含的采样帧个数aout_FormatPrepare(&p_sys->fmt);// 标记该输出类型为低延迟low_latency = true;}// 该条件代码为宏定义,用于获取并判断音频量化精度是否不为0if( AOUT_FMT_LINEAR( &p_sys->fmt ) )// 不为0,执行开始PCM数据处理流程// 见2.1小节分析i_ret = StartPCM( env, p_aout, i_max_channels );else if( b_try_passthrough )// 否则执行编码音频流的输出给支持该格式的设备播放?i_ret = StartPassthrough( env, p_aout );elsereturn VLC_EGENERIC;if( i_ret != 0 )return VLC_EGENERIC;// 估算缓冲区buffer中audiotrack最大样本数p_sys->i_max_audiotrack_samples = BYTES_TO_FRAMES( p_sys->audiotrack_args.i_size );#ifdef AUDIOTRACK_HW_LATENCYif( jfields.AudioTimestamp.clazz ){// 创建java层【AudioTimestamp即当前播放时间】对象的JNi层对象引用/* create AudioTimestamp object */jobject p_obj = JNI_CALL( NewObject, jfields.AudioTimestamp.clazz,jfields.AudioTimestamp.ctor );if( p_obj ){p_sys->timestamp.p_obj = (*env)->NewGlobalRef( env, p_obj );(*env)->DeleteLocalRef( env, p_obj );}if( !p_sys->timestamp.p_obj )goto error;}
#endif// 重置AudioTrack平滑位置和时间戳位置AudioTrack_Reset( env, p_aout );// 获取写入数据类型if( p_sys->fmt.i_format == VLC_CODEC_FL32 ){msg_Dbg( p_aout, "using WRITE_FLOATARRAY");p_sys->i_write_type = WRITE_FLOATARRAY;}else if( p_sys->fmt.i_format == VLC_CODEC_SPDIFL ){assert( jfields.AudioFormat.has_ENCODING_IEC61937 );msg_Dbg( p_aout, "using WRITE_SHORTARRAYV23");p_sys->i_write_type = WRITE_SHORTARRAYV23;}else if( jfields.AudioTrack.writeV23 ){msg_Dbg( p_aout, "using WRITE_BYTEARRAYV23");p_sys->i_write_type = WRITE_BYTEARRAYV23;}else if( jfields.AudioTrack.writeBufferV21 ){msg_Dbg( p_aout, "using WRITE_BYTEBUFFER");p_sys->i_write_type = WRITE_BYTEBUFFER;}else{msg_Dbg( p_aout, "using WRITE_BYTEARRAY");p_sys->i_write_type = WRITE_BYTEARRAY;}p_sys->circular.i_read = p_sys->circular.i_write = 0;// 计算缓冲区循环buffer字节大小即计算码率大小:每个样本大小乘以采样率p_sys->circular.i_size = (int)p_sys->fmt.i_rate* p_sys->fmt.i_bytes_per_frame/ p_sys->fmt.i_frame_length;if (low_latency){// 由上面分析可知,立体环绕声道类型时进入,// 计算缓冲区循环buffer字节大小【码率大小】,可接受buffer数据缩小为40ms小的延迟(40毫秒内数据)/* 40 ms of buffering */p_sys->circular.i_size = p_sys->circular.i_size / 25;}else{// 默认缓冲区循环buffer字节大小【码率大小】可接受buffer数据扩大为2秒延迟/* 2 seconds of buffering */p_sys->circular.i_size = p_sys->circular.i_size * AOUT_MAX_PREPARE_TIME/ CLOCK_FREQ;}// 根据写入数据类型来分配缓冲区buffer内存/* Allocate circular buffer */switch( p_sys->i_write_type ){case WRITE_BYTEARRAY:case WRITE_BYTEARRAYV23:{jbyteArray p_bytearray;p_bytearray = (*env)->NewByteArray( env, p_sys->circular.i_size );if( p_bytearray ){p_sys->circular.u.p_bytearray = (*env)->NewGlobalRef( env, p_bytearray );(*env)->DeleteLocalRef( env, p_bytearray );}if( !p_sys->circular.u.p_bytearray ){msg_Err(p_aout, "byte array allocation failed");goto error;}break;}case WRITE_SHORTARRAYV23:{jshortArray p_shortarray;// short比byte(8位)长度多1倍,因此将比特码率减小一倍p_shortarray = (*env)->NewShortArray( env,p_sys->circular.i_size / 2 );if( p_shortarray ){p_sys->circular.u.p_shortarray = (*env)->NewGlobalRef( env, p_shortarray );(*env)->DeleteLocalRef( env, p_shortarray );}if( !p_sys->circular.u.p_shortarray ){msg_Err(p_aout, "short array allocation failed");goto error;}break;}case WRITE_FLOATARRAY:{jfloatArray p_floatarray;// float比byte(8位)长度多4倍,因此将比特码率减小一倍p_floatarray = (*env)->NewFloatArray( env,p_sys->circular.i_size / 4 );if( p_floatarray ){p_sys->circular.u.p_floatarray = (*env)->NewGlobalRef( env, p_floatarray );(*env)->DeleteLocalRef( env, p_floatarray );}if( !p_sys->circular.u.p_floatarray ){msg_Err(p_aout, "float array allocation failed");goto error;}break;}case WRITE_BYTEBUFFER:// p_data就是bytep_sys->circular.u.bytebuffer.p_data = malloc( p_sys->circular.i_size );if( !p_sys->circular.u.bytebuffer.p_data ){msg_Err(p_aout, "bytebuffer allocation failed");goto error;}break;}// 运行AudioTrack线程,并标记当前线程状态/* Run AudioTrack_Thread */p_sys->b_thread_running = true;p_sys->b_thread_paused = false;// AudioTrack_Thread线程实现见第11小节分析if ( vlc_clone( &p_sys->thread, AudioTrack_Thread, p_aout,VLC_THREAD_PRIORITY_LOW ) ){msg_Err(p_aout, "vlc clone failed");goto error;}// 调用AudioTrack的play()播放方法,等待写入播放数据进行播放JNI_AT_CALL_VOID( play );CHECK_AT_EXCEPTION( "play" );*p_fmt = p_sys->fmt;// 调节音量功能// 见第9小节分析    p_aout->volume_set(p_aout, p_sys->volume);if (p_sys->mute)// 需要静音则设置静音功能// 见第10小节分析p_aout->mute_set(p_aout, true);// 打印音频样本格式数据aout_FormatPrint( p_aout, "VLC will output:", &p_sys->fmt );return VLC_SUCCESS;error:// 见第3小节分析Stop( p_aout );return VLC_EGENERIC;
}

2.1、StartPCM实现分析:

// [vlc/modules/audio_output/audiotrack.c]
static int
StartPCM( JNIEnv *env, audio_output_t *p_aout, unsigned i_max_channels )
{aout_sys_t *p_sys = p_aout->sys;unsigned i_nb_channels;int i_at_format, i_ret;// 通过AudioTrack的getNativeOutputSampleRate静态方法获取音频采样率值// Music类型通道if (jfields.AudioTrack.getNativeOutputSampleRate)p_sys->fmt.i_rate =JNI_AT_CALL_STATIC_INT( getNativeOutputSampleRate,jfields.AudioManager.STREAM_MUSIC );else// 否则根据音频输入的采样率来适配,最低4KHz,最高48KHzp_sys->fmt.i_rate = VLC_CLIP( p_sys->fmt.i_rate, 4000, 48000 );do{// 注:仅处理的PCM位深为此四个值【U8, S16N, FL32, and AC3】/* We can only accept U8, S16N, FL32, and AC3 */switch( p_sys->fmt.i_format ){case VLC_CODEC_U8:i_at_format = jfields.AudioFormat.ENCODING_PCM_8BIT;break;case VLC_CODEC_S16N:i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;break;case VLC_CODEC_FL32:if( jfields.AudioFormat.has_ENCODING_PCM_FLOAT )i_at_format = jfields.AudioFormat.ENCODING_PCM_FLOAT;else{p_sys->fmt.i_format = VLC_CODEC_S16N;i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;}break;default:p_sys->fmt.i_format = VLC_CODEC_S16N;i_at_format = jfields.AudioFormat.ENCODING_PCM_16BIT;break;}// 注:android可能需要将不支持的声道类型降音混合处理为双声道类型进行播放/* Android AudioTrack supports only mono, stereo, 5.1 and 7.1.* Android will downmix to stereo if audio output doesn't handle 5.1 or 7.1*/// 获取声道数i_nb_channels = aout_FormatNbChannels( &p_sys->fmt );if( i_nb_channels == 0 )return VLC_EGENERIC;// 该条件代码为宏定义,用于获取并判断音频量化精度是否不为0    if( AOUT_FMT_LINEAR( &p_sys->fmt ) )// 选择最小的声道数作为目标声道类型i_nb_channels = __MIN( i_max_channels, i_nb_channels );if( i_nb_channels > 5 ){if( i_nb_channels > 7 && jfields.AudioFormat.has_CHANNEL_OUT_SIDE )// 若超出7声道数即8声道并且Audio格式有声道超出设置,则降为7.1物理声道模式p_sys->fmt.i_physical_channels = AOUT_CHANS_7_1;else// 否则降为5.1物理声道数p_sys->fmt.i_physical_channels = AOUT_CHANS_5_1;} else{if( i_nb_channels == 1 )// 若声道数为1,则处理为左声道p_sys->fmt.i_physical_channels = AOUT_CHAN_LEFT;else// 否则其他情况都默认处理为双声道模式p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;}// 注:尝试创建AudioTrack对象,若失败则可能通过调整PCM位深和声道数来重新尝试// 见下面的分析/* Try to create an AudioTrack with the most advanced channel and* format configuration. If AudioTrack_Create fails, try again with a* less advanced format (PCM S16N). If it fails again, try again with* Stereo channels. */i_ret = AudioTrack_Create( env, p_aout, p_sys->fmt.i_rate, i_at_format,p_sys->fmt.i_physical_channels );if( i_ret != 0 ){if( p_sys->fmt.i_format == VLC_CODEC_FL32 ){msg_Warn( p_aout, "FL32 configuration failed, ""fallback to S16N PCM" );p_sys->fmt.i_format = VLC_CODEC_S16N;}else if( p_sys->fmt.i_physical_channels & AOUT_CHANS_5_1 ){msg_Warn( p_aout, "5.1 or 7.1 configuration failed, ""fallback to Stereo" );p_sys->fmt.i_physical_channels = AOUT_CHANS_STEREO;}elsebreak;}} while( i_ret != 0 );if( i_ret != VLC_SUCCESS )return i_ret;uint32_t p_chans_out[AOUT_CHAN_MAX];memset( p_chans_out, 0, sizeof(p_chans_out) );// 多声道数时重排序各声道位置【FL FR FC LFE BL BR BC SL SR 】AudioTrack_GetChanOrder( p_sys->fmt.i_physical_channels, p_chans_out );// 检查channel表重排序p_sys->i_chans_to_reorder =aout_CheckChannelReorder( NULL, p_chans_out,p_sys->fmt.i_physical_channels,p_sys->p_chan_table );// 根据当前音频采样格式参数,初始化其音频通道(声道)数、量化精度【每个采样点使用的位数bits】、// 每压缩帧数据占字节数大小、每压缩帧数据包含的采样帧个数aout_FormatPrepare( &p_sys->fmt );return VLC_SUCCESS;
}// [vlc/modules/audio_output/audiotrack.c]
/*** Configure and create an Android AudioTrack.* returns -1 on configuration error, 0 on success.*/
static int
AudioTrack_Create( JNIEnv *env, audio_output_t *p_aout,unsigned int i_rate,int i_format,uint16_t i_physical_channels )
{aout_sys_t *p_sys = p_aout->sys;int i_size, i_min_buffer_size, i_channel_config;// 通道音频物理声道数获取java层AudioFormat类格式中声道数对应配置switch( i_physical_channels ){case AOUT_CHANS_7_1:/* bitmask of CHANNEL_OUT_7POINT1 doesn't correspond to 5POINT1 and* SIDES */i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1 |jfields.AudioFormat.CHANNEL_OUT_SIDE_LEFT |jfields.AudioFormat.CHANNEL_OUT_SIDE_RIGHT;break;case AOUT_CHANS_5_1:i_channel_config = jfields.AudioFormat.CHANNEL_OUT_5POINT1;break;case AOUT_CHAN_LEFT:i_channel_config = jfields.AudioFormat.CHANNEL_OUT_MONO;break;case AOUT_CHANS_STEREO:i_channel_config = jfields.AudioFormat.CHANNEL_OUT_STEREO;break;default:vlc_assert_unreachable();}// 通过调用java层AudioTrack的getMinBufferSize静态方法来获取估算的最小数据缓冲区buffer大小i_min_buffer_size = JNI_AT_CALL_STATIC_INT( getMinBufferSize, i_rate,i_channel_config, i_format );if( i_min_buffer_size <= 0 ){msg_Warn( p_aout, "getMinBufferSize returned an invalid size" ) ;return -1;}// 默认设置目标缓冲区大小为最小buffer缓冲区的两倍i_size = i_min_buffer_size * 2;// 创建java层AudioTrack对象的JNI层对应引用// 见下面分析/* create AudioTrack object */if( AudioTrack_New( env, p_aout, i_rate, i_channel_config,i_format , i_size ) != 0 )return -1;// 保存AudioTrack配置信息p_sys->audiotrack_args.i_rate = i_rate;p_sys->audiotrack_args.i_channel_config = i_channel_config;// PCM位深p_sys->audiotrack_args.i_format = i_format;p_sys->audiotrack_args.i_size = i_size;return 0;
}// [vlc/modules/audio_output/audiotrack.c]
/*** Create an Android AudioTrack.* returns -1 on error, 0 on success.*/
static int
AudioTrack_New( JNIEnv *env, audio_output_t *p_aout, unsigned int i_rate,int i_channel_config, int i_format, int i_size )
{aout_sys_t *p_sys = p_aout->sys;// 获取audiotrack session id值,由组件初始化可知该值默认为0jint session_id = var_InheritInteger( p_aout, "audiotrack-session-id" );// 根据配置信息创建java层AudioTrack类对象的JNI类型引用jobject p_audiotrack = JNI_AT_NEW( jfields.AudioManager.STREAM_MUSIC,i_rate, i_channel_config, i_format,i_size, jfields.AudioTrack.MODE_STREAM,session_id );if( CHECK_AT_EXCEPTION( "AudioTrack<init>" ) || !p_audiotrack ){msg_Warn( p_aout, "AudioTrack Init failed" ) ;return -1;}// 调用【AudioTrack.getState】获取AudioTrack状态if( JNI_CALL_INT( p_audiotrack, jfields.AudioTrack.getState )!= jfields.AudioTrack.STATE_INITIALIZED ){JNI_CALL_VOID( p_audiotrack, jfields.AudioTrack.release );(*env)->DeleteLocalRef( env, p_audiotrack );msg_Err( p_aout, "AudioTrack getState failed" );return -1;}// 重新创建AudioTrack局部引用的全局引用并保存,释放局部变量p_sys->p_audiotrack = (*env)->NewGlobalRef( env, p_audiotrack );(*env)->DeleteLocalRef( env, p_audiotrack );if( !p_sys->p_audiotrack )return -1;return 0;
}

由于当前章节内容分析非常多,将其分为了两部分分析发布,此为Part 1部分,Part 2部分的文章请查看如下:
【十四】【vlc-android】aout音频输出模块源码实现分析【Part 2】

【十四】【vlc-android】aout音频输出模块源码实现分析【Part 1】相关推荐

  1. 【十四】【vlc-android】aout音频输出模块源码实现分析【Part 2】

    该章节承接上一章节内容继续分析 上一章节:[十四][vlc-android]aout音频输出模块源码实现分析[Part 1] 3.Stop实现分析:[停止AudioTrack线程等相关操作] // [ ...

  2. Android 5.1 Settings源码简要分析

    概述: 先声明:本人工作快两年了,仍是菜鸟级别的,惭愧啊!以前遇到好多知识点都没有记录下来,感觉挺可惜的,现在有机会接触Android 源码.我们一个Android组的搞Setting,我觉得是得写得 ...

  3. Flink从入门到精通100篇(二十四)-对Flink SQL Client 源码做深度解析

    前言 本文基于 Flink 1.12-SNAPSHOT,使用sql client命令行提交insert语句进行整个流程的分析. sql-client.sh embedded --update &quo ...

  4. Android 4.0 Launcher源码详细分析 傻蛋

    http://wenku.baidu.com/view/80e280e2998fcc22bcd10dcd.html

  5. linux 音频播放器源码,Android音乐播放器源码

    相当完整的Android音乐播放器,直接上效果图及源代码,自己欣赏,具体不再解释了,可以说是一个很给力的Android音乐播放器. 示例代码: /* * Copyright (C) 2009 Tele ...

  6. 2021最强Android中高级大厂面试源码秘籍,为你备战2022金三银四,直通大厂

    11.LottieAndroid使用详解及源码解析 12.Fresco 源码分析--图片加载流程 13.rxandroid 源码解析 14.SlidingMenu源码解析 15.PhotoView源码 ...

  7. Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service

    上一章:Android 双开沙箱 VirtualApp 源码分析(三)App 启动 原生 Service 创建过程 首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA ...

  8. Android AOSP基础(四)Source Insight和Android Studio导入系统源码

    本文首发于微信公众号「刘望舒」 关联系列 Android AOSP基础系列 Android系统启动系列 前言 在上一篇文章Android AOSP基础(三)Android系统源码的整编和单编中,我们对 ...

  9. 【十五】【vlc-android】vlc-sout流媒体输出端源码实现分析【Part 3】【02】

    此章节分析承接上一章分析: [十五][vlc-android]vlc-sout流媒体输出端源码实现分析[Part 3][01] 1.1.2.1.net_ListenSingle实现分析:[本地IP地址 ...

最新文章

  1. 英语模板末尾【希望可以记着】
  2. hdu3336 KMP + DP 前缀数组出现的次数
  3. 指南:如何运用谷歌Google Shopping和Product Listing Ads
  4. 编程之美-蚂蚁爬杆方法整理
  5. 如何获取Oracle数据库中某表及索引、约束、触发器、对象权限的创
  6. C#中Lambda表达式类型Expression不接受lambda函数
  7. flask-session总结
  8. wifi协议_物联网网关智能家居工业4G路由器通用的物联网WiFi模块 MQTT/TCP协议 Linux嵌入式...
  9. 深挖android low memory killer
  10. Qt4.8编译MYSQL驱动
  11. python机器学习库xgboost——xgboost算法
  12. 局域网的分类:以太网、令牌环、FDDI、ATM、WLAN
  13. hssfrow 单元格样式_poi导出excel单元格中画斜线_AnyReport报表
  14. 天圆地方,物换星移. 北京天坛,先祖的祈福
  15. 基于snmp 交换机监控系统实现
  16. 【洛谷】洛谷深基学习记录 第二章 顺序结构程序设计
  17. linux系统笔记本如何投屏,Linux版手机投屏Qtscrcpy
  18. android pak文件_android 文件读写I/O 大集合 (持续更新)
  19. 【附源码】计算机毕业设计SSM天气预报系统
  20. 炼丹工程师的自我修养

热门文章

  1. Caché 命令大全
  2. 什么是绩效点、奖励加分、处罚扣分
  3. 《云边协同关键技术态势研究报告》丨附下载
  4. 发动机太热 请不要用冷水降温
  5. 录屏时如何录制麦克风声音?-QVE屏幕录制
  6. 在设计条形音箱时,确保您的无线技术能够提供最高质量的、可靠的音频
  7. 视频|《8问》浙江大学张宏鑫:边缘计算或许是区块链的福音
  8. texture中的 anisotropy属性,纹理的各向异性
  9. 如何设置计算机自动连接宽带,宽带自动连接设置,小编教你电脑怎么设置宽带自动连接...
  10. 拨号盘拨号数字间距太小 调大 修改通讯录里面收藏和所有联系人字体颜色