EventBus - 订阅者类及其父类中没有使用@subscribe注解的公共方法

47

我正在使用EventBus创建一个Android应用程序,用于向其他类发布异步广播,但在执行过程中遇到了错误。

MainActivity.java

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.gms.maps.model.LatLng;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;


public class MainActivity extends AppCompatActivity {

    //Globals
    public String uname = null;
    public double lat = 0;
    public double lng = 0;

    //Get GUI handles
    public Button sendButton; //
    public EditText username;
    public Button MapButton; //
    public EditText LatBox;
    public EditText LngBox;


    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        //register EventBus
        EventBus.getDefault().register(this);

        super.onCreate(savedInstanceState);
        //set GUI for MainActivity
        setContentView(R.layout.activity_main);

        //get handlers
        LatBox = (EditText)findViewById(R.id.LatBox);
        LngBox = (EditText)findViewById(R.id.LngBox);

        MapButton = (Button)findViewById(R.id.locationButton);
        //Call the class which will handle finding coordinates
        MapButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent MapIntent = new Intent(getApplicationContext(), MapClass.class);
                startActivityForResult(MapIntent, 0);
            }
        });

        sendButton = (Button)findViewById(R.id.Submit);
        //Set action for Button
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //Get username from user
                username  = (EditText)findViewById(R.id.UsernameText);
                uname = username.getText().toString();

                //Generate intent to start IntentService
                Intent i = new Intent(getApplicationContext(), Register.class);

                //Put the extra field of username
                i.putExtra("username", uname);
                i.putExtra("latitude", lat);
                i.putExtra("longitude", lng);
                i.putExtra("type", "meetup.be2015.gcm_meetup.MAIN_ACTIVITY");

                //Start the IntentService on a different thread
                startService(i);
            }
        });

    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(LatLng currentPos){

        LatBox.setText(String.valueOf(currentPos.latitude));
        LngBox.setText(String.valueOf(currentPos.longitude));

        lat = currentPos.latitude;
        lng = currentPos.longitude;
    }
}

MapClass.java

import android.app.IntentService;
import android.content.Intent;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.LatLng;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

public class MapClass extends AppCompatActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    private GoogleApiClient mGoogleApiClient;
    private GoogleMap mgoogleMap;
    private LatLng latLng;
    private GoogleApiClient client;

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mgoogleMap = googleMap;
        mgoogleMap.setMyLocationEnabled(true);      //Sets location to current position
        buildGoogleApiClient();
        mGoogleApiClient.connect();
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().unregister(this);
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        Location MLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (MLastLocation != null) {
            latLng = new LatLng(MLastLocation.getLatitude(), MLastLocation.getLongitude());

            //Post the LatLng to MainActivity
            EventBus.getDefault().post(latLng);

            //Send sticky event to Register and MyGcmListenerService
            EventBus.getDefault().postSticky(latLng);

        } else {
            Log.d("onConnected", "Value of LatLng is NULL");
            latLng = new LatLng(0, 0);   //equator
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        //Notify
        Log.d("ConnectionSuspended", "Connection Suspended. Status: " +   i);
        mgoogleMap.clear();
        mGoogleApiClient.disconnect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        //Notify
        Log.d("ConnectionFailed", "Connection Failed. Status: " + connectionResult.toString());
        mgoogleMap.clear();
        mGoogleApiClient.disconnect();
    }

    @Subscribe
    public void onEvent() {
        Log.d("EVENT", "EVENT");
    }

    @Override
    public void onStart() {
        super.onStart();
        if (!EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().register(this);
        }


    @Override
    public void onStop() {
        super.onStop();
        if (EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().unregister(this);
        }

    }
}

LogCat 显示如下内容:

03-08 22:54:56.970 8570-8570/meetup.be2015.gcm_meetup E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{meetup.be2015.gcm_meetup/meetup.be2015.gcm_meetup.MapClass}:
org.greenrobot.eventbus.EventBusException: Subscriber class meetup.be2015.gcm_meetup.MapClass 
and its super classes have no public methods with the @Subscribe annotation
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2118)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2143)
at android.app.ActivityThread.access$700(ActivityThread.java:140)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:174)
at android.app.ActivityThread.main(ActivityThread.java:4952)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)
Caused by: org.greenrobot.eventbus.EventBusException: Subscriber class meetup.be2015.gcm_meetup.MapClass 
and its super classes have no public methods with the @Subscribe annotation
at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:67)
at org.greenrobot.eventbus.EventBus.register(EventBus.java:136)
at meetup.be2015.gcm_meetup.MapClass.onStart(MapClass.java:91)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1178)
at android.app.Activity.performStart(Activity.java:5198)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2091)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2143) 
at android.app.ActivityThread.access$700(ActivityThread.java:140) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:174) 
at android.app.ActivityThread.main(ActivityThread.java:4952) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:511) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794) 
at dalvik.system.NativeStart.main(Native Method) 

为什么会出现这种情况?是我做错了什么吗?


使用 R8 我们有相同的问题。 - Duna
16个回答

156
请确保如果您正在使用Proguard进行构建,则将这些行添加到您的proguard配置文件中。
-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

1
第二行类名后面的双星号在最近版本的工具中显示语法错误。语法有变化吗?双星号到底是什么意思? - Abhijit

21

我认为这是因为MapClass.java中的onEvent没有参数。你可以尝试使用预期的参数吗?


期望的参数是什么?实际上,在 MapClass.java 中我不想接收任何东西。 - Rahul Kulhalli
5
如果您不想在MapClass.java中接收任何内容,那么为什么要订阅(注册)?只需删除注册和注销,还有onEvent即可。 - elsennov
3
我只想将LatLng发送给MainActivity。我认为,即使要使用EventBus发送事件,你也必须注册和注销。 所以,你的意思是如果我不打算接收任何东西,那么我就不需要向EventBus注册和注销了? 反过来说,只有当我需要接收事件时,才需要向EventBus注册和注销,是吗?如果我的类既想发布又想订阅事件,该怎么办? 如果我的理解有问题,请见谅。我无法理解EventBus的概念。谢谢,@elsennov! :) - Rahul Kulhalli
7
没错,关键在于:“作为订阅者,您必须注册事件总线以接收事件,并在完成后注销。但是,如果您是发布者,则无需注册。” - elsennov

17

我曾经遇到过同样的问题,在长时间的研究之后,找到了每个案例的解决方案。这个问题是由于您正在尝试注册事件总线的类中缺少@Subscribe公共方法onEvent()造成的。如果您使用Event bus注册类,则必须存在此函数。

情况有两种:

  1. 使用progruad:progruad可能会修改onEvent()方法的名称,导致事件总线无法找到它。请将以下行放入progruad规则中:

    -keepattributes Annotation

    -keepclassmembers class ** {

    @org.greenrobot.eventbus.Subscribe ;

    }

    -keep enum org.greenrobot.eventbus.ThreadMode { *;

  2. 如果您没有使用progruard,则肯定缺少带有@Subscribe注释的onEvent()方法。 EventBus 3.0.0版本中此注释与方法是强制性的,因此请仔细检查您的类中是否存在此方法。


12

万一你的代码像我的一样 :p

我必须将方法设置为public,因为它当前是private


11

ProGuard

ProGuard混淆了方法名,并且可能会删除没有被调用的方法(死代码删除)。由于订阅器方法不是直接调用的,ProGuard认为它们未被使用。因此,如果启用ProGuard缩小,必须告诉ProGuard保留这些订阅器方法。

在您的ProGuard配置文件(proguard.cfg)中使用以下规则以防止订阅器被删除:

-keepattributes *Annotation*
-keepclassmembers class * {
   @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
 
# Only required if you usenter code heree AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

5
在我的情况中,onEvent()私有的,并且放置在子类中。但是register()unregister()父类中被调用。解决方案是将onEvent()改为公有的

一样的问题。我把函数设为 private,然后改为 public,这样就解决了。 - sud007

5

在我的情况下,我因为没有在注册EventBus的类上写@Subscribe而出现了这个错误。


4

如果您正在使用proguard,那么在调试模式下不会遇到此问题。我在发布版本中遇到了这个问题,但在proguard-rules.pro文件中添加以下代码后,问题得以解决。

-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
 }
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

3

以下内容可能对某些人有所帮助! 我的情况是,我忘记在buildTypes下添加以下代码行:

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'


3

顺便提一下,我在从Google的实现的EventBus切换到这个之后也遇到了同样的错误。这个错误让我很疯狂,因为Google的EventBus也有@Subscribe注释,我使用的是那个而不是由greenrobot提供的。

enter image description here

好的,这是我的一个非常愚蠢的错误,但如果我能帮助像我一样的人,我就很开心。


这是我的案例。谢谢。 - shaby

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