Android 集成Google Cast 異常問題解析
GoogleCast 異常問題
項(xiàng)目中集成 Google Cast 功能時(shí), 部分機(jī)型遇到一些問題
需求
項(xiàng)目中Cast需求有點(diǎn)特別,默認(rèn)Cast Button處于不可見狀態(tài)。 APP進(jìn)入前臺(tái)時(shí),查詢周圍是否有可用Cast設(shè)備, 有才顯示Cast Button, 否則隱藏
根據(jù)流程, 我們有兩種情況需要處理
Fragment/Activity 啟動(dòng)時(shí)查詢Available Cast Devices
暫命名方法1
代碼實(shí)現(xiàn)如下: Fragment -> onResume(), checkCastDevices:
public static boolean checkCastDevice(Context activity) { MediaRouter mediaRouter = MediaRouter.getInstance(activity); List<mediarouter.routeinfo> routes = mediaRouter.getRoutes(); if (routes != null && routes.size() != 0) { for (MediaRouter.RouteInfo info : routes) { if (info.getDeviceType() != MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED && !info.isDefault() && !info.isBluetooth() && !info.isDeviceSpeaker()) { return true; } } } return false; }
Fragment/Activity 處于前臺(tái)時(shí),動(dòng)態(tài)獲取Availabe Cast Devices
暫命名方法2
監(jiān)聽了addCastStateListener CastContext.addCastStateListener(this)
@Override public void onCastStateChanged(int i) { stateListenerListTemp.clear(); stateListenerListTemp.addAll(stateListenerList); switch (i) { case CastState.CONNECTED: for (CastStateListener listener : stateListenerListTemp) { listener.connected(); } break; case CastState.CONNECTING: for (CastStateListener listener : stateListenerListTemp) { listener.connecting(); } break; case CastState.NO_DEVICES_AVAILABLE: //has not available devices for (CastStateListener listener : stateListenerListTemp) { listener.noDevicesAvailable(); // hide cast button } break; case CastState.NOT_CONNECTED: //has available devices, but not connect for (CastStateListener listener : stateListenerListTemp) { listener.notConnected(); // show cast button } break; } stateListenerListTemp.clear(); }
依據(jù)上述實(shí)現(xiàn), 可以即時(shí)監(jiān)聽Cast Devices的變化,動(dòng)態(tài)更新Cast Button的可見性
問題
新增了一個(gè)有個(gè)需求, Toolbar上新增了一個(gè)視圖,需要根據(jù)Cast Button的可見性來設(shè)置視圖可見性,即二者可見性互斥,但在測(cè)試中發(fā)現(xiàn)一些問題。
部分設(shè)備上,例如Remi Note 4, 上述兩個(gè)方法都無效
- 問題1,方法1 只有在進(jìn)程銷毀后第一次啟動(dòng)可以獲取周圍Available Cast Devices,之后關(guān)閉APP重啟一直返回false
- 問題2,方法2 APP處于前臺(tái)時(shí),斷開WIFI時(shí),Cast Button消失,重新連接WIFI,Cast Button可見,但代碼中并沒有收到Cast Devices 狀態(tài)更新的回調(diào), 設(shè)置Cast Button Visibility的代碼并未執(zhí)行,這就出現(xiàn)一種情況:雖然Cast Button不可見,但是新增的視圖卻并未顯示。
分析
- 問題1
當(dāng)App進(jìn)程銷毀后重新啟動(dòng), 可以成功獲取Cast Devices, 說明初始化CastContext時(shí), 主動(dòng)掃描了周邊Routes, 而App關(guān)閉后重新打開, 部分設(shè)備未主動(dòng)掃描, 所以獲取狀態(tài)失敗。
- 問題2
當(dāng)Cast Button可見時(shí),斷開WIFI連接, 因?yàn)闆]有收到onCastStateChanged的回調(diào),CastButton.SetVisibility(View.GONE)
并未執(zhí)行, 但是Cast Button 卻不可見了, 打開Developers options--Show layouts bounds開關(guān), 發(fā)現(xiàn)Cast Button 在布局中存在,但是圖標(biāo)卻不可見,那么有兩種可能: - Cast Button的Drawable 被移除了 - Cast Button被設(shè)為View.INVISIBLE
但是onCastStateChanged的回調(diào)并未執(zhí)行, 但是Cast Button 狀態(tài)卻有變化,那只能說明Cast Button內(nèi)部監(jiān)聽了連接狀態(tài)的變化,并內(nèi)部處理了展示狀態(tài)。
項(xiàng)目中Cast Button是一個(gè)MediaRouteButton, 查詢?cè)创a
public class MediaRouteButton extends View { private static final String TAG = "MediaRouteButton"; private static ConnectivityReceiver sConnectivityReceiver; private int mVisibility = VISIBLE; private boolean mAlwaysVisible; public MediaRouteButton(Context context) { this(context, null); } public MediaRouteButton(Context context, AttributeSet attrs) { this(context, attrs, R.attr.mediaRouteButtonStyle); } public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) { super(MediaRouterThemeHelper.createThemedButtonContext(context), attrs, defStyleAttr); if (sConnectivityReceiver == null) { sConnectivityReceiver = new ConnectivityReceiver(context.getApplicationContext()); } } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); sConnectivityReceiver.registerReceiver(this); } @Override public void onDetachedFromWindow() { if (!isInEditMode()) { sConnectivityReceiver.unregisterReceiver(this); } super.onDetachedFromWindow(); } void refreshVisibility() { //set VISIBLE or INVISIBLE super.setVisibility(mVisibility == VISIBLE && !(mAlwaysVisible || sConnectivityReceiver.isConnected()) ? INVISIBLE : mVisibility); } /** * 監(jiān)聽連接狀態(tài)的變化,刷新MediaRouteButton可見性 */ private static final class ConnectivityReceiver extends BroadcastReceiver { private final Context mContext; // If we have no information, assume that the device is connected private boolean mIsConnected = true; private List<MediaRouteButton> mButtons; ConnectivityReceiver(Context context) { mContext = context; mButtons = new ArrayList<MediaRouteButton>(); } public void registerReceiver(MediaRouteButton button) { if (mButtons.size() == 0) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); mContext.registerReceiver(this, intentFilter); } mButtons.add(button); } public void unregisterReceiver(MediaRouteButton button) { mButtons.remove(button); if (mButtons.size() == 0) { mContext.unregisterReceiver(this); } } public boolean isConnected() { return mIsConnected; } @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { boolean isConnected = !intent.getBooleanExtra( ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (mIsConnected != isConnected) { mIsConnected = isConnected; for (MediaRouteButton button: mButtons) { button.refreshVisibility(); } } } } } }
源碼中可見,內(nèi)部定義了ConnectivityReceiver來監(jiān)聽連接狀態(tài)的變化, 從而內(nèi)部控制MediaRouteButton的顯示。
解決
問題1
App啟動(dòng)時(shí),雖然主動(dòng)掃描周邊Routes,來更新可以Cast Devices狀態(tài)
//Init Cast public static void initCast(Context context) { if (context == null) return; if (initialized) { CastContextManager.getInstance().addRouter(context); return; } initialized = true; //初始化CastContextManager .... } public void addRouter(Context context) { if(castContext !=null){ MediaRouter mediaRouter = MediaRouter.getInstance(context); mediaRouter.removeCallback(callback); mediaRouter.addCallback(MediaRouteSelector.EMPTY, callback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); } } private final MediaRouter.Callback callback = new MediaRouter.Callback() { @Override public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) { super.onProviderAdded(router, provider); ZenLogger.dd("MXMediaRouter", () -> "onProviderAdded " + provider.getRoutes()); } @Override public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) { super.onProviderRemoved(router, provider); ZenLogger.dd("MXMediaRouter", () -> "onProviderRemoved " + provider.getRoutes()); } @Override public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) { super.onProviderChanged(router, provider); ZenLogger.dd("MXMediaRouter", () -> "onProviderChanged " + provider.getRoutes()); } };
問題2
因?yàn)闊o法收到onCastStateChanged的回調(diào), 而且MediaRouteButton內(nèi)部監(jiān)聽了連接狀態(tài)的變化來更新顯示,那么我們也效仿相同的實(shí)現(xiàn),通過監(jiān)聽當(dāng)前連接狀態(tài),結(jié)合MediaRouteButton的可見性,來判斷Available Cast Devices
- 當(dāng)前連接斷開, 直接隱藏MediaRouteButton
- 當(dāng)前連接恢復(fù), 查詢是否有Available Cast Devices,來設(shè)置MediaRouteButton可見
- 再判斷MediaRouteButton是否可見,來設(shè)置新視圖的可見性
public static boolean checkCastDevice(Context activity) { //如果當(dāng)前無連接, 說明unavailable Cast devices if (!NetworkMonitor.isConnected(App.applicationContext())) return false; MediaRouter mediaRouter = MediaRouter.getInstance(activity); List<MediaRouter.RouteInfo> routes = mediaRouter.getRoutes(); if (routes != null && routes.size() != 0) { for (MediaRouter.RouteInfo info : routes) { if (info.getDeviceType() != MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED && !info.isDefault() && !info.isBluetooth() && !info.isDeviceSpeaker()) { return true; } } } return false; } private NetworkMonitor.OnNetworkListener onNetWorkListener = (last, current) -> { showMediaRouteButton(CastHelper.checkCastDevice(getActivity())); //check MediaRouteButton isVisible ... };
總結(jié)
部分設(shè)備當(dāng)非殺進(jìn)程重新啟動(dòng)App,不能主動(dòng)掃描routes, 需要在添加主動(dòng)掃描,來獲取最新的Cast Devices
部分設(shè)備無法獲取onCastStateChanged回調(diào),且MediaRouteButton內(nèi)部會(huì)監(jiān)聽連接狀態(tài)設(shè)置顯隱性, 所以我們也同樣監(jiān)聽連接狀態(tài)的變化,來判斷Cast 是否可用
以上就是Android 集成Google Cast 異常問題解析的詳細(xì)內(nèi)容,更多關(guān)于Android集成Google Cast異常的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 實(shí)現(xiàn)滑動(dòng)的六種方式
這篇文章主要給大家分享的是Android 實(shí)現(xiàn)滑動(dòng)的六種方式,分別是layout、scrollBy、offsetLeftAndRight offsetTopAndButton、LayoutParams、Scroller、平移動(dòng)畫,需要的朋友可以參考一下下面文章的具體內(nèi)容2021-11-11Android 自動(dòng)判斷是電話,網(wǎng)址,EMAIL方法之Linkify的使用
本篇文章小編為大家介紹,在Android中 自動(dòng)判斷是電話,網(wǎng)址,EMAIL方法之Linkify的使用。需要的朋友參考下2013-04-04Android ServiceManager的啟動(dòng)和工作原理
這篇文章主要介紹了Android ServiceManager的啟動(dòng)和工作原理,幫助大家更好的理解和學(xué)習(xí)使用Android開發(fā),感興趣的朋友可以了解下2021-03-03Android Adapter里面嵌套ListView實(shí)例詳解
這篇文章主要介紹了Android Adapter里面嵌套ListView實(shí)例詳解的相關(guān)資料,這里提供實(shí)例代碼并說明如何實(shí)現(xiàn)該功能,需要的朋友可以參考下2017-07-07android實(shí)現(xiàn)注冊(cè)登錄程序
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)注冊(cè)登錄程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Android中監(jiān)聽判斷網(wǎng)絡(luò)連接狀態(tài)的方法
這篇文章主要介紹了Android中監(jiān)聽判斷網(wǎng)絡(luò)連接狀態(tài)的方法,介紹了是否有網(wǎng)絡(luò)連接判斷、連接的類型和監(jiān)聽網(wǎng)絡(luò)狀態(tài)的方法,需要的朋友可以參考下2014-06-06Android自定義控件實(shí)現(xiàn)不規(guī)則區(qū)域點(diǎn)擊事件
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)不規(guī)則區(qū)域點(diǎn)擊事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05android實(shí)現(xiàn)倒計(jì)時(shí)功能(開始、暫停、0秒結(jié)束)
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)倒計(jì)時(shí)功能,開始、暫停、0秒結(jié)束,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09Android中監(jiān)聽系統(tǒng)網(wǎng)絡(luò)連接打開或者關(guān)閉的實(shí)現(xiàn)代碼
本篇文章對(duì)Android中監(jiān)聽系統(tǒng)網(wǎng)絡(luò)連接打開或者關(guān)閉的實(shí)現(xiàn)用實(shí)例進(jìn)行了介紹。需要的朋友參考下2013-05-05Android開發(fā)實(shí)現(xiàn)讀取excel數(shù)據(jù)并保存為xml的方法
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)讀取excel數(shù)據(jù)并保存為xml的方法,涉及Android針對(duì)Excel數(shù)據(jù)讀取及xml格式文件的構(gòu)造與保存相關(guān)操作技巧,需要的朋友可以參考下2017-10-10