2


我正在编写一个简单的基于 AIDL 的 android 远程服务和一个客户端来访问远程服务公开的 API。我在互联网上查了一下,在每个帖子中,人们都在客户端代码的按钮的 onClickListener() 方法中调用了远程服务 API。但是,当我尝试在 onClickListener() 方法之外调用远程服务公开的 API 时,它会抛出 NullPointerException,表明我的服务对象尚未初始化(请检查客户端代码的 onCreate 方法中的注释)。我已将我的代码附在这个问题上。如果有人可以向我解释为什么会出现这种行为,那就太好了。
这是客户端代码:

package com.myapp.myclient;

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.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.myapp.myservice.RemoteServiceInterface;

public class MyClient extends Activity {

RemoteServiceInterface remoteInterface;
ServiceConnection connection;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Intent i = new Intent();
    i.setAction("com.myapp.myservice.RemoteService");

    startRemoteInterface(i);
    bindRemoteInterface(i);

    /* This code doesn't execute. Raises a Null Pointer 
      Exception, indicating that remoteInterface is not 
      initialized. */
    try {
        Toast.makeText(getBaseContext(), remoteInterface.getMessage(), Toast.LENGTH_SHORT).show();
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    /* Whereas this code does work. */      
    Button getMessage = (Button)findViewById(R.id.getMessage);
    getMessage.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            String msg = null;
            try {
                msg = remoteInterface.getMessage();
            } catch (RemoteException e) {
                e.printStackTrace();
            }

            Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
        }
    });
}

class RemoteServiceConnection implements ServiceConnection{

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        remoteInterface = RemoteServiceInterface.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
}

private void startRemoteInterface(Intent i) {
    startService(i);
}

private void bindRemoteInterface(Intent i) {
    if(connection == null){
        connection = new RemoteServiceConnection();
        bindService(i, connection, Context.BIND_AUTO_CREATE);
    } else {
        Toast.makeText(getBaseContext(), "Service cannot bind - already bound.", Toast.LENGTH_SHORT).show();
    }
}
}

这是我的远程服务代码:

package com.myapp.myservice;

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

public class RemoteService extends Service {

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

@Override
public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
}

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

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, flags, startId);
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

private final RemoteServiceInterface.Stub mBinder = new RemoteServiceInterface.Stub() {

    @Override
    public String getMessage() throws RemoteException {
        return "Hello World!";
    }
};
}

这是我的aidl文件:

package com.myapp.myservice; 

interface RemoteServiceInterface {
String getMessage();
}

提前致谢,
Rupesh

4

2 回答 2

4
bindRemoteInterface(i);

    /* This code doesn't execute. Raises a Null Pointer 
      Exception, indicating that remoteInterface is not 
      initialized. */
    try {
        Toast.makeText(getBaseContext(), remoteInterface.getMessage(), Toast.LENGTH_SHORT).show();
    } catch (RemoteException e) {
        e.printStackTrace();
    }

请记住,绑定是一个异步调用,您必须等待 ServiceConnection 中的回调以获取onServiceConnected并在此之后执行操作。

您还必须使用 asInterface 方法来获取连接的真实接口,这由googleaidl 示例演示

    RemoteServiceInterface mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Following the example above for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service
        mIRemoteService = RemoteServiceInterface.Stub.asInterface(service);
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "Service has unexpectedly disconnected");
        mIRemoteService = null;
    }
};

然后,您可以调用 mIRemoteService 对象。直接在 onServiceConnected 回调中或通过通知服务。

于 2011-04-15T10:50:43.663 回答
1

remoteInterface 在服务连接(onServiceConnected调用)之前为 NULL 。

startService是一个async电话,你打电话startService并不意味着服务已启动并已连接。当服务被连接时,onServiceConnected被调用,那么你可以使用连接来调用远程服务。

实际上,您应该经常检查天气remoteInterface是否为空。

于 2011-04-15T10:56:51.293 回答