OnServiceConnected a veces no se llama después de bindService en algunos dispositivos

He mirado un número de otros hilos con títulos similares, y ningunos parecen cubrir mi problema. Así que, aquí va.

Estoy usando los archivos de expansión del mercado de Google (apkx) y código de ejemplo, con algunas modificaciones. Este código se basa en recibir devoluciones de llamada de un servicio que maneja la descarga de fondo, las verificaciones de licencias, etc.

Tengo un error en el que el servicio no se adjunta correctamente, lo que resulta en un bloqueo de software. Para hacer esto más inútil, este error nunca ocurre en algunos dispositivos, pero ocurre aproximadamente dos tercios del tiempo en otros dispositivos. Creo que es independiente de la versión de Android, sin duda tengo dos dispositivos que ejecutan 2.3.4, uno de los cuales (un Nexus S) no tiene el problema, el otro (un HTC Evo 3D) lo hace.

Para intentar conectarse al servicio, bindService se llama y devuelve true. OnBind luego se llama como se espera y devuelve un valor razonable pero (cuando el error ocurre) onServiceConnected no sucede (he esperado 20 minutos por si acaso).

¿Alguien más ha visto algo así? Si no, cualquier conjetura para lo que podría haber hecho para causar tal comportamiento? Si nadie tiene pensamientos, publicaré algún código mañana.

EDIT: Aquí está el código correspondiente. Si he perdido algo, por favor pregunte.

Al agregar este código, encontré un error menor. La fijación causó la frecuencia del problema que estoy intentando solucionar para cambiar de 2 veces en 3 a cerca de 1 vez en 6 en el teléfono que estoy probando encendido; ninguna idea sobre efectos en otros teléfonos. Esto sigue para sugerirme una condición de la raza o similar, pero no tengo ninguna idea qué con.

OurDownloaderActivity.java (copiado y cambiado del código de muestra de Google)

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... //Test the licence is up to date //if (current stored licence has expired) { startLicenceCheck(); initializeDownloadUI(); return; } ... } @Override protected void onResume() { if (null != mDownloaderClientStub) { mDownloaderClientStub.connect(this); } super.onResume(); } private void startLicenceCheck() { Intent launchIntent = OurDownloaderActivity.this .getIntent(); Intent intentToLaunchThisActivityFromNotification = new Intent(OurDownloaderActivity .this, OurDownloaderActivity.this.getClass()); intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction()); if (launchIntent.getCategories() != null) { for (String category : launchIntent.getCategories()) { intentToLaunchThisActivityFromNotification.addCategory(category); } } // Build PendingIntent used to open this activity from Notification PendingIntent pendingIntent = PendingIntent.getActivity(OurDownloaderActivity.this, 0, intentToLaunchThisActivityFromNotification, PendingIntent.FLAG_UPDATE_CURRENT); DownloaderService.startLicenceCheck(this, pendingIntent, OurDownloaderService.class); } initializeDownloadUI() { mDownloaderClientStub = DownloaderClientMarshaller.CreateStub (this, OurDownloaderService.class); //do a load of UI setup ... } //This should be called by the Stub's onServiceConnected method /** * Critical implementation detail. In onServiceConnected we create the * remote service and marshaler. This is how we pass the client information * back to the service so the client can be properly notified of changes. We * must do this every time we reconnect to the service. */ @Override public void onServiceConnected(Messenger m) { mRemoteService = DownloaderServiceMarshaller.CreateProxy(m); mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger()); } 

DownloaderService.java (en la biblioteca de expansión del mercado de Google, pero algo editado)

 //this is the onBind call that happens fine; the value it returns is definitely not null @Override public IBinder onBind(Intent paramIntent) { return this.mServiceMessenger.getBinder(); } final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this); final private Messenger mServiceMessenger = mServiceStub.getMessenger(); //MY CODE, derived from Google's code //I have seen the bug occur with a service started by Google's code too, //but this code happens more often so is more repeatably related to the problem public static void startLicenceCheck(Context context, PendingIntent pendingIntent, Class<?> serviceClass) { String packageName = serviceClass.getPackage().getName(); String className = serviceClass.getName(); Intent fileIntent = new Intent(); fileIntent.setClassName(packageName, className); fileIntent.putExtra(EXTRA_LICENCE_EXPIRED, true); fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent); context.startService(fileIntent); } @Override protected void onHandleIntent(Intent intent) { setServiceRunning(true); try { final PendingIntent pendingIntent = (PendingIntent) intent .getParcelableExtra(EXTRA_PENDING_INTENT); if (null != pendingIntent) { mNotification.setClientIntent(pendingIntent); mPendingIntent = pendingIntent; } else if (null != mPendingIntent) { mNotification.setClientIntent(mPendingIntent); } else { Log.e(LOG_TAG, "Downloader started in bad state without notification intent."); return; } if(intent.getBooleanExtra(EXTRA_LICENCE_EXPIRED, false)) { //we are here due to startLicenceCheck updateExpiredLVL(this); return; } ... } } //MY CODE, based on Google's, again public void updateExpiredLVL(final Context context) { Context c = context.getApplicationContext(); Handler h = new Handler(c.getMainLooper()); h.post(new LVLExpiredUpdateRunnable(c)); } private class LVLExpiredUpdateRunnable implements Runnable { LVLExpiredUpdateRunnable(Context context) { mContext = context; } final Context mContext; @Override public void run() { setServiceRunning(true); mNotification.onDownloadStateChanged(IDownloaderClient.STATE_LVL_UPDATING); String deviceId = getDeviceId(mContext); final APKExpansionPolicy aep = new APKExpansionPolicy(mContext, new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId)); // Construct the LicenseChecker with a Policy. final LicenseChecker checker = new LicenseChecker(mContext, aep, getPublicKey() // Your public licensing key. ); checker.checkAccess(new LicenseCheckerCallback() { ... }); } } 

DownloaderClientMarshaller.java (en la biblioteca de expansión del mercado de Google)

 public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) { return new Stub(itf, downloaderService); } 

y la clase Stub del mismo archivo:

 private static class Stub implements IStub { private IDownloaderClient mItf = null; private Class<?> mDownloaderServiceClass; private boolean mBound; private Messenger mServiceMessenger; private Context mContext; /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_ONDOWNLOADPROGRESS: Bundle bun = msg.getData(); if ( null != mContext ) { bun.setClassLoader(mContext.getClassLoader()); DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData() .getParcelable(PARAM_PROGRESS); mItf.onDownloadProgress(dpi); } break; case MSG_ONDOWNLOADSTATE_CHANGED: mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE)); break; case MSG_ONSERVICECONNECTED: mItf.onServiceConnected( (Messenger) msg.getData().getParcelable(PARAM_MESSENGER)); break; } } }); public Stub(IDownloaderClient itf, Class<?> downloaderService) { mItf = itf; mDownloaderServiceClass = downloaderService; } /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { //this is the critical call that never happens public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mServiceMessenger = new Messenger(service); mItf.onServiceConnected( mServiceMessenger); mBound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mServiceMessenger = null; mBound = false; } }; @Override public void connect(Context c) { mContext = c; Intent bindIntent = new Intent(c, mDownloaderServiceClass); bindIntent.putExtra(PARAM_MESSENGER, mMessenger); if ( !c.bindService(bindIntent, mConnection, 0) ) { if ( Constants.LOGVV ) { Log.d(Constants.TAG, "Service Unbound"); } } } @Override public void disconnect(Context c) { if (mBound) { c.unbindService(mConnection); mBound = false; } mContext = null; } @Override public Messenger getMessenger() { return mMessenger; } } 

DownloaderServiceMarshaller.java (en la biblioteca de expansión del mercado de Google, sin cambios)

 private static class Proxy implements IDownloaderService { private Messenger mMsg; private void send(int method, Bundle params) { Message m = Message.obtain(null, method); m.setData(params); try { mMsg.send(m); } catch (RemoteException e) { e.printStackTrace(); } } public Proxy(Messenger msg) { mMsg = msg; } @Override public void requestAbortDownload() { send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle()); } @Override public void requestPauseDownload() { send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle()); } @Override public void setDownloadFlags(int flags) { Bundle params = new Bundle(); params.putInt(PARAMS_FLAGS, flags); send(MSG_SET_DOWNLOAD_FLAGS, params); } @Override public void requestContinueDownload() { send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle()); } @Override public void requestDownloadStatus() { send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle()); } @Override public void onClientUpdated(Messenger clientMessenger) { Bundle bundle = new Bundle(1); bundle.putParcelable(PARAM_MESSENGER, clientMessenger); send(MSG_REQUEST_CLIENT_UPDATE, bundle); } } private static class Stub implements IStub { private IDownloaderService mItf = null; final Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REQUEST_ABORT_DOWNLOAD: mItf.requestAbortDownload(); break; case MSG_REQUEST_CONTINUE_DOWNLOAD: mItf.requestContinueDownload(); break; case MSG_REQUEST_PAUSE_DOWNLOAD: mItf.requestPauseDownload(); break; case MSG_SET_DOWNLOAD_FLAGS: mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS)); break; case MSG_REQUEST_DOWNLOAD_STATE: mItf.requestDownloadStatus(); break; case MSG_REQUEST_CLIENT_UPDATE: mItf.onClientUpdated((Messenger) msg.getData().getParcelable( PARAM_MESSENGER)); break; } } }); public Stub(IDownloaderService itf) { mItf = itf; } @Override public Messenger getMessenger() { return mMessenger; } @Override public void connect(Context c) { } @Override public void disconnect(Context c) { } } /** * Returns a proxy that will marshall calls to IDownloaderService methods * * @param ctx * @return */ public static IDownloaderService CreateProxy(Messenger msg) { return new Proxy(msg); } /** * Returns a stub object that, when connected, will listen for marshalled * IDownloaderService methods and translate them into calls to the supplied * interface. * * @param itf An implementation of IDownloaderService that will be called * when remote method calls are unmarshalled. * @return */ public static IStub CreateStub(IDownloaderService itf) { return new Stub(itf); } 

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.