onServiceConnected wird auf einigen Geräten manchmal nicht nach bindService aufgerufen

Ich habe eine Reihe anderer Threads mit ähnlichen Titeln angeschaut, und keiner scheint mein Problem zu behandeln. Also, hier geht.

Ich verwende die Google Market Expansion-Dateibibliothek (apkx) und den Beispielcode mit einigen Änderungen. Dieser Code basiert auf dem Empfang von Rückrufen von einem Dienst, der das Herunterladen im Hintergrund, Lizenzprüfungen usw. abwickelt.

Ich habe einen Fehler, bei dem der Dienst nicht richtig zugeordnet wird, was zu einem Softlock führt. Um dies weniger hilfreich zu machen, tritt dieser Fehler auf einigen Geräten nie auf, auf anderen jedoch in etwa zwei Dritteln der Fälle. Ich glaube, es ist unabhängig von der Android-Version, sicherlich habe ich zwei Geräte mit 2.3.4, von denen eines (ein Nexus S) nicht das Problem hat, das andere (ein HTC Evo 3D).

Um zu versuchen, eine Verbindung zum Dienst herzustellen, wird bindService aufgerufen und gibt true zurück. OnBind wird dann wie erwartet aufgerufen und gibt einen sinnvollen Wert zurück, aber (wenn der Fehler auftritt) onServiceConnected tritt nicht auf (ich habe 20 Minuten gewartet, nur für den Fall).

Hat noch jemand so etwas gesehen? Wenn nicht, irgendwelche Vermutungen, was ich getan haben könnte, um ein solches Verhalten zu verursachen? Wenn sich niemand Gedanken macht, poste ich morgen einen Code.

EDIT: Hier ist der relevante Code. Wenn ich etwas verpasst habe, bitte fragen Sie.

Beim Hinzufügen dieses Codes habe ich einen kleinen Fehler gefunden. Das Beheben des Problems hat die Häufigkeit des Problems verursacht, das ich zu lösen versuche. Auf dem Telefon, auf dem ich es teste, wurde der Wert von 2 mal in 3 auf etwa 1 mal in 6 geändert. Keine Ahnung über Effekte auf anderen Handys. Dies legt mir weiterhin eine Rennbedingung oder ähnliches nahe, aber ich habe keine Ahnung, womit.

OurDownloaderActivity.java (kopiert und geändert aus dem Google-Beispielcode)

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 (in der Google-Markterweiterungsbibliothek, aber etwas bearbeitet)

//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 (in der Google-Markterweiterungsbibliothek)

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

und die Stub-Klasse aus derselben Datei:

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 (in der Google-Markterweiterungsbibliothek unverändert)

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);
}

Antworten auf die Frage(0)

Ihre Antwort auf die Frage