developers - android service http



Android come posso aspettare fino a quando un servizio è effettivamente connesso? (4)

Come posso attendere che ServiceConnection.onServiceConnected venga chiamato in modo affidabile?

Non lo fai. Esci da onCreate() (o dovunque tu sia onCreate() ) e metti il ​​codice " onServiceConnected() the connection established" in onServiceConnected() .

Sono tutti i gestori di eventi: Activity.onCreate, qualsiasi View.onClickListener.onClick, ServiceConnection.onServiceConnected, ecc. Effettivamente chiamati nello stesso thread

Sì.

Quando si chiamerà in realtà ServiceConnection.onServiceConnected? Al completamento di Activity.onCreate o quando AoC è ancora in esecuzione?

Probabilmente la tua richiesta di collegamento non inizierà nemmeno fino a quando non lasci onCreate() . Quindi, onServiceConnected() verrà chiamato qualche volta dopo aver lasciato onCreate() .

Ho un'attività che chiama un servizio definito in IDownloaderService.aidl:

public class Downloader extends Activity {
 IDownloaderService downloader = null;
// ...

In Downloader.onCreate (Bundle) ho provato a associareService

Intent serviceIntent = new Intent(this, DownloaderService.class);
if (bindService(serviceIntent, sc, BIND_AUTO_CREATE)) {
  // ...

e all'interno dell'oggetto ServiceConnection sc ho fatto questo

public void onServiceConnected(ComponentName name, IBinder service) {
  Log.w("XXX", "onServiceConnected");
  downloader = IDownloaderService.Stub.asInterface(service);
  // ...

Aggiungendo tutti i tipi di Log.xx ho scoperto che il codice dopo if (bindService (...)) va effettivamente BEFORE Viene chiamato ServiceConnection.onServiceConnected - cioè, quando il downloader è ancora nullo - che mi mette nei guai. Tutti i campioni in ApiDemos evitano questo problema di temporizzazione chiamando solo i servizi quando vengono attivati ​​dalle azioni dell'utente. Ma cosa dovrei fare per usare correttamente questo servizio dopo che bindService ha avuto successo? Come posso attendere che ServiceConnection.onServiceConnected venga chiamato in modo affidabile?

Un'altra domanda correlata. Sono tutti i gestori di eventi: Activity.onCreate, qualsiasi View.onClickListener.onClick, ServiceConnection.onServiceConnected, ecc. Effettivamente chiamato nello stesso thread (menzionato nel doc come "thread principale")? Ci sono interleave tra di loro, o Android programmerebbe che tutti gli eventi vengano gestiti uno per uno? Oppure, quando viene chiamato in realtà ServiceConnection.onServiceConnected? Al completamento di Activity.onCreate o quando AoC è ancora in esecuzione?


Ho avuto lo stesso problema. Non volevo inserire il mio codice dipendente dal servizio associato in onServiceConnected , però, perché volevo collegarlo / onStart con onStart e onStop, ma non volevo che il codice si onStop, ogni volta che l'attività tornava in primo piano. Volevo solo che fosse eseguito quando l'attività è stata creata per la prima volta.

Finalmente ho superato la mia visione del tunnel onStart() e ho usato un booleano per indicare se si trattava della prima esecuzione onServiceConnected o meno. In questo modo, posso unbindService in onStop e bindService di nuovo in onStart senza dover eseguire tutte le attività di avvio ogni volta.


Ho fatto qualcosa di simile prima, l'unica differenza è che non ero vincolante al servizio, ma solo all'inizio.

Trasmetterei un intento dal servizio per informare il chiamante / l'attività su di esso è stata avviata.


* L'idea di base è la stessa con @ 18446744073709551615, ma condividerò anche il mio codice.

Come risposta alla domanda principale,

Ma cosa dovrei fare per usare correttamente questo servizio dopo che bindService ha avuto successo?

[Aspettativa originale (ma non lavoro)]

attendere fino a quando il servizio è collegato come di seguito

    @Override
    protected void onStart() {
        bindService(service, mWebServiceConnection, BIND_AUTO_CREATE);
        synchronized (mLock) { mLock.wait(40000); }

        // rest of the code continues here, which uses service stub interface
        // ...
    }

Non funzionerà perché entrambi bindService() in onCreate()/onStart() e onServiceConnected() vengono chiamati allo stesso thread principale . onServiceConnected() non viene mai chiamato prima che termini l'attesa.

[Soluzione alternativa]

Invece di "attendere", definire il proprio Runnable da chiamare dopo Service Connected ed eseguire questo runnable dopo il servizio connesso.

Implementare la classe personalizzata di ServiceConnection nel modo seguente.

public class MyServiceConnection implements ServiceConnection {

    private static final String TAG = MyServiceConnection.class.getSimpleName();

    private Context mContext = null;
    private IMyService mMyService = null;
    private ArrayList<Runnable> runnableArrayList;
    private Boolean isConnected = false;

    public MyServiceConnection(Context context) {
        mContext = context;
        runnableArrayList = new ArrayList<>();
    }

    public IMyService getInterface() {
        return mMyService;
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.v(TAG, "Connected Service: " + name);
        mMyService = MyService.Stub.asInterface(service);

        isConnected = true;
        /* Execute runnables after Service connected */
        for (Runnable action : runnableArrayList) {
            action.run();
        }
        runnableArrayList.clear();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        try {
            mMyService = null;
            mContext.unbindService(this);
            isConnected = false;
            Log.v(TAG, "Disconnected Service: " + name);
        } catch(Exception e) {
            Log.e(TAG, e.toString());
        }
    }

    public void executeAfterServiceConnected(Runnable action) {
        Log.v(TAG, "executeAfterServiceConnected");
        if(isConnected) {
            Log.v(TAG, "Service already connected, execute now");
            action.run();
        } else {
            // this action will be executed at the end of onServiceConnected method
            Log.v(TAG, "Service not connected yet, execute later");
            runnableArrayList.add(action);
        }
    }
}

E poi usalo nel modo seguente (nella tua classe di attività o ecc.),

private MyServiceConnection myServiceConnection = null;

@Override
protected void onStart() {
    Log.d(TAG, "onStart");
    super.onStart();

    Intent serviceIntent = new Intent(getApplicationContext(), MyService.class);
    startService(serviceIntent);
    myServiceConnection = new MyServiceConnection(getApplicationContext());
    bindService(serviceIntent, myServiceConnection, BIND_AUTO_CREATE);

    // Instead of "wait" here, create callback which will be called after service is connected
    myServiceConnection.executeAfterServiceConnected(new Runnable() {
        @Override
        public void run() {
            // Rest of the code comes here.
            // This runnable will be executed after service connected, so we can use service stub interface
            IMyService myService = myServiceConnection.getInterface();
            // ...
        }
    });
}

Ha funzionato per me. Ma potrebbe esserci un modo migliore.





serviceconnection