Почему мы не можем напрямую вызвать API удаленного сервиса Android (на основе AIDL)? - PullRequest
2 голосов
/ 15 апреля 2011


Я писал простую удаленную службу Android на основе AIDL и клиент для доступа к API, предоставляемому удаленной службой.Я проверял в Интернете, что в каждом сообщении люди вызывали API удаленного сервиса внутри метода кнопки onClickListener () клиентского кода.Однако, когда я попытался вызвать API, предоставляемый удаленным сервисом вне метода onClickListener (), он выдает мне 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!";
    }
};
}

Вот мой файл помощи:

package com.myapp.myservice; 

interface RemoteServiceInterface {
String getMessage();
}

Заранее спасибо,
Рупеш

Ответы [ 2 ]

4 голосов
/ 15 апреля 2011
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 для получения реального интерфейса для вашего соединения, это демонстрируется на примере google aidl

    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, либо уведомив службу.

1 голос
/ 15 апреля 2011

remoteInterface равно NULL до подключения службы (onServiceConnected вызывается).

startService - это звонок async, вы звоните startService не означает, что услуга запущена и подключена.когда служба подключена, вызывается onServiceConnected, тогда вы можете использовать соединение для вызова удаленной службы.

На самом деле, вы всегда должны проверять погоду remoteInterface является нулевым или нет.

...