web-dev-qa-db-fra.com

Android Service pour afficher des toasts

Ce code est censé utiliser un service pour afficher un message toast. Il n'y a aucune erreur, mais cela ne montre pas le toast.

activité principale

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Intent i= new Intent(this, BackgroundMusic.class);
    this.startService(i); 

}



}

service (son appelé musique de fond, mais pour l'instant, il est censé afficher un message toast)

public class BackgroundMusic extends IntentService {

 public BackgroundMusic() {
      super("BackgroundMusic");
  }



 @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
     Context context = getApplicationContext();
     CharSequence text = "Hello toast!";
     int duration = Toast.LENGTH_SHORT;

     Toast toast = Toast.makeText(context, text, duration);
     toast.show();
 }



}

manifeste

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
package="com.example.starwars"
Android:versionCode="1"
Android:versionName="1.0" >

<uses-sdk
    Android:minSdkVersion="8"
    Android:targetSdkVersion="18" />

<application
    Android:allowBackup="true"
    Android:debuggable="true"
    Android:icon="@drawable/ic_launcher"
    Android:label="@string/app_name"
    Android:theme="@style/AppTheme" >
     <service Android:name=".BackgroundMusic" />
    <activity
        Android:name="com.example.starwars.MainActivity"
        Android:label="@string/app_name" >
        <intent-filter>
            <action Android:name="Android.intent.action.MAIN" />

            <category Android:name="Android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity Android:label="@string/app_name" Android:name="BackgroundMusic"/>
</application>

</manifest>
33
codenamejupiterx

Voir cette partie de la documentation

(Un IntentService a quelques limitations:

Il ne peut pas interagir directement avec votre interface utilisateur. Pour mettre ses résultats dans l'interface utilisateur, vous devez les envoyer à une activité.

Vous devez le mettre sur le Thread principal. Voir la réponse ici par rony d'une façon de le faire.

et du documentation complète sur IntentService

gère chaque intention à son tour à l'aide d'un thread de travail

22
codeMagic

Essaye ça:

Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {

    @Override
    public void run() {
            Toast.makeText(YourService.this.getApplicationContext(),"My Awesome service toast...",Toast.LENGTH_SHORT).show();
            }
        });
75
Gal Rom

Il est probablement préférable de déléguer toutes les activités de l'interface graphique (y compris les toasts) à l'activité qui utilise votre service. Par exemple, j'ai un service lié pour effectuer une capture de position en arrière-plan et publier des mises à jour à l'écran lorsque mon application est visible.

Mon application implémente une interface simple:

public interface ICapture {
    void update(Location location);
}

et ma définition de classe ressemble à ceci:

public class MyActivity extends Activity implements ICapture {
...

Voici les éléments pour gérer le service lié:

private CaptureService captureService;
private ServiceConnection captureServiceConnection = new ServiceConnection() {

    public void onServiceConnected(ComponentName className, IBinder service) {
        CaptureService.MyLocalBinder binder = (CaptureService.MyLocalBinder) service;
        captureService = binder.getService();
        captureService.setOwner(ICapture.this);
    }

    public void onServiceDisconnected(ComponentName arg0) {
    }
};

La seule chose ici qui n'est pas standard est la ligne

captureService.setOwner(ICapture.this);

qui fournit au service une référence à la mise en œuvre de ICapture par l'application. Voir ci-dessous pour savoir comment il est utilisé.

Je démarre le service dans onCreate ():

    Intent intent = new Intent(this, CaptureService.class);
    startService(intent);
    bindService(intent, captureServiceConnection, Context.BIND_AUTO_CREATE);

et j'utilise ces méthodes pour indiquer au service lorsque l'application est visible et capable de répondre aux demandes de l'interface graphique:

@Override
public void onPause() {
    super.onPause();
    if (captureService != null) {
        captureService.setOwner(null);
    }
}

@Override
public void onResume() {
    super.onResume();
    if (captureService != null) {
        captureService.setOwner(this);
    }
}

Le service ressemble à ceci:

package *****;

import Android.app.Service;
import Android.content.Intent;
import Android.location.Location;
import Android.os.Binder;
import Android.os.Bundle;
import Android.os.IBinder;

import com.google.Android.gms.common.ConnectionResult;
import com.google.Android.gms.common.GooglePlayServicesUtil;
import com.google.Android.gms.common.api.GoogleApiClient;
import com.google.Android.gms.location.LocationRequest;
import com.google.Android.gms.location.LocationServices;

public class CaptureService extends Service implements
        com.google.Android.gms.location.LocationListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final long UPDATE_INTERVAL = 1000 * 10;
    private static final long FASTEST_INTERVAL = 1000 * 5;

    private final IBinder myBinder = new MyLocalBinder();

    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;
    private ICapture owner;

    @Override
    public void onCreate() {
        if (isGooglePlayServicesAvailable()) {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
            mLocationRequest = new LocationRequest();
            mLocationRequest.setInterval(UPDATE_INTERVAL);
            mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            mGoogleApiClient.connect();
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
    }

    /**************************************************************************
     * The binder that returns the service activity.
     */
    public class MyLocalBinder extends Binder {
        public CaptureService getService() {
            return CaptureService.this;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return myBinder;
    }

    /**************************************************************************
     * Bound methods.
     *
     * Set the owner, to be notified when the position changes.
     *
     * @param owner
     */
    public void setOwner(ICapture owner) {
        this.owner = owner;
    }

    /**************************************************************************
     * Start the service and keep it running when the phone is idle.
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    /**
     * Callback when the location changes. Inform the owner.
     *
     * @param location
     */
    @Override
    public void onLocationChanged(Location location) {
        if (owner != null) {
            owner.update(location);
        }
    }

    private boolean isGooglePlayServicesAvailable() {
        int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (ConnectionResult.SUCCESS == status) {
            return true;
        } else {
            return false;
        }
    }
}

Tout cela est un code assez standard que vous pouvez trouver ailleurs. L'essentiel est que lorsqu'une mise à jour d'emplacement se produit, le code appelle l'application via son interface ICapture implémentée, mais niquement si l'application est visible. L'implémentation de onPause () et onResume () dans l'application garantit que le service sait quand l'application peut accepter des appels.

Pour faire un toast popup, ajoutez une autre méthode à l'interface ICapture et implémentez-la dans l'application. Votre service peut alors l'appeler chaque fois qu'il sait que l'écran peut l'accepter. En fait, les pop-ups toast apparaîtront toujours même lorsque l'application n'est pas au premier plan, mais je pense qu'ils sont bloqués lorsque l'écran devient inactif, ce qui bloque à son tour le service. Il est donc préférable de les envoyer uniquement lorsque l'application est au premier plan.

0
Graham