安卓服务显示Toast

49
这段代码本来应该使用一个服务来展示一个消息提示框。尽管没有出现错误,但是并没有弹出提示框。 主活动
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>

“但似乎它并没有起作用”,就像……发生了什么?你是否收到了错误提示?如果是这样,它是什么?你是否进行了任何调试来查看哪些内容被调用或未被调用? - codeMagic
1
抱歉,我会澄清一下... - codenamejupiterx
2
可能是重复问题 http://stackoverflow.com/questions/6824050/why-isnt-my-service-running?rq=1 - StarsSky
2
问题6824050帮了我很多..谢谢StarsSky! - codenamejupiterx
使用getApplicationContext()代替this - Phillip Kigenyi
3个回答

102

试一下这个:

new Handler(Looper.getMainLooper()).post(new Runnable() {

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

3
它确实显示了弹窗通知,但会一直保持屏幕亮着。 - hellojinjie
你为什么认为你的解决方案适用于 OP 的问题? - IgorGanapolsky

26

现在http://developer.android.com/guide/components/services.html#Notifications上说:“一旦运行,服务可以使用Toast通知或状态栏通知通知用户事件。”看起来这些通知方法不算作常规应用程序“UI”。 - Ciro Santilli OurBigBook.com
一个Service可以更新UI,但是Intent Service不行。 - codeMagic

0
最好将所有GUI活动(包括toast)委托给使用您的服务的Activity。例如,我有一个绑定服务用于在后台进行位置捕获并在我的应用程序可见时向屏幕发布更新。
我的应用程序实现了一个简单的接口:
public interface ICapture {
    void update(Location location);
}

我的类定义看起来像这样:

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

我使用这些方法来告诉服务应用程序何时可见并能够满足GUI请求:

@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()的实现确保服务知道何时应用程序可以接受调用。

要做一个toast弹出窗口,请向ICapture接口添加另一个方法并在应用程序中实现它。您的服务随时可以调用它,只要它知道屏幕可以接受它。事实上,即使应用程序不在前台,toast弹出窗口仍然会显示,但我认为当屏幕变为非活动状态时,它们会被阻止,从而阻止了服务。因此,最好只在应用程序在前台时发送它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接