編輯:關於Android編程
一、ToneGenerator的使用
參照com.android.contacts.dialpad.DialpadFragment
ToneGenerator只能播放在ToneGenerator中定義好的TONE_TYPE。
1、常量申明
[java] /** Tone音的長度,單位:milliseconds */
private static final int TONE_LENGTH_MS = 150;
/** 主音量的比例:以80%的主音量播放Tone音 */
private static final int TONE_RELATIVE_VOLUME = 80;
/** 主音量的音頻種別 */
private static final int DIAL_TONE_STREAM_TYPE =AudioManager.STREAM_MUSIC;
/** Tone音的長度,單位:milliseconds */
private static final int TONE_LENGTH_MS = 150;
/** 主音量的比例:以80%的主音量播放Tone音 */
private static final int TONE_RELATIVE_VOLUME = 80;
/** 主音量的音頻種別 */
private static final int DIAL_TONE_STREAM_TYPE =AudioManager.STREAM_MUSIC;
2、變量申明
[java] // Tone音播放器
private ToneGenerator mToneGenerator;
// Tone相關的同步鎖
private Object mToneGeneratorLock = new Object();
// 設定中的Tone音播放設置
private boolean mDTMFToneEnabled;
// Tone音播放器
private ToneGenerator mToneGenerator;
// Tone相關的同步鎖
private Object mToneGeneratorLock = new Object();
// 設定中的Tone音播放設置
private boolean mDTMFToneEnabled;
3、Tone的初始化
[java] public void onResume() {
super.onResume();
// 讀取設定的值
mDTMFToneEnabled =Settings.System.getInt(getActivity().getContentResolver(),
Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
// 失敗了也無所謂,不是啥重要的東西
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
try {
// we want the user to be ableto control the volume of the dial tones
// outside of a call, so we usethe stream type that is also mapped to the
// volume control keys for thisactivity
mToneGenerator = newToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
getActivity().setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
} catch (RuntimeException e) {
Log.w(TAG, "Exceptioncaught while creating local tone generator: " + e);
mToneGenerator = null;
}
}
}
}
public void onResume() {
super.onResume();
// 讀取設定的值
mDTMFToneEnabled =Settings.System.getInt(getActivity().getContentResolver(),
Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
// 失敗了也無所謂,不是啥重要的東西
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
try {
// we want the user to be ableto control the volume of the dial tones
// outside of a call, so we usethe stream type that is also mapped to the
// volume control keys for thisactivity
mToneGenerator = newToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
getActivity().setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
} catch (RuntimeException e) {
Log.w(TAG, "Exceptioncaught while creating local tone generator: " + e);
mToneGenerator = null;
}
}
}
}
4、釋放Tone資源
[java] public voidonPause() {
super.onPause();
synchronized (mToneGeneratorLock) {
if (mToneGenerator != null) {
mToneGenerator.release();
mToneGenerator = null;
}
}
}
public voidonPause() {
super.onPause();
synchronized (mToneGeneratorLock) {
if (mToneGenerator != null) {
mToneGenerator.release();
mToneGenerator = null;
}
}
}
5、播放Tone音
[java] /**
* 播放TONE_LENGTH_MS milliseconds的Tone音.
* 只有在設定中選擇了播放Tone,並且不是靜音模式才會播放Tone音。
* @param tone a tone code from {@linkToneGenerator}
*/
void playTone(int tone) {
// 設定中沒有選中的話,就不播
if (!mDTMFToneEnabled) {
return;
}
// 靜音模式的時候也不播,需要每次都檢查,因為沒有Activity切換也能設成靜音模式
// 設定中的那個就不需要,因為要設定必須要先切入設定Activity才行
AudioManager audioManager =
(AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE);
int ringerMode =audioManager.getRingerMode();
if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
|| (ringerMode ==AudioManager.RINGER_MODE_VIBRATE)) {
return;
}
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
Log.w(TAG, "playTone:mToneGenerator == null, tone: " + tone);
return;
}
// Start the new tone (will stop anyplaying tone)
mToneGenerator.startTone(tone,TONE_LENGTH_MS);
}
}
/**
* 播放TONE_LENGTH_MS milliseconds的Tone音.
* 只有在設定中選擇了播放Tone,並且不是靜音模式才會播放Tone音。
* @param tone a tone code from {@linkToneGenerator}
*/
void playTone(int tone) {
// 設定中沒有選中的話,就不播
if (!mDTMFToneEnabled) {
return;
}
// 靜音模式的時候也不播,需要每次都檢查,因為沒有Activity切換也能設成靜音模式
// 設定中的那個就不需要,因為要設定必須要先切入設定Activity才行
AudioManager audioManager =
(AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE);
int ringerMode =audioManager.getRingerMode();
if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
|| (ringerMode ==AudioManager.RINGER_MODE_VIBRATE)) {
return;
}
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
Log.w(TAG, "playTone:mToneGenerator == null, tone: " + tone);
return;
}
// Start the new tone (will stop anyplaying tone)
mToneGenerator.startTone(tone,TONE_LENGTH_MS);
}
}
二、ToneGenerator的實現
相關代碼位置:
ToneGenerator.java:ICS/frameworks/base/media/java/
Android_media_ToneGenerator.cpp:ICS/frameworks/base/core/jni/
ToneGenerator.cpp:ICS/frameworks/base/media/libmedia/
1、ToneGenerator.java
定義了多種ToneType,提供了java的接口
2、Android_media_ToneGenerator.cpp
將Java層的請求轉發給Native層。
android_media_ToneGenerator_native_setup中的有句話看不懂:
[java] ToneGenerator *lpToneGen= new ToneGenerator(streamType, <STRONG>AudioSystem::linearToLog(volume)</STRONG>,true);
// change this value tochange volume scaling
static const float dBPerStep= 0.5f;
// shouldn't need totouch these
static const floatdBConvert = -dBPerStep * 2.302585093f / 20.0f;
static const floatdBConvertInverse = 1.0f / dBConvert;
floatAudioSystem::linearToLog(int volume)
{
// float v = volume ? exp(float(100 -volume) * dBConvert) : 0;
// LOGD("linearToLog(%d)=%f",volume, v);
// return v;
return volume ? exp(float(100 - volume) *dBConvert) : 0;
}
ToneGenerator *lpToneGen= new ToneGenerator(streamType, AudioSystem::linearToLog(volume),true);
// change this value tochange volume scaling
static const float dBPerStep= 0.5f;
// shouldn't need totouch these
static const floatdBConvert = -dBPerStep * 2.302585093f / 20.0f;
static const floatdBConvertInverse = 1.0f / dBConvert;
floatAudioSystem::linearToLog(int volume)
{
// float v = volume ? exp(float(100 -volume) * dBConvert) : 0;
// LOGD("linearToLog(%d)=%f",volume, v);
// return v;
return volume ? exp(float(100 - volume) *dBConvert) : 0;
}
算出來的值會直接設到AudioTrack中,可是AudioTrack中的音量應該是個0~1.0f的百分比才對,為啥需要這麼個公式呢,難道是Bug。應該測試一下!!
3、ToneGenerator.cpp
根據定義的Tone因的頻率,長度等信息生成音頻數據,最後交給AudioTrack播放。
三、AudioPolicyService中的mTonePlaybackThread
本來以為這個線程是專門處理Tone音設備的,可是根據上面一看原來是直接走AudioTrack的。這就奇怪了,並且AudioSystem中也沒有提供對應的接口,這就更奇怪了,難道它沒准備讓外面的人用。再一檢索,發現原來它是提供給AudioPolicyManagerBase使用的一個非同期播放Tone音的接口。
經過AudioCommandThread的處理,最終還是交給ToneGenerator來處理。
四、RingtoneManager與Ringtone
播放鈴聲的類,沒具體看,最後是通過MediaPlayer來播放的。
前言我們開發人員在實際項目過程中遇到的需求是多種多樣的,有時我們要匹配APP自己的設計風格,有時我們會覺得系統的對話框使用起來不夠自由,因此自己定義一個適合自己的Dial
一、源代碼源代碼及demo二、背景先看看Win10的加載動畫(找了很久才找到):每次打開電腦都會有這個加載動畫,看上挺cool的,就想著自己能否實現它。要實現這個動畫?首
背景Android是基於Linux的操作系統,在其中運行的應用或者系統服務,實際上就是一個個Linux進程。這意味著它們彼此之間是隔離的,必須通過進程間通信(IPC)來相
BinaryTree線索化二叉樹>二叉樹是一種非線性結構,在之前實現的二叉樹遍歷中不管是遞歸還是非遞歸用二叉樹作為存儲結構時只能取到該結點的左孩子和右孩子,不能得到