关于android:如何将Activity绑定到Service并从Activity控制和管理Service

How to bind an Activity to a Service and control and manage the Service from the Activity

我正在尝试将 Activity 绑定到 LocalService 以与之交互。但是在我的 Activity 中,我只能调用 LocalBinder 中定义的方法,而不能调用 LocalService 中定义的方法。我做错了什么?

没有从头开始我阅读了另一个问题,并且我已经阅读了一些如何编写一些示例代码的内容,我的代码类似于该示例代码。此外,为了方便起见,我一直在阅读一些服务文档,这里是文档该部分的一小段引述:

"当应用程序组件通过调用 bindService() 绑定到服务时,服务被"绑定"。绑定服务提供客户端-服务器接口,允许组件与服务交互、发送请求、获取结果,甚至执行所以通过进程间通信(IPC)跨进程。绑定的服务只有在另一个应用程序组件绑定到它时才会运行。多个组件可以一次绑定到服务,但是当它们全部解除绑定时,服务就会被销毁。"

但我不能那样做。如上所述,我能做的最好的事情就是在我的 LocalBinder 中定义我的 Activity 调用方法。我没有像上面黑色突出显示的部分那??样取得任何成就。

如果有帮助,这里是我的代码的相关部分。

要绑定的LocalService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/**************************************************************************************************
 * Filename: LocalService.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains the LocalService (extends Service) for our Local Service app
 **************************************************************************************************/

package com.marie.localservicesample;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class LocalService extends Service {
    private NotificationManager mNM;

    // Unique Identification Number for the Notification.
    // We use it on Notification start, and to cancel it.
    private int NOTIFICATION = R.string.local_service_started;

    // just some arbitrary numbers for test purposes
    public static int statusCode = 99;
    public static  int emptyMsg = 549;

    // I get my Extras from onStartCommand and use in ServiceWorker() thread
    public static final String EXTRA_MAC ="com.marie.localservicesample.EXTRA_MAC";
    private String macString;

    public static final String EXTRA_MESSENGER ="com.marie.localservicesample.EXTRA_MESSENGER";
    private Messenger messenger;

    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    //private static final String macString ="00:06:66:02:D0:EC";

    Boolean stop_receive_data = false;

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example - or not because
    // this is a local service
    private final IBinder mBinder =  new LocalBinder();

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("onBind","called in LocalService" );
        Log.i("onBind","intent:" + intent.toString());
        Log.i("onBind","mBinder:" + mBinder);
        return mBinder;
    }

    @Override
    public void onCreate() {
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

        // Display a notification about us starting.  We put an icon in the status bar.
        showNotification();
    }

    // Call this at the end of onStartCommand() after we got the Extras
    public void afterStartCommand() {
        Thread thr = new Thread(null, new ServiceWorker(),"LocalService");
        thr.start();  
    }

    /*
     * This is the ServiceWorker thread that passes messages to the handler defined in
     * the Controller activity.
     */
    class ServiceWorker implements Runnable
    {
        public void run() {
            // do background processing here... something simple
            Looper.prepare();

            BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
            BluetoothDevice btDevice = btAdapter.getRemoteDevice(macString);
            BluetoothSocket btSocket = null;
            InputStream btIstream = null;
            OutputStream btOstream = null;
            try {
                btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                btSocket.connect();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                btIstream = btSocket.getInputStream();
                btOstream = btSocket.getOutputStream();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                int data = btIstream.read();

                // reset the bluetooth device
                while (data != 63) {
                    Log.d("LocalService","resetting bluetooth device");
                    btOstream.write('r');
                    data = btIstream.read();
                }

                StringBuffer strBuffer = new StringBuffer("");
                Boolean dataBegin = false;

                int ndxPlus = 0;

                while (data != -1) {
                    char printableB = (char) data;
                    if (data < 32 || data > 126) {
                        //printableB = ' ';
                    }              
                    //Log.d("LocalService", Character.toString(printableB) +"(" + data +")");

                    if (data == 63) {
                        btOstream.write('$');
                        btOstream.write(',');
                    }
                    if (data == 45) {
                        btOstream.write('1');
                        btOstream.write(',');
                        dataBegin = true;
                    }
                    if (dataBegin == true) {
                        strBuffer = strBuffer.append(Character.toString(printableB));
                    }
                    if (data == 13) {
                        dataBegin = false;
                        //Log.d("LocalServiceDataString", strBuffer.toString());

                        // send data to the handler to plot the data
                        Message msg = Message.obtain();
                        msg.what = Controller.MESSAGE_MAC;
                        msg.obj = strBuffer;
                        try {
                            messenger.send(msg);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }

                        strBuffer = new StringBuffer("");
                        if (ndxPlus < 0) {
                            btOstream.write('+');
                            ndxPlus++;
                        }
                    }

                    data = btIstream.read();
                    if (stop_receive_data) data = -1;

                }

            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                btSocket.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }

            LocalService.this.stopSelf();
            Looper.loop();
            // stop the service when done...
            // Or use the unbindBtn in the MainActivity class?
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("LocalService","Received start id" + startId +":" + intent);

        Bundle extras = intent.getExtras();

        messenger = (Messenger)extras.get(EXTRA_MESSENGER);
        macString = extras.getString(EXTRA_MAC);

        afterStartCommand();

        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        // Cancel the persistent notification.
        mNM.cancel(NOTIFICATION);
        stop_receive_data = true;
        // Tell the user we stopped.
        Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
    }

    /**
     * Show a notification while this service is running.
     */
    private void showNotification() {
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.local_service_started);

        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis());

        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0);

        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.local_service_label), text, contentIntent);

        // Send the notification.
        mNM.notify(NOTIFICATION, notification);
    }
}

绑定到 LocalService 的活动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**************************************************************************************************
 * Filename: Binding.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains the Binding class for our Local Service application
 **************************************************************************************************/
package com.marie.localservicesample;

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

/*
 * Example of binding and unbinding to the local service.
 * This demonstrates the implementation of a service which the client will
 * bind to, receiving an object through which it can communicate with the service.
 */

public class Binding extends Activity {
    private ILocalBinder mBoundService;
    private boolean mIsBound;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  Because we have bound to a explicit
            // service that we know is running in our own process, we can
            // cast its IBinder to a concrete class and directly access it.
            mBoundService = (ILocalBinder)service;

            int statusCode = mBoundService.getStatusCode();

            Log.d("Binding.java","called onServiceConnected. statusCode:" + statusCode);

            Toast.makeText(Binding.this, R.string.local_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            // Because it is running in our same process, we should never
            // see this happen.
            mBoundService = null;

            Log.d("Binding","called onServiceDisconnected");

            Toast.makeText(Binding.this, R.string.local_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    void doBindService() {
        // Establish a connection with the service.  We use an explicit
        // class name because we want a specific service implementation that
        // we know will be running in our own process (and thus won't be
        // supporting component replacement by other applications).
        bindService(new Intent(Binding.this, LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    void doUnbindService() {
        if (mIsBound) {
            int statusCode = mBoundService.getStatusCode();
            if (statusCode != 0) Log.d("doUnbindService","Binding.java statusCode:" + statusCode);

            // Tell the user we did an unbind
            Toast.makeText(this, R.string.local_service_unbound, Toast.LENGTH_SHORT).show();

            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.local_service_binding);

        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(mUnbindListener);
    }

    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
            doBindService();
        }
    };

    private OnClickListener mUnbindListener = new OnClickListener() {
        public void onClick(View v) {
            doUnbindService();
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }
}

我的 ILocalBinder 和 LocalBinder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**************************************************************************************************
 * Filename: ILocalBinder.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains an example interface for my LocalBinder
 **************************************************************************************************/

package com.marie.localservicesample;

public interface ILocalBinder {

    public int getStatusCode();
}

/**************************************************************************************************
 * Filename: LocalBinder.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains the LocalBinder class for our Local Service application
 **************************************************************************************************/

package com.marie.localservicesample;

import android.os.Binder;

import com.marie.localservicesample.LocalService;

/**
 * Class for clients to access.  Because we know this service always
 * runs in the same process as its clients, we don't need to deal with
 * IPC.
 */
public class LocalBinder extends Binder implements ILocalBinder {
    @Override
    public int getStatusCode() {
        return LocalService.statusCode;
    }

}

谢谢!


参见本地服务示例。

只需将它们拥有的 binder 类代码复制到您的服务中,而不是为其创建单独的文件:(在 LocalService 类声明中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LocalService {
    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();

    /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }

    ...
}

然后:

1
2
3
4
5
6
7
public void onServiceConnected(ComponentName className, IBinder service) {
    // This is called when the connection with the service has been
    // established, giving us the service object we can use to
    // interact with the service.  Because we have bound to a explicit
    // service that we know is running in our own process, we can
    // cast its IBinder to a concrete class and directly access it.
    mBoundService = ((LocalService.LocalBinder)service).getService();

现在您可以使用 mBoundService 直接访问您的服务。