Android: 绑定到远程服务

Android: Binding to a remote service

我正在构建一个远程服务和一个客户端应用程序,目标是 API 24 在 Nexus 6P 设备上执行。我有一个在开机时自动启动的远程服务。以下是代码片段:

远程服务清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="a.b.c">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <service android:name=".MyService" >
            <intent-filter>
                <action android:name="a.b.c.MY_INTENT" />
            </intent-filter>
        </service>

        <activity android:name=".MyActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

远程服务

package a.b.c;

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

public class MyService extends Service
{
    @Override
    public IBinder onBind(Intent intent) {
        return null;
        // EDIT: see Whosebug answers/comments below:
        // Returning an IBinder here solves the problem.
        // e.g. "return myMessenger.getBinder()" where myMessenger
        // is an instance of Android's Messenger class.
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

远程广播接收器

package a.b.c;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
            Intent serviceIntent = new Intent(context, MyService.class);
            context.startService(serviceIntent);
        }
    }
}

远程Activity (Android Studio 坚持要有 Activity)

package a.b.c;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MyActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);
    }
}

然后我有一个单独的项目来在尝试绑定到远程服务的不同包中实现客户端 activity。以下是代码片段:

客户端清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="x.y.z">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ClientActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

客户端Activity

package x.y.z;

import android.app.ActivityManager;
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.support.v7.app.AppCompatActivity;
import android.util.Log;

public class ClientActivity extends AppCompatActivity
{
    private final String TAG = "ClientActivity";

    private ServiceConnection mMyServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "ServiceConnection onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "ServiceConnection onServiceDisconnected");
        }
    };

    private boolean isServiceRunning(String className) {
        ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (className.equals(serviceInfo.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    private void bindMyService() {
        Intent intent = new Intent("a.b.c.MY_INTENT");
        intent.setPackage("a.b.c");
        bindService(intent, mMyServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);
        if (isServiceRunning("a.b.c.MyService")) {
            bindMyService();
        }
        else {
            Log.e(TAG, "Service is not running");
        }
    }
}

"isServiceRunning" 函数 returns 为真所以我知道 a.b.c.MyService 是 运行。 bindService 函数似乎成功了(Logcat 中没有错误)但是永远不会执行 onServiceConnected 回调。

如何在 Android 目标 SDK 24 中从 x.y.z.ClientActivity 绑定到 a.b.c.Myservice?

谢谢!

我在这里看到至少 2 个问题:

  1. 您在调用 bindService() 时使用了“隐式”Intent(即:您没有明确指定组件)。如果您的目标是 API 21 或更高,这应该会引发异常。您应该使用“显式”Intent(即:指定包名称和 Service 的 class 名称)。

  2. 你的 Service returns null 来自 onBind()。这意味着客户端将无法绑定到 Service,因为您没有返回客户端随后可以用来调用 Service 上的方法的 IBinder。尝试查找有关如何使用绑定 Services.

    的操作方法或一些示例代码