android 監聽屏幕鎖屏專題及常見問題 - CSDN

2021-01-10 CSDN技術社區

> 鎖屏聽音樂(音頻),沒有鎖屏看視頻
Android系統亮屏、鎖屏、屏幕解鎖事件(解決部分手機亮屏後未解鎖即進入resume狀態)- http://blog.csdn.net/oracleot/article/details/20378453
Android 實現鎖屏的較完美方案- https://segmentfault.com/a/1190000003075989
https://github.com/android/platform_frameworks_policies_base/blob/master/phone/com/android/internal/policy/impl/KeyguardViewManager.java
Android QQ音樂/酷狗音樂鎖屏控制實現原理,酷狗鎖屏- https://blog.csdn.net/yangxi_pekin/article/details/50456763
Android 監聽屏幕鎖屏,用戶解鎖- http://blog.csdn.net/mengweiqi33/article/details/18094221
  最近在做視頻播放器的時候,遇到一個問題,在用戶播放視頻然後鎖屏之後,視頻播放器仍然在繼續播放,遇到類似手機系統狀態改變的問題的時候,首先想到了廣播(don't call me,i will call you);在網上搜羅了一個方法,做個總結;
public class ScreenListener {
    private Context mContext;
    private ScreenBroadcastReceiver mScreenReceiver;
    private ScreenStateListener mScreenStateListener;

    public ScreenListener(Context context) {
        mContext = context;
        mScreenReceiver = new ScreenBroadcastReceiver();
    }

    /**
     * screen狀態廣播接收者
     */
    private class ScreenBroadcastReceiver extends BroadcastReceiver {
        private String action = null;

        @Override
        public void onReceive(Context context, Intent intent) {
            action = intent.getAction();
            if (Intent.ACTION_SCREEN_ON.equals(action)) { // 開屏
                mScreenStateListener.onScreenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 鎖屏
                mScreenStateListener.onScreenOff();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解鎖
                mScreenStateListener.onUserPresent();
            }
        }
    }

    /**
     * 開始監聽screen狀態
     * 
     * @param listener
     */
    public void begin(ScreenStateListener listener) {
        mScreenStateListener = listener;
        registerListener();
        getScreenState();
    }

    /**
     * 獲取screen狀態
     */
    private void getScreenState() {
        PowerManager manager = (PowerManager) mContext
                .getSystemService(Context.POWER_SERVICE);
        if (manager.isScreenOn()) {
            if (mScreenStateListener != null) {
                mScreenStateListener.onScreenOn();
            }
        } else {
            if (mScreenStateListener != null) {
                mScreenStateListener.onScreenOff();
            }
        }
    }

    /**
     * 停止screen狀態監聽
     */
    public void unregisterListener() {
        mContext.unregisterReceiver(mScreenReceiver);
    }

    /**
     * 啟動screen狀態廣播接收器
     */
    private void registerListener() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        mContext.registerReceiver(mScreenReceiver, filter);
    }

    public interface ScreenStateListener {// 返回給調用者屏幕狀態信息
        public void onScreenOn();
        public void onScreenOff();
        public void onUserPresent();
    }
}
在上面的代碼中,用到了動態註冊廣播,在需要監聽屏幕狀態的activity中,當不需要監聽的時候,如activity finish的時候,使用unregisterListener解除廣播監聽; 
下面是在Activity中調用;
ScreenListener l = new ScreenListener(this);
        l.begin(new ScreenStateListener() {
            @Override
            public void onUserPresent() {
                Log.e("onUserPresent", "onUserPresent");
            }

            @Override
            public void onScreenOn() {
                Log.e("onScreenOn", "onScreenOn");
            }

            @Override
            public void onScreenOff() {
                Log.e("onScreenOff", "onScreenOff");
            }
        });
通過以上步驟即實現了對屏幕狀態的監聽.

> 音視頻播放焦點及暫停
MediaPlayer聲音自動停止、恢復,一鍵靜音等功能。
--  音頻焦點
Android音頻焦點詳解(上)- http://blog.csdn.net/wangjihuanghun/article/details/54957459
Android音頻焦點詳解(下)源碼詳解-  http://blog.csdn.net/wangjihuanghun/article/details/56069283
Android AudioManager處理兩個播放器同時有聲音- http://blog.csdn.net/shuaicike/article/details/39930823
博客Demo- https://github.com/landptf/BlogDemo

// 獲取AudioManager實例
final AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener l = new AudioManager.OnAudioFocusChangeListener() {
    @Override
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {// 焦點獲取到了,那繼續播放,並恢復音量。
            AudioTrack audioTrack = mAudioTrack;
            if (audioTrack != null) {
                audioTrack.setStereoVolume(1.0f, 1.0f);
                if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
                    audioTrack.flush();
                    audioTrack.play();
                }
            }
        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {// 焦點丟失了,暫停播放。
             AudioTrack audioTrack = mAudioTrack;
            if (audioTrack != null) {
                if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
                    audioTrack.pause();
                }
            }
        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { // 焦點丟失了,但是允許在降低音量的前提下繼續播放,那麼降低聲音。
            AudioTrack audioTrack = mAudioTrack;
            if (audioTrack != null) {
                audioTrack.setStereoVolume(0.5f, 0.5f);
            }
        }
    }
};

// 因為這裡要獲得的焦點無法預知時長,因此用AUDIOFOCUS_GAIN模式。
int requestCode = am.requestAudioFocus(l, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (requestCode == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
   // 成功獲取到了焦點。那啟動播放
   AudioTrack audioTrack = mAudioTrack;
    if (audioTrack != null) {
        audioTrack.setStereoVolume(1.0f, 1.0f);
        if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
            audioTrack.flush();
            audioTrack.play();
        }
    }
}else{  // 沒有獲取到音頻焦點。那不播放聲音
    AudioTrack audioTrack = mAudioTrack;
    if (audioTrack != null) {
        if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            audioTrack.pause();
        }
    }
}

> 音頻焦點
Android 音頻焦點(Audio Focus)- https://blog.csdn.net/suyimin2010/article/details/79479323
Android音頻焦點處理- https://blog.csdn.net/zwlove5280/article/details/84881019
  從Android 8.0開始(API 26),請求音頻焦點的方式以及系統對音頻焦點變化的管理有些微妙的變化。首先,對音頻焦點變化的管理的變化體現在兩個方面,延遲獲取焦點和自動降低音量。
-- 實例1
public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {
    private AudioManager mAudioManager;
    private AudioFocusRequest mFocusRequest;
    private AudioAttributes mAudioAttributes;

    private onRequestFocusResultListener mOnRequestFocusResultListener;
    private OnAudioFocusChangeListener mAudioFocusChangeListener;

    public AudioFocusManager(Context context) {
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

    /**
     * Request audio focus.
     */
    public void requestFocus() {
        int result;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (mFocusRequest == null) {
                if (mAudioAttributes == null) {
                    mAudioAttributes = new AudioAttributes.Builder()
                            .setUsage(AudioAttributes.USAGE_MEDIA)
                            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                            .build();
                }
                mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                        .setAudioAttributes(mAudioAttributes)
                        .setWillPauseWhenDucked(true)
                        .setOnAudioFocusChangeListener(this)
                        .build();
            }
            result = mAudioManager.requestAudioFocus(mFocusRequest);
        } else {
            result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        }
        if (mOnRequestFocusResultListener != null) {
            mOnRequestFocusResultListener.onHandleResult(result);
        }
    }


    @Override
    public void onAudioFocusChange(int focusChange) {
        if (mAudioFocusChangeListener != null) {
            mAudioFocusChangeListener.onAudioFocusChange(focusChange);
        }
    }

    /**
     * Release audio focus.
     */
    public void releaseAudioFocus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mAudioManager.abandonAudioFocusRequest(mFocusRequest);
        } else {
            mAudioManager.abandonAudioFocus(this);
        }
    }

    /**
     * Handle the result of audio focus.
     */
    public interface onRequestFocusResultListener {
        void onHandleResult(int result);
    }


    public void setOnHandleResultListener(onRequestFocusResultListener listener) {
        mOnRequestFocusResultListener = listener;
    }


    /**
     * Same as AudioManager.OnAudioFocusChangeListener.
     */
    public interface OnAudioFocusChangeListener {
        void onAudioFocusChange(int focusChange);
    }


    public void setOnAudioFocusChangeListener(OnAudioFocusChangeListener listener) {
        mAudioFocusChangeListener = listener;
    }

}

-- 實例2
public class AudioFocusUtils {
    private static AudioFocusUtils mInstance;
    private AudioManager mAudioManager;
    private AudioFocusRequest mFocusRequest;
    private AudioAttributes mAudioAttributes;

    private AudioFocusUtils() {}

    public static AudioFocusUtils getInstance() {
        if (mInstance == null) {
            synchronized (AudioFocusUtils.class) {
                mInstance = new AudioFocusUtils();
            }
        }
        return mInstance;
    }

    /**
     * 獲取音頻焦點
     */
    public void requestAudioFocus(Context context) {
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        if (mAudioManager != null) {
//            mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
            int result;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (mFocusRequest == null) {
                    if (mAudioAttributes == null) {
                        mAudioAttributes = new AudioAttributes.Builder()
                                .setUsage(AudioAttributes.USAGE_MEDIA)
                                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                                .build();
                    }
                    mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                            .setAudioAttributes(mAudioAttributes)
                            .setWillPauseWhenDucked(true)
                            .setOnAudioFocusChangeListener(mAudioFocusListener)
                            .build();
                }
                result = mAudioManager.requestAudioFocus(mFocusRequest);
            } else {
                result = mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
            }
        }
    }

    private int mLossTransientCount;
    /**
     * 音頻焦點監聽
     */
    public AudioManager.OnAudioFocusChangeListener mAudioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            switch(focusChange){
                case AudioManager.AUDIOFOCUS_LOSS:
                    Log.d("desaco", "AUDIOFOCUS_LOSS");
                    // 對應AUDIOFOCUS_GAIN
                    // 表示音頻焦點請求者需要長期佔有焦點,這裡一般需要stop播放和釋放
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    Log.d("desaco", "AUDIOFOCUS_LOSS_TRANSIENT");
                    // 對應AUDIOFOCUS_GAIN_TRANSIENT
                    // 表示音頻焦點請求者需要短暫佔有焦點,這裡一般需要pause播放
                    mLossTransientCount ++;
//                    if (mLossTransientCount == 2) {
//                        Log.d("desaco", "mLossTransientCount == 2");
//                        VoiceMainController.getInstance().dismissVr();
//                    }
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    Log.d("desaco", "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
                    // 對應AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
                    // 表示音頻焦點請求者需要佔有焦點,但是我也可以繼續播放,只是需要降低音量或音量置為0
                    break;
                case AudioManager.AUDIOFOCUS_GAIN:
                    Log.d("desaco", "AUDIOFOCUS_GAIN");
                    // 獲得焦點,這裡可以進行恢復播放
                    mLossTransientCount = 0;
                    break;
            }
        }
    };

    /**
     * 釋放音頻焦點及資源
     * @param context
     */
    public void abandonAudioFocus(Context context) {
        if (mAudioManager != null) {
            mAudioManager.abandonAudioFocus(mAudioFocusListener);
        }
    }
}

> 音頻焦點

/** * 音頻焦點處理 */public class AudioFocusUtils { private static AudioFocusUtils mInstance; private AudioFocusUtils() {} public static AudioFocusUtils getInstance() { if (mInstance == null) { synchronized (AudioFocusUtils.class) { mInstance = new AudioFocusUtils(); } } return mInstance; } /** * 獲取音頻焦點 */ public void requestAudioFocus(Context context) { AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); if (am != null) { am.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } } /** * 音頻焦點監聽 */ public AudioManager.OnAudioFocusChangeListener mAudioFocusListener = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { switch(focusChange){ case AudioManager.AUDIOFOCUS_LOSS: Log.d("desaco", "AUDIOFOCUS_LOSS"); // 對應AUDIOFOCUS_GAIN // 表示音頻焦點請求者需要長期佔有焦點,這裡一般需要stop播放和釋放 break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: Log.d("desaco", "AUDIOFOCUS_LOSS_TRANSIENT"); // 對應AUDIOFOCUS_GAIN_TRANSIENT // 表示音頻焦點請求者需要短暫佔有焦點,這裡一般需要pause播放 break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: Log.d("desaco", "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); // 對應AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK // 表示音頻焦點請求者需要佔有焦點,但是我也可以繼續播放,只是需要降低音量或音量置為0 break; case AudioManager.AUDIOFOCUS_GAIN: Log.d("desaco", "AUDIOFOCUS_GAIN"); // 獲得焦點,這裡可以進行恢復播放 break; } } }; /** * 釋放音頻焦點及資源 * @param context */ public void abandonAudioFocus(Context context) { AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); if (am != null) { am.abandonAudioFocus(mAudioFocusListener); } }}

相關焦點

  • android啟動頁設計專題及常見問題 - CSDN
    轉載請註明出處:http://blog.csdn.net/wangjihuanghun/article/details/63255144啟動頁幾乎成為了每個app的標配,有些商家在啟動頁中增加了開屏廣告以此帶來更多的收入。
  • Android - android xml 層級專題及常見問題 - CSDN
    比如說,你需要為不同的屏幕解析度提供替代的圖片資源,為不同的語言提供替代的字符串資源。在運行時,Android 檢測當前設備配置,並為應用程式加載合適的資源。要為特定的配置的確定一系列替代資源,遵循如下的步驟:在res/ 下創建一個新的目錄,以 _ 的方式命名。這裡的 resources_name 是上表中提到的任意資源,如布局、圖片等。
  • android app被殺原因專題及常見問題 - CSDN
    分析長按HOME鍵清理App最終會執行到ActivityManagerService.cleanUpRemovedTaskLocked方法中,ActivityManagerService類在文件"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java"中,
  • 對抗學習專題及常見問題 - CSDN
    物理世界中的對抗樣本,有列印重照、亮度對比度等調整)【https://blog.csdn.net/u010710787/article/details/78916762】隱馬爾可夫:通過顯序列算隱序列generalization ability 泛化:不要過擬合神經網絡:非線性積累NP完全問題
  • android 啟動頁慢專題及常見問題 - CSDN
    如果說,OOP如果是把問題劃分到單個模塊的話,那麼AOP就是把涉及到眾多模塊的某一類問題進行統一管理。打個比方Android 裡面PMS,AMS都擁有各自的職責,但是他們都需要通過log系統管理log,這就是一種AOP思想。AspectJ實際上是對AOP編程思想的一個實踐,當然,除了AspectJ以外,還有很多其它的AOP實現,例如ASMDex,但目前最好、最方便的,依然是AspectJ。
  • android開發 自我優勢 - CSDN
    3、能夠有效避免APP運行過程中遇到的內存洩漏和內存溢出問題。4、熟練掌握線程之間通過Handler傳遞消息的機制原理5、熟練掌握自定義控制項,自定義組合控制項中View所經過的測量,布局,繪製的流程,以及Touch事件分發機制。
  • android 虛擬機版本專題及常見問題 - CSDN
    我們使用Java開發android,在編譯打包APK文件時,會經過以下流程Java編譯器將應用中所有Java文件編譯為class文件 dx工具將應用編譯輸出的類文件轉換為Dalvik字節碼,即dex文件之後經過籤名、對齊等操作變為APK文件。
  • android 布局 覆蓋 - CSDN
    按照正常使用點擊item的空白區域選擇時沒有問題的,但是當你單獨點擊checkbox時,CheckBox沒有相應的點擊事件,而父控制項也沒有捕獲。導致的問題是:顯示的是選擇了,但是實際上並沒有捕獲點擊事件。
  • 如何利用 Android 自定義控制項實現炫酷的動畫?|CSDN 博文精選
    RecyclerView相關的面試題也是各大廠常問的問題之一(權重非常高)。2、 張旭童的掌握自定義LayoutManager(一) 系列開篇 常見誤區、問題、注意事項,常用APIhttps://blog.csdn.net/zxt0601/article/details/529480093、張旭童的掌握自定義LayoutManager(二) 實現流式布局https://blog.csdn.net/zxt0601/article
  • 加載布局專題及常見問題 - CSDN
    relativeLayout_parent_params) }動態加載布局LayoutInflater 來間接加載什麼叫LayoutInflater這個是個抽象類 在文檔中如下聲明: public abstract class LayoutInflater extends Object Layout inflation是在android
  • linux啟動監聽 - CSDN
    報錯提示:The listener supports no servicesThe command completed successfully修改伺服器主機名[root@myhost]# vi /etc/hosts添加127.0.0.1 主機名ip  主機名問題原因
  • windows10卡啟動修復專題及常見問題 - CSDN
    在Windows 10上,安全模式允許加載一組基本功能和通用設備驅動程序,足以解決常見的軟體和硬體問題。例如,當計算機無法正常啟動、網絡連接問題以及應用程式或Windows Update無法下載更新時,可以使用安全模式對其進行故障排除。
  • android 自定義view大小 - CSDN
    直觀來說,可能有以下問題需要考慮:自定的View最好不要超過父控制項的大小,這樣才能保證自己能在父控制項中完整顯示自定的View(如果是ViewGroup)的子控制項最好不要超過自己的大小,這樣才能保證子控制項顯示完整如果明確為View指定了尺寸,最好按照指定的尺寸設置以上三個問題可能是自定義ViewGroup最需要考慮的問題
  • slot vue 用法專題及常見問題 - CSDN
    這是一種常見的模式,雖然它不需要很多代碼,但是如果沒有為了可重用性而提取邏輯,它會使很多組件變得混亂。在watch部分中,監聽promise的變化,當promise發生變化時,清除狀態,然後調用 then 並 catch promise,當 promise 成功完成或失敗時更新狀態。然後,在模板中,我們根據狀態顯示一個不同的槽。請注意,我們沒有保持它真正的無渲染,因為我們需要一個根元素來使用模板。我們還將data和error傳遞到相關的插槽範圍。
  • ai語音控制是什麼專題及常見問題 - CSDN
    反對語音技術的一個常見言論是,對著設備大聲說話,尤其是在公共場合會讓人覺得怪異或者是尷尬(尤其是年齡大一點的人會這樣覺得,我們指的是20歲以上的人)。BBH公司的觀點是,這些標準很快就會改變。考慮到這一點,我們也要注意到,越來越多的語音平臺用戶可能擁有他們和語音助手都能訪問的屏幕,要麼是內置在設備中(比如Echo Show),要麼是通過智慧型手機或生態系統的屏幕,比如谷歌助理。雖然不能完全依賴這些屏幕,但它們可以用來豐富用戶體驗。
  • cdn助力專題及常見問題 - CSDN
    本次騰訊雲大學大咖分享《騰訊雲計算人才加速計劃》專題之《CDN邊緣智能助力5G》邀請騰訊雲專家工程師 廖龍 將與大家共探技術與產業發展變化下如何引領時代。本課程主要有三章:5G元年,探索與思考,未來展望。5G元年在過去十年,整個中國網際網路的發展迅速。
  • bootstrap 用什麼布局專題及常見問題 - CSDN
    柵格的class有四個col-lg-1,col-md-1,col-sm-1,col-xs-1,分別對應大屏幕(large),中等屏幕(middl),小屏幕(smll),極小(xs)。其中col表示列的意思column,中間為尺寸的縮寫,最後是div分配的柵格大小(例子是佔用的是1/12)4.響應式布局?柵格布局通過簡單的bootstrap類引用便可以實現網頁布局。
  • 怎麼給電腦設置密碼和鎖屏?怎麼一鍵設置電腦屏幕保護
    介紹最常用的一種方法給你們,一招教會你們怎麼給電腦設置密碼和鎖屏。電腦設置密碼和鎖屏的操作流程一、給電腦設置密碼二、給電腦設置鎖屏三、鎖屏的方法怎麼給電腦設置密碼1、在任務欄中搜索「控制面板」,點擊進入控制面板應用。
  • 智能硬體新品發布會專題及常見問題 - CSDN
    這款二合一電腦升級成了英特爾第八代酷睿處理器,配備了12.3英寸觸控屏幕,解析度為2736 x 1824。Surface Pro 6提供黑、藍、紅和灰四種配色,單機重量僅1.7磅(約771克)。Surface Laptop 2:屏幕素質升級