如何从活动中调用服务的方法?

43

我只想从我的活动中调用本地服务的方法。我该怎么做?

6个回答

86

这里有一个可能会有所帮助的示例
Server.java:

package com.example.bindservice.binder;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class Server extends Service {

    IBinder mBinder = new LocalBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public class LocalBinder extends Binder {
        public Server getServerInstance() {
            return Server.this;
        }
    }

    public String getTime() {
        SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return mDateFormat.format(new Date());
    }
}

Client.java

package com.example.bindservice.binder;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.example.bindservice.binder.Server.LocalBinder;

public class Client extends Activity {

    boolean mBounded;
    Server mServer;
    TextView text;
    Button button;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        text = (TextView)findViewById(R.id.text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                text.setText(mServer.getTime());
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();

        Intent mIntent = new Intent(this, Server.class);
        bindService(mIntent, mConnection, BIND_AUTO_CREATE);
    };

    ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(Client.this, "Service is disconnected", 1000).show();
            mBounded = false;
            mServer = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(Client.this, "Service is connected", 1000).show();
            mBounded = true;
            LocalBinder mLocalBinder = (LocalBinder)service;
            mServer = mLocalBinder.getServerInstance();
        }
    };

    @Override
    protected void onStop() {
        super.onStop();
        if(mBounded) {
            unbindService(mConnection);
            mBounded = false;
        }
    };
}

1
如果服务被强制停止,我会收到onServiceDisconnected的回调吗? - Vivek MVK
68
为什么调用一个简单的函数如此困难? - Zulqurnain Jutt
@Mr.Z 也许你没有在Android中将服务绑定到活动。https://dev59.com/bnI-5IYBdhLWcg3wTWn4 - CrandellWS
这应该是正确的答案。谢谢你,你节省了我的时间。 - Dinith Rukshan Kumara
@Prateek Yadav:我们真的需要mBounded吗?我们不能只检查mServer!= null吗? - markzzz

31

15

如何调用普通的Java方法?

A obj = new A();
obj.method();

Service是一个Java类。那么如何调用服务方法?

serviceObj.method();

现在真正的问题是如何创建Service对象?

Service serviceObj = new Service();

肯定不是这样。

在Android中,Service是由Android操作系统创建、销毁和管理的系统组件。
要创建service对象,需要IBinder

以下是从IBinder获取Service对象的方法。

enter image description here

一旦获得了serviceObject,它就像任何普通的Java对象一样工作。
上面解释的内容被称为绑定服务(Binding a Service)

绑定使得Activity可以观察后台服务。通过绑定,我们可以进行双向通信,即Activity<--->Service
Prateek Yadav已经提供了一个很好的代码片段。您可以使用它。

需要记住的几件事:

  • 永远不要忘记取消绑定服务,否则会导致资源泄漏。
  • 您可以以任何顺序调用startService(intent)bindService(mIntent, mConnection, BIND_AUTO_CREATE)。绑定和启动服务是两个独立的事情。

2

Kotlin的等效代码

MainActivity.kt

private var mBounded = false

private var foregroundService: ForegroundService? = null

 override fun onPostCreate(savedInstanceState: Bundle?) {
    super.onPostCreate(savedInstanceState)

    btn_start_service.setOnClickListener { startMyService(); }

    btn_stop_service.setOnClickListener { stopMyService(); }

    mConnection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName) {
            Toast.makeText(this@MainActivity, "Service is disconnected", Toast.LENGTH_SHORT)
                .show()
            mBounded = false
            foregroundService = null
        }

        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            Toast.makeText(this@MainActivity, "Service is connected", Toast.LENGTH_SHORT).show()
            mBounded = true
            val mLocalBinder = service as LocalBinder
            foregroundService = mLocalBinder.getServerInstance()
        }
    }

    val startIntent = Intent(this, ForegroundService::class.java)
    bindService(startIntent, mConnection as ServiceConnection, Context.BIND_AUTO_CREATE);
}


private fun startMyService() {
    foregroundService!!.startService(this, "sdds")

}

  private fun stopMyService() {
    if (mBounded) {
        mConnection?.let { unbindService(it) };
        mBounded = false;
    }
    val stopIntent = Intent(this, ForegroundService::class.java)
    stopService(stopIntent)
}

ForegroundService.kt

class ForegroundService : Service() {

private val CHANNEL_ID = "ForegroundService Kotlin"

var mBinder: IBinder = LocalBinder()

fun startService(context: Context, message: String) {
    val startIntent = Intent(context, ForegroundService::class.java)
    startIntent.putExtra("inputExtra", message)
    ContextCompat.startForegroundService(context, startIntent)
}

fun stopService(context: Context) {
    val stopIntent = Intent(context, ForegroundService::class.java)
    context.stopService(stopIntent)
}

override fun onBind(intent: Intent?): IBinder? {
    return mBinder
}


class LocalBinder : Binder() {
    fun getServerInstance(): ForegroundService? {
        return ForegroundService()
    }
}}

2
一种方法是通过使用Android的AIDL定义接口,并利用Binder子系统执行IPC。我在发布的链接中提供了一组非常好的说明。建议您从那里开始,如果有问题再来这里发帖问。尽管IPC是一个相当复杂的主题,但Android和Binder确实做得非常好(至少在入门方面,如果您想要的话,肯定可以把它搞得更复杂;-))。 编辑:正如评论中指出的那样,如果Service和客户端在同一个进程中运行,则此操作是不必要的。除非另有规定,否则这是默认设置。然而,即使如此,它仍然能够工作,只是增加了一些复杂性。

4
只有当服务和活动位于不同的应用程序中时才需要使用AIDL。如果您的服务和活动是本地应用程序(这是常见情况),请参阅@hackbod的回复。 - odedfos
@odedfos 你说的部分正确。使用AIDL的必要性取决于调用方和被调用方是否在不同的“进程”中,而不一定是不同的应用程序。大多数情况下,应用程序只有一个进程,因此这不是问题,但即使在同一个应用程序中,您也可以指定服务在与UI不同的进程中运行。 - Chris Thompson
1
根据定义,本地服务不使用IPC,而问题是关于本地服务的,因此这个答案是错误的。 - User
@Ixx 默认情况下它们不会,但是您可以指定它们在单独的进程中运行。此外,这确实有效,因此它并没有错误,只是比在客户端中运行服务相同的进程需要更复杂和更多的开销。 - Chris Thompson

0

我不知道你的问题出在哪里,请贴上一些代码。 使用Binder,Activity可以访问服务对象。请参阅API中有关创建活动和服务之间连接的示例。

在您的活动中拥有服务对象后,您只需调用:
mService.yourMethod();
如果您能确切地描述您的问题并像我说的那样发布一些代码片段,我们可以更好地帮助您。


6
为什么不在文章中加入一些有用资源的链接呢?可以发布一些关于如何绑定服务并随后调用其方法的框架代码片段。 :) - Juri
@Juri https://dev59.com/bnI-5IYBdhLWcg3wTWn4 突然之间被链接了。 - CrandellWS

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