Android-сервис, чтобы показать тост

Этот код должен использовать службу для отображения тоста. Ошибок нет, но он не показывает тост.

основная деятельность

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

}



}

сервис (его называемая фоновая музыка, но пока она должна показывать тост-сообщение)

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



}

манифест

<?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>

Ответ 1

См. Эту часть документов

(У IntentService есть несколько ограничений:

Он не может напрямую взаимодействовать с вашим пользовательским интерфейсом. Чтобы привести свои результаты в пользовательский интерфейс, вы должны отправить их в Activity.

Вам нужно поместить его в главную Thread. См. Ответ здесь, один из способов сделать это.

и из полной документации по IntentService

обрабатывает каждое намерение, в свою очередь, используя рабочий поток

Ответ 2

Попробуй это:

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

Ответ 3

Вероятно, лучше всего делегировать все действия GUI (включая тосты) для Activity, использующего вашу службу. Например, у меня есть связанная служба для выполнения захвата местоположения в фоновом режиме и публикации обновлений на экране, пока мое приложение видно.

Мое приложение реализует простой интерфейс:

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

и мой класс def выглядит следующим образом:

public class MyActivity extends Activity implements ICapture {
...

Здесь материал для обработки связанного сервиса:

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

Единственное, что здесь не стандартное, - это линия

captureService.setOwner(ICapture.this);

который предоставляет услугу ссылку на реализацию приложения ICapture. См. Ниже, как он используется.

Я запускаю службу в onCreate():

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

и я использую эти методы, чтобы сообщить службе, когда приложение видно и способно удовлетворить запросы графического интерфейса:

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

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

Служба выглядит так:

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

Все это довольно стандартный код, который вы можете найти в другом месте. Главное, что при обновлении местоположения код вызывает приложение через реализованный интерфейс ICapture, но только если приложение видно. Реализация onPause() и onResume() в приложении гарантирует, что служба знает, когда приложение может принимать вызовы.

Чтобы сделать тост, добавьте еще один метод в интерфейс ICapture и внесите его в приложение. Затем ваш сервис может позвонить ему в любое время, когда он узнает, что экран может его принять. На самом деле, всплывающие окна с тостами все равно появятся, даже когда приложение не находится на переднем плане, но я считаю, что они блокируются, когда экран неактивен, что, в свою очередь, блокирует службу. Поэтому лучше отправлять их только тогда, когда приложение находится на переднем плане.