Android 集成Google Cast 異常問題解析
GoogleCast 異常問題
項目中集成 Google Cast 功能時, 部分機型遇到一些問題
需求
項目中Cast需求有點特別,默認Cast Button處于不可見狀態(tài)。 APP進入前臺時,查詢周圍是否有可用Cast設備, 有才顯示Cast Button, 否則隱藏
根據(jù)流程, 我們有兩種情況需要處理
Fragment/Activity 啟動時查詢Available Cast Devices
暫命名方法1
代碼實現(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)獲取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ù)上述實現(xiàn), 可以即時監(jiān)聽Cast Devices的變化,動態(tài)更新Cast Button的可見性
問題
新增了一個有個需求, Toolbar上新增了一個視圖,需要根據(jù)Cast Button的可見性來設置視圖可見性,即二者可見性互斥,但在測試中發(fā)現(xiàn)一些問題。
部分設備上,例如Remi Note 4, 上述兩個方法都無效
- 問題1,方法1 只有在進程銷毀后第一次啟動可以獲取周圍Available Cast Devices,之后關閉APP重啟一直返回false
- 問題2,方法2 APP處于前臺時,斷開WIFI時,Cast Button消失,重新連接WIFI,Cast Button可見,但代碼中并沒有收到Cast Devices 狀態(tài)更新的回調(diào), 設置Cast Button Visibility的代碼并未執(zhí)行,這就出現(xiàn)一種情況:雖然Cast Button不可見,但是新增的視圖卻并未顯示。
分析
- 問題1
當App進程銷毀后重新啟動, 可以成功獲取Cast Devices, 說明初始化CastContext時, 主動掃描了周邊Routes, 而App關閉后重新打開, 部分設備未主動掃描, 所以獲取狀態(tài)失敗。
- 問題2
當Cast Button可見時,斷開WIFI連接, 因為沒有收到onCastStateChanged的回調(diào),CastButton.SetVisibility(View.GONE)并未執(zhí)行, 但是Cast Button 卻不可見了, 打開Developers options--Show layouts bounds開關, 發(fā)現(xiàn)Cast Button 在布局中存在,但是圖標卻不可見,那么有兩種可能: - Cast Button的Drawable 被移除了 - Cast Button被設為View.INVISIBLE
但是onCastStateChanged的回調(diào)并未執(zhí)行, 但是Cast Button 狀態(tài)卻有變化,那只能說明Cast Button內(nèi)部監(jiān)聽了連接狀態(tài)的變化,并內(nèi)部處理了展示狀態(tài)。
項目中Cast Button是一個MediaRouteButton, 查詢源碼
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啟動時,雖然主動掃描周邊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
因為無法收到onCastStateChanged的回調(diào), 而且MediaRouteButton內(nèi)部監(jiān)聽了連接狀態(tài)的變化來更新顯示,那么我們也效仿相同的實現(xiàn),通過監(jiān)聽當前連接狀態(tài),結(jié)合MediaRouteButton的可見性,來判斷Available Cast Devices
- 當前連接斷開, 直接隱藏MediaRouteButton
- 當前連接恢復, 查詢是否有Available Cast Devices,來設置MediaRouteButton可見
- 再判斷MediaRouteButton是否可見,來設置新視圖的可見性
public static boolean checkCastDevice(Context activity) {
//如果當前無連接, 說明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é)
部分設備當非殺進程重新啟動App,不能主動掃描routes, 需要在添加主動掃描,來獲取最新的Cast Devices
部分設備無法獲取onCastStateChanged回調(diào),且MediaRouteButton內(nèi)部會監(jiān)聽連接狀態(tài)設置顯隱性, 所以我們也同樣監(jiān)聽連接狀態(tài)的變化,來判斷Cast 是否可用
以上就是Android 集成Google Cast 異常問題解析的詳細內(nèi)容,更多關于Android集成Google Cast異常的資料請關注腳本之家其它相關文章!
相關文章
Android實現(xiàn)圖片轉(zhuǎn)高斯模糊以及高斯模糊布局
這篇文章主要為大家詳細介紹了Android實現(xiàn)圖片轉(zhuǎn)高斯模糊的方法,以及高斯模糊布局,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Android進階——安卓調(diào)用ESC/POS打印機打印實例
本篇文章主要介紹了Android進階——安卓調(diào)用ESC/POS打印機打印實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04
Android中ImageCropper矩形、圓形 裁剪框的實現(xiàn)方法
這篇文章主要給大家介紹了關于Android中ImageCropper矩形、圓形 裁剪框的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2018-07-07
Android實現(xiàn)下載zip壓縮文件并解壓的方法(附源碼)
這篇文章主要給大家介紹了利用Android實現(xiàn)下載zip壓縮文件并解壓的方法,文中給出了示例代碼并提供了源碼下載,需要的朋友可以參考借鑒,下面來一起看看吧。2017-02-02
Android模擬器安裝APP出現(xiàn)INSTALL_FAILED_NO_MATCHING_ABIS錯誤解決方案
這篇文章主要介紹了 Android模擬器安裝APP出現(xiàn)INSTALL_FAILED_NO_MATCHING_ABIS錯誤解決方案的相關資料,需要的朋友可以參考下2016-12-12
Android Listview 滑動過程中提示圖片重復錯亂的原因及解決方法
android中l(wèi)istview是比較常見的組件,通過本文主要給大家分析Android中Listview滾動過程造成的圖片顯示重復、錯亂、閃爍的原因及解決方法,順便跟進Listview的緩存機制,感興趣的朋友一起看下吧2016-08-08

