成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

淺談Android O Notification聲音播放流程

huhud / 1043人閱讀

摘要:在線程中,在中創(chuàng)建線程在線程中通過(guò)播放饒了一大圈竟然也用來(lái)播放就是從傳下來(lái)的需要注意,通知音是會(huì)申請(qǐng)焦點(diǎn)的??偨Y(jié)從創(chuàng)建到播放的流程基本就這樣,至于聲音的區(qū)分是否電話中,如果則使用播放,反之播放。

前言

我們?cè)谧鯝ndroid開發(fā)的時(shí)候,免不了會(huì)使用到Notification,而且在android設(shè)備的設(shè)置中還可以設(shè)置通知音的優(yōu)先級(jí),以及播放的聲音種類。那么通知音是如何播放的呢,今天我們就來(lái)談?wù)勥@個(gè)。

Notification的使用
NotificationManager notificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        //重點(diǎn):先創(chuàng)建通知渠道
        if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.O){
    NotificationChannel mChannel=new NotificationChannel(getString(R.string.app_name),getString(R.string.app_name),NotificationManager.IMPORTANCE_MAX);
        NotificationChannel channel=new NotificationChannel(channelId,
        channelName,NotificationManager.IMPORTANCE_DEFAULT);
        channel.enableLights(true); //設(shè)置開啟指示燈,如果設(shè)備有的話
        channel.setLightColor(Color.RED); //設(shè)置指示燈顏色
        channel.setShowBadge(true); //設(shè)置是否顯示角標(biāo)
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);//設(shè)置是否應(yīng)在鎖定屏幕上顯示此頻道的通知
        channel.setDescription(channelDescription);//設(shè)置渠道描述
        channel.setVibrationPattern(new long[]{100,200,300,400,500,600});//設(shè)置震動(dòng)頻率
        channel.setBypassDnd(true);//設(shè)置是否繞過(guò)免打擾模式
        notificationManager.createNotificationChannel(mChannel);
        }

        //再創(chuàng)建通知
        NotificationCompat.Builder builder=new NotificationCompat.Builder(this,getString(R.string.app_name));
        //設(shè)置通知欄大圖標(biāo),上圖中右邊的大圖
        builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
        // 設(shè)置狀態(tài)欄和通知欄小圖標(biāo)
        .setSmallIcon(R.drawable.ic_launcher_background)
        // 設(shè)置通知欄應(yīng)用名稱
        .setTicker("通知欄應(yīng)用名稱")
        // 設(shè)置通知欄顯示時(shí)間
        .setWhen(System.currentTimeMillis())
        // 設(shè)置通知欄標(biāo)題
        .setContentTitle("通知欄標(biāo)題")
        // 設(shè)置通知欄內(nèi)容
        .setContentText("通知欄內(nèi)")
        // 設(shè)置通知欄點(diǎn)擊后是否清除,設(shè)置為true,當(dāng)點(diǎn)擊此通知欄后,它會(huì)自動(dòng)消失
        .setAutoCancel(false)
        // 將Ongoing設(shè)為true 那么左滑右滑將不能刪除通知欄
        .setOngoing(true)
        // 設(shè)置通知欄點(diǎn)擊意圖
        .setContentIntent(pendingIntent)
        // 鈴聲、閃光、震動(dòng)均系統(tǒng)默認(rèn)
        .setDefaults(Notification.DEFAULT_ALL)
        //設(shè)置通知時(shí)間
         .setWhen(System.currentTimeMillis())
        // 設(shè)置為public后,通知欄將在鎖屏界面顯示
        .setVisibility(NotificationCompat.VISIBILITY_PRIVATE);
        //發(fā)送通知
         notificationManager.notify(10, builder.build());

ANdroid O主要增加了NotificationChannel,詳細(xì)用法可參照其API。

正文

那么就從發(fā)送通知的notify開始入手

    public void notify(String tag, int id, Notification notification)
    {
    //當(dāng)我們調(diào)用Notification的notify()發(fā)送通知時(shí),會(huì)繼續(xù)調(diào)到notifyAsUser
       notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
   }
 public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
    {
        //得到NotificationManagerService
        INotificationManager service = getService();

        //…………
        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        boolean isLowRam = am.isLowRamDevice();
        final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
        try {
        //把Nofitication copy到了NofificationManagerservie中
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    copy, user.getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
       }
}

而enqueueNotificationWithTag()又調(diào)用了 enqueueNotificationInternal()
在enqueueNotificationInternal()中需要注意下面這行代碼:

 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);

我們來(lái)看下NotificationRecord的初始化,在NotificationRecoder的構(gòu)造方法中

mAttributes = calculateAttributes();

mAttributes 是不是很熟悉,就是audio播放時(shí)需要傳入的那個(gè)AudioAttributes,在

calculateAttributes()中會(huì)取我們?cè)趧?chuàng)建channel時(shí)傳入的AudioAttributes,如果沒(méi)有則使用默認(rèn)的,如果ANdroid O之前的版本,沒(méi)有channel,則會(huì)使用Notification中默認(rèn)的AudioAttributes,(NotificationChannel中的AudioAttributes是通過(guò)setSounde()方法設(shè)置下來(lái)的)。默認(rèn)的AudioAttributes
是什么呢?就是

   //通知中默認(rèn)的AudioAttributes 
   public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .setUsage(AudioAttributes.USAGE_NOTIFICATION)
            .build();

在enqueueNotificationInternal()中有將notificationRecord放到了EnqueueNotificationRunnable中線程運(yùn)行代碼如下:

mHandler.post(new EnqueueNotificationRunnable(userId, r));

而在EnqueueNotificationRunnable線程中又調(diào)用的PostNotificationRunnable線程中執(zhí)行,代碼如下

mHandler.post(new PostNotificationRunnable(r.getKey()));

在PostNotificationRunnable中 通過(guò)buzzBeepBlinkLocked(r)方法播放

if (hasValidSound) {
                        mSoundNotificationKey = key;
                        //如果電話中則playInCallNotification()
                        if (mInCall) {
                            playInCallNotification();                            
                            beep = true;
                        } else {
                        //否則調(diào)用playSound(),
                            beep = playSound(record, soundUri);
                        }
                    }

無(wú)論哪個(gè)方法方法都是一樣的,只是 AudioAttributes不同,playInCallNotification()使用的mInCallNotificationAudioAttributes即

       mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                .build();

而playSound()使用的AudioAttributes如果未通過(guò)channel傳入,則使用上面提到的默認(rèn)的,那么看看到底是如何播放的呢,通知音的播放是通過(guò)

 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();

來(lái)播放的,代碼略過(guò),簡(jiǎn)單說(shuō)下RingTonePlayer的play()和playerAsync()這倆方法,還是有點(diǎn)區(qū)別的,play使用的Ringtone來(lái)播放的,源碼:

 client.mRingtone.setLooping(looping);
            client.mRingtone.setVolume(volume);
            client.mRingtone.play();

而playerAsync()是通過(guò)NotificationPlayer來(lái)播放的,對(duì)于NotificationPlayer的play()會(huì)通過(guò)enqueueLocked()創(chuàng)建CmdThread線程。在CmdThread線程中startSound(),在startSound()中創(chuàng)建CreationAndCompletionThread線程

                mCompletionThread = new CreationAndCompletionThread(cmd);
                synchronized (mCompletionThread) {
                  mCompletionThread.start();
                    mCompletionThread.wait();
                }

在CreationAndCompletionThread線程中通過(guò)mediaplayer播放

  private final class CreationAndCompletionThread extends Thread {
        public Command mCmd;
        public CreationAndCompletionThread(Command cmd) {
            super();
            mCmd = cmd;
        }

        public void run() {
            Looper.prepare();
            // ok to modify mLooper as here we are
            // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
            mLooper = Looper.myLooper();
            if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
            synchronized(this) {
                AudioManager audioManager =
                    (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
                try {
                    //饒了一大圈竟然也用mediaplayer來(lái)播放
                    MediaPlayer player = new MediaPlayer();
                    //attributes 就是從NotificationChannel傳下來(lái)的attributes 
                    if (mCmd.attributes == null) {
                        mCmd.attributes = new AudioAttributes.Builder()
                                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                                .build();
                    }
                    player.setAudioAttributes(mCmd.attributes);
                    player.setDataSource(mCmd.context, mCmd.uri);
                    player.setLooping(mCmd.looping);
                    player.setOnCompletionListener(NotificationPlayer.this);
                    player.setOnErrorListener(NotificationPlayer.this);
                    player.prepare();
                    if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
                            && (mCmd.uri.getEncodedPath().length() > 0)) {
                        if (!audioManager.isMusicActiveRemotely()) {
                            synchronized (mQueueAudioFocusLock) {
                                if (mAudioManagerWithAudioFocus == null) {
                                    if (DEBUG) Log.d(mTag, "requesting AudioFocus");
                                    int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
                                    if (mCmd.looping) {
                                        focusGain = AudioManager.AUDIOFOCUS_GAIN;
                                    }
                                    mNotificationRampTimeMs = audioManager.getFocusRampTimeMs(
                                            focusGain, mCmd.attributes);
                                     //需要注意i,通知音是會(huì)申請(qǐng)焦點(diǎn)的。
                                    audioManager.requestAudioFocus(null, mCmd.attributes,
                                                focusGain, 0);
                                    mAudioManagerWithAudioFocus = audioManager;
                                } else {
                                    if (DEBUG) Log.d(mTag, "AudioFocus was previously requested");
                                }
                            }
                        }
                    }
                    // FIXME Having to start a new thread so we can receive completion callbacks
                    //  is wrong, as we kill this thread whenever a new sound is to be played. This
                    //  can lead to AudioFocus being released too early, before the second sound is
                    //  done playing. This class should be modified to use a single thread, on which
                    //  command are issued, and on which it receives the completion callbacks.
                    if (DEBUG)  { Log.d(mTag, "notification will be delayed by "
                            + mNotificationRampTimeMs + "ms"); }
                    try {
                        Thread.sleep(mNotificationRampTimeMs);
                        player.start();
                    } catch (InterruptedException e) {
                        Log.e(mTag, "Exception while sleeping to sync notification playback"
                                + " with ducking", e);
                    }
                    if (DEBUG) { Log.d(mTag, "player.start"); }
                    if (mPlayer != null) {
                        if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
                        mPlayer.release();
                    }
                    mPlayer = player;
                }
                catch (Exception e) {
                    Log.w(mTag, "error loading sound for " + mCmd.uri, e);
                }
                this.notify();
            }
            Looper.loop();
        }
    };

到此over,代碼邏輯很復(fù)雜,涉及的類也比較多,有興趣的可以去看看源碼,我就不多說(shuō)了。

總結(jié)

Notification從創(chuàng)建到播放的流程基本就這樣,至于聲音的區(qū)分是否電話中,如果incall則使用RingTone播放,反之mediaplayer播放。
而使用的attributes也區(qū)分是否incall。

以上。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72166.html

相關(guān)文章

  • 淺談Android O Touch聲音播放流程

    摘要:前言當(dāng)我們點(diǎn)擊屏幕按鍵時(shí),就會(huì)聽(tīng)到音,那么音是如何播放起來(lái)的呢,由于最近項(xiàng)目需求順便熟悉下了音的邏輯。完成的繪制過(guò)程,包括過(guò)程。向分發(fā)收到的用戶發(fā)起的事件,如按鍵,觸屏等事件??偨Y(jié)音的流程就簡(jiǎn)單分析到這里,歡迎大家交流指正。 前言 當(dāng)我們點(diǎn)擊屏幕按鍵時(shí),就會(huì)聽(tīng)到touch音,那么touch音是如何播放起來(lái)的呢,由于最近項(xiàng)目需求順便熟悉下了touch音的邏輯。 正文 談touch邏輯首先...

    XiNGRZ 評(píng)論0 收藏0
  • Notification通知欄

    摘要:當(dāng)向系統(tǒng)發(fā)出通知時(shí),它將先以圖標(biāo)的形式顯示在通知欄中。通知欄和抽屜式通知欄均是由系統(tǒng)控制,用戶可以隨時(shí)查看。更新通知跟發(fā)送通知使用相同的方式。創(chuàng)建返回棧添加返回棧代碼默認(rèn)情況下,從通知啟動(dòng)一個(gè),按返回鍵會(huì)回到主屏幕。 目錄介紹 1.Notification簡(jiǎn)單概述 2.Notification通知用途 3.Notification的基本操作 3.1 Notification創(chuàng)建必要的...

    FWHeart 評(píng)論0 收藏0
  • iNotify.js 2 實(shí)現(xiàn)瀏覽器的title閃爍滾動(dòng)聲音提示,彈出通知

    摘要:實(shí)現(xiàn)瀏覽器的閃爍滾動(dòng)聲音提示等系統(tǒng)彈出通知。它沒(méi)有依賴,壓縮只有只有,實(shí)例預(yù)覽。下載使用有消息了。文字的方向它的值可以是自動(dòng)從左到右從右到左。一個(gè)圖片的,將被用于顯示通知的圖標(biāo)。當(dāng)用戶關(guān)閉通知時(shí)被觸發(fā)。 JS 實(shí)現(xiàn)瀏覽器的 title 閃爍、滾動(dòng)、聲音提示、chrome、Firefox、Safari等系統(tǒng)彈出通知。它沒(méi)有依賴,壓縮只有只有4.66kb(gzipped: 1.70kb),...

    fantix 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<