Android:绑定远程服务

8
我正在构建一个远程服务和一个客户端应用程序,目标是在Nexus 6P设备上执行API 24。我有一个在启动时自动启动的远程服务。以下是代码片段: 远程服务清单
<?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 StackOverflow 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);
        }
    }
}

远程活动(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);
    }
}

我随后有一个单独的项目,实现在不同包中的客户端活动,试图绑定到远程服务。以下是代码片段:
客户端清单
<?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>

客户活动
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”函数返回true,因此我知道a.b.c.MyService正在运行。bindService函数似乎成功(Logcat中没有错误),但onServiceConnected回调从未执行。
我该如何在Android目标SDK 24中从x.y.z.ClientActivity绑定到a.b.c.Myservice?
谢谢!
1个回答

8

我认为这里至少有两个问题:

  1. 在调用bindService()时,您正在使用一个“隐式”Intent(即:您没有显式指定组件)。如果您的目标是API 21或更高版本,则应该抛出异常。您应该使用“显式”Intent(即:指定Service的包名和类名)。

  2. 您的ServiceonBind()返回null。这意味着客户端将无法绑定到Service,因为您没有返回一个IBinder,客户端可以使用它来调用Service上的方法。尝试找到一个如何使用绑定的Service的教程或示例代码。


1
  1. 不完全是这样:他调用了 intent.setPackage("a.b.c");,然后该意图变得“明确”。
- pskink
1
解决方案是David Wasser的评论(2)...我需要返回一个IBinder。虽然这个方法可行,但也有关于显式意图的讨论:你有没有发送“更明确的意图”的例子,将a.b.c.MY_INTENT发送到a.b.c.MyService? - pfp
4
一个更加明确的Intentintent.setClassName("a.b.c", "a.b.c.MyService");。在这种情况下,你明确地指定要实例化的组件(类)。如果你这样做了,就不需要在Intent中指定ACTION,并且可以从<service>声明中删除<intent-filter>,因为它不再需要(<intent-filter>仅用于隐式Intent解析)。但是,如果您删除了<intent-filter>,那么您将需要向<service>声明添加 android:exported="true",因为没有<intent-filter>时... - David Wasser
3
Service将是私有的,不会对包名为“a.b.c”以外的应用程序可用。如果您提供一个<intent-filter>,那么android:exported的默认值将自动为true,但是如果您不提供<intent-filter>,则默认值为false。希望这很清楚。 - David Wasser
1
我希望我能为您上面“更明确”的评论加50分。您救了我的一天。请考虑将该评论提升到答案正文中。 - Uri London
显示剩余3条评论

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