Android 让Google Cast v3在不受支持的设备上运行
CastV3框架具有一些功能,这些功能试图使它能够在没有Google Play服务的设备上运行,但在测试时我遇到了一些问题 在Kindle上,Google API返回服务_INVALID,并带有isUserResolvable true。 在onActivityResult在升级后返回ConnectionResult.SUCCESS的设备上,CastContext.getSharedInstance可能引发RuntimeError。 作为2的副作用,包含MiniControllerFragment的项的XML膨胀将失败。 我发现了一些错误Android 让Google Cast v3在不受支持的设备上运行,android,chromecast,Android,Chromecast,CastV3框架具有一些功能,这些功能试图使它能够在没有Google Play服务的设备上运行,但在测试时我遇到了一些问题 在Kindle上,Google API返回服务_INVALID,并带有isUserResolvable true。 在onActivityResult在升级后返回ConnectionResult.SUCCESS的设备上,CastContext.getSharedInstance可能引发RuntimeError。 作为2的副作用,包含MiniControllerFragmen
java.lang.RuntimeException: Unable to start activity ComponentInfo{##########.MainActivity}: android.view.InflateException: Binary XML file line #42: Error inflating class fragment
Caused by: java.lang.RuntimeException:
com.google.android.gms.dynamite.DynamiteModule$zzc: Remote load failed. No local fallback found.
at com.google.android.gms.internal.zzauj.zzan(Unknown Source)
at com.google.android.gms.internal.zzauj.zza(Unknown Source)
at com.google.android.gms.cast.framework.CastContext.<init>(Unknown Source)
at com.google.android.gms.cast.framework.CastContext.getSharedInstance(Unknown Source)
at com.google.android.gms.cast.framework.media.uicontroller.UIMediaController.<init>(Unknown Source)
at com.google.android.gms.cast.framework.media.widget.MiniControllerFragment.onCreateView(Unknown Source)
当我实现ViewStub时,我仍然在预发布测试机器中崩溃,因为它们返回了成功,但没有可用的CastContext。为了解决这个问题,我需要另一个测试来检查CastContext是否可创建。在应用程序中需要一个单例/代码,如下所示
boolean gCastable = false;
boolean gCastTested = false;
public boolean isCastAvailable(Activity act, int resultCode ){
if( gCastTested == true ){
return gCastable;
}
GoogleApiAvailability castApi = GoogleApiAvailability.getInstance();
int castResult = castApi.isGooglePlayServicesAvailable(act);
switch( castResult ) {
case ConnectionResult.SUCCESS:
gCastable = true;
gCastTested = true;
return true;
/* This code is needed, so that the user doesn't get a
*
* your device is incompatible "OK"
*
* message, it isn't really "user actionable"
*/
case ConnectionResult.SERVICE_INVALID: // Result from Amazon kindle - perhaps check if kindle first??
gCastable = false;
gCastTested = true;
return false;
////////////////////////////////////////////////////////////////
default:
if (castApi.isUserResolvableError(castResult)) {
castApi.getErrorDialog(act, castResult, resultCode, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
gCastable = false;
gCastTested = false;
return;
}
}).show();
} else {
gCastTested = true;
gCastable = false;
return false;
}
}
return gCastable;
}
public void setCastOK(Activity mainActivity, boolean result ) {
gCastTested = true;
gCastable = result;
}
和一个助手函数来检查我们是否知道强制转换的状态
public boolean isCastAvailableKnown() {
return gCastable;
}
然而,为了处理返回成功的设备,我在App/singleton中还需要以下代码
当活动收到cast结果时,我们创建一个CastContext。希望是,如果应用程序能够创建CastContext,那么框架将以导致崩溃的相同方式成功
public boolean onCastResultReceived( Activity act, int result ) {
boolean wasOk = false;
if( result == ConnectionResult.SUCCESS ){
try {
CastContext ctx = CastContext.getSharedInstance(act );
wasOk = true;
} catch ( RuntimeException e ){
wasOk = false;
}
}
if( wasOk ) {
setCastOK(act, true);
return true;
}else {
setCastOK(act, false );
return false;
}
}
通过使用ViewStub和片段禁用迷你控制器的充气
Fragment mini_controller_Fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/cast_mini_controller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
app:castShowImageThumbnail="true"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
一旦创建修改如下
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mApp = (MyApplication)getApplication();
if( mApp.isCastAvailable( (Activity)this, GPS_RESULT )) {
onCastAvailable();
}
...
}
onActivityResult需要处理Google Play服务升级的结果
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == GPS_RESULT ) {
if(mApp.onCastResultReceived( this, resultCode ) ){
onCastAvailable();
}
恢复
protected void onResume() {
if( mCastContext != null && mCastStateListener != null ) {
mCastContext.addCastStateListener(mCastStateListener);
mCastContext.getSessionManager().addSessionManagerListener(
mSessionManagerListener, CastSession.class);
if (mCastSession == null) {
mCastSession = CastContext.getSharedInstance(this).getSessionManager()
.getCurrentCastSession();
}
if (mQueueMenuItem != null) {
mQueueMenuItem.setVisible(
(mCastSession != null) && mCastSession.isConnected());
}
}
super.onResume();
}
暂停
protected void onPause() {
super.onPause();
if( mCastContext != null && mCastStateListener != null ) {
mCastContext.removeCastStateListener(mCastStateListener);
mCastContext.getSessionManager().removeSessionManagerListener(
mSessionManagerListener, CastSession.class);
}
}
类中的会话管理器侦听器
private final SessionManagerListener<CastSession> mSessionManagerListener =
new MySessionManagerListener();
private class MySessionManagerListener implements SessionManagerListener<CastSession> {
@Override
public void onSessionEnded(CastSession session, int error) {
if (session == mCastSession) {
mCastSession = null;
}
invalidateOptionsMenu();
}
@Override
public void onSessionResumed(CastSession session, boolean wasSuspended) {
mCastSession = session;
invalidateOptionsMenu();
}
@Override
public void onSessionStarted(CastSession session, String sessionId) {
mCastSession = session;
invalidateOptionsMenu();
}
@Override
public void onSessionStarting(CastSession session) {
}
@Override
public void onSessionStartFailed(CastSession session, int error) {
}
@Override
public void onSessionEnding(CastSession session) {
}
@Override
public void onSessionResuming(CastSession session, String sessionId) {
}
@Override
public void onSessionResumeFailed(CastSession session, int error) {
}
@Override
public void onSessionSuspended(CastSession session, int reason) {
}
}
你是如何解决你的问题的?通过检查GoogleServices版本?OnCasterSultReceived,可以在try块中创建CastContext。如果此操作失败,则不会使强制转换可用。我试图编辑答案来澄清这一点。但我没有使用你的解决方案。总的来说,我想问的是,您提到的问题的原因是什么?我的活动在OnActivityResult中获得了成功代码,但嵌入式GPS UI在充气时崩溃。考虑到它无法创建一个CastContext,在启用充气之前创建一个CastContext允许我预测结果。在我的情况下,很难重现。我也得到了成功代码。很少有设备会遇到这个问题。将viewstub放在一个try-catch中并观察它是否失败不是更容易吗?@AndreasRudolph,这会使演员阵容是否可用变得不那么清楚,如果他们没有正确处理捕捉,可能会破坏一些UI元素。如果我希望ViewStub正常工作,那么我觉得我最好控制结果。你是对的,我认为这是一个大的正确解决方案,而不是一个小的但不是目标明确的解决方案。一个人可以选择自己-谢谢你的帮助!
protected void onResume() {
if( mCastContext != null && mCastStateListener != null ) {
mCastContext.addCastStateListener(mCastStateListener);
mCastContext.getSessionManager().addSessionManagerListener(
mSessionManagerListener, CastSession.class);
if (mCastSession == null) {
mCastSession = CastContext.getSharedInstance(this).getSessionManager()
.getCurrentCastSession();
}
if (mQueueMenuItem != null) {
mQueueMenuItem.setVisible(
(mCastSession != null) && mCastSession.isConnected());
}
}
super.onResume();
}
protected void onPause() {
super.onPause();
if( mCastContext != null && mCastStateListener != null ) {
mCastContext.removeCastStateListener(mCastStateListener);
mCastContext.getSessionManager().removeSessionManagerListener(
mSessionManagerListener, CastSession.class);
}
}
private final SessionManagerListener<CastSession> mSessionManagerListener =
new MySessionManagerListener();
private class MySessionManagerListener implements SessionManagerListener<CastSession> {
@Override
public void onSessionEnded(CastSession session, int error) {
if (session == mCastSession) {
mCastSession = null;
}
invalidateOptionsMenu();
}
@Override
public void onSessionResumed(CastSession session, boolean wasSuspended) {
mCastSession = session;
invalidateOptionsMenu();
}
@Override
public void onSessionStarted(CastSession session, String sessionId) {
mCastSession = session;
invalidateOptionsMenu();
}
@Override
public void onSessionStarting(CastSession session) {
}
@Override
public void onSessionStartFailed(CastSession session, int error) {
}
@Override
public void onSessionEnding(CastSession session) {
}
@Override
public void onSessionResuming(CastSession session, String sessionId) {
}
@Override
public void onSessionResumeFailed(CastSession session, int error) {
}
@Override
public void onSessionSuspended(CastSession session, int reason) {
}
}
int visibility = View.GONE;
if( mApplication.isCastAvailableKnown( ) ) {
CastSession castSession = CastContext.getSharedInstance(mApplication).getSessionManager()
.getCurrentCastSession();
if( castSession != null && castSession.isConnected() ){
visibility = View.VISIBLE;
}
}
viewHolder.mMenu.setVisibility( visibility);