2

I'm developing an android application that have to receive some notification. I receive this notification between firebase and I had create a dispatcher object for update some information in some Fragment or Activity.

I'm trying to use Observer pattern for this pourpose.

I had create an interface called Update.

public interface Updateable {

    void update();

    void update(int caller);

    void update(int caller, Object obj);
}

The Fragment or Activity are the Observer, and implement Update.

The notification dispatcher is the Subject, that store Updateable Object and notify that when the FirebaseMessagingService pass to it a notification.

My problem is, when I visualize an Updateable Activity Chat and I receive a notification that have to update Chat I have a thread conflicts and the crash.

This is the log about Chat Activity.

 2019-08-26 17:38:41.668 1193-1440/? W/InputDispatcher: channel 'd733762 it.myapp/it.myapp.Chat (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
 2019-08-26 17:38:41.668 1193-1440/? E/InputDispatcher: channel 'd733762 it.myapp/it.myapp.Chat (server)' ~ Channel is unrecoverably broken and will be disposed!
 2019-08-26 17:38:41.750 1193-2760/? I/WindowManager: WIN DEATH: Window{d733762 u0 it.myapp/it.myapp.Chat}
 2019-08-26 17:38:41.750 1193-2760/? V/WindowManager: removeIfPossible: Window{d733762 u0 it.myapp/it.myapp.Chat}
 2019-08-26 17:38:41.750 1193-2760/? W/InputDispatcher: Attempted to unregister already unregistered input channel 'd733762 it.myapp/it.myapp.Chat (server)'
 2019-08-26 17:38:41.750 1193-2760/? V/WindowManager: Not removing Window{d733762 u0 it.myapp/it.myapp.Chat EXITING} due to exit animation 
 2019-08-26 17:38:41.772 1193-11657/? V/WindowManager: Exit animation finished in Window{d733762 u0 it.myapp/it.myapp.Chat EXITING}: remove=true
 2019-08-26 17:38:41.773 1193-11657/? E/WindowManager: win=Window{d733762 u0 it.myapp/it.myapp.Chat EXITING} destroySurfaces: appStopped=false win.mWindowRemovalAllowed=true win.mRemoveOnExit=true
 2019-08-26 17:38:41.773 1193-11657/? W/WindowManager: Exception thrown when updateSurfaceStatusSurface(name=it.myapp/it.myapp.Chat)/@0xafbd060: android.os.DeadObjectException
 2019-08-26 17:38:41.774 1193-11657/? V/WindowManager: postWindowRemoveCleanupLocked: Window{d733762 u0 it.myapp/it.myapp.Chat}
 2019-08-26 17:38:41.774 1193-11657/? V/WindowManager: Removing Window{d733762 u0 it.myapp/it.myapp.Chat} from AppWindowToken{74a1a5f token=Token{39042fe ActivityRecord{f6f3eb9 u0 it.myapp/.Chat t1826}}}
 2019-08-26 17:38:41.782 1193-11657/? V/WindowManager: removeAppToken: AppWindowToken{74a1a5f token=Token{39042fe ActivityRecord{f6f3eb9 u0 it.myapp/.Chat t1826}}} delayed=true Callers=com.android.server.wm.DisplayContent.removeAppToken:1086 com.android.server.wm.AppWindowContainerController.removeContainer:315 com.android.server.am.ActivityRecord.removeWindowContainer:1201 com.android.server.am.ActivityStack.removeActivityFromHistoryLocked:4568 
 2019-08-26 17:38:41.817 2085-4155/? E/DollieAdapterService: notifyActivityState pkg:it.myapp/it.myapp.Chat state:18 fg:false mUid:10162
 2019-08-26 17:38:41.817 1193-1299/? I/StatusBarDisable: setFlags what=0 which=1 pkg=Window{d733762 u0 it.myapp/it.myapp.Chat}
 2019-08-26 17:38:41.817 1193-1299/? I/StatusBarDisable: setFlags what=0 which=1 pkg=Window{d733762 u0 it.myapp/it.myapp.Chat}
 2019-08-26 17:38:41.818 1193-1299/? I/StatusBarDisable: setFlags what=0 which=1 pkg=Window{d733762 u0 it.myapp/it.myapp.Chat}

this is the Chat Activity

public class Chat extends AppCompatActivity implements Loader, Updateable  {

    private ArrayList<Message> mess;

    private RecyclerView mex_list;
    private RecyclerView.Adapter mAdapter;
    private final LinearLayoutManager llm = new LinearLayoutManager(this);;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.chat);
    initGui();
    NotificationEventDispatcher.registry(this, NotificationEventDispatcher.CH_4_0);

    mAdapter = new ChatAdapter(this,mess);
    llm.setStackFromEnd(true);
    mAdapter.setHasStableIds(false);
    mex_list.setLayoutManager(llm);
    mex_list.setAdapter(mAdapter);
    /*
    *
    */

}


/*
*
*/


@Override
public void update() {

}

@Override
public void update(int caller) {

}

@Override
public void update(final int caller, final Object obj) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            RemoteMessage rm = (RemoteMessage) obj;
            Message m = createMessageWithRemoteMessageInfo(rm):

            mess.add(m);

            mAdapter.notifyItemInserted(mess.size() - 1);
            if (llm.findLastCompletelyVisibleItemPosition() == mess.size() - 2)
                   llm.scrollToPosition(mess.size() - 1);

            }
    });

}
}

This is my FirebaseNotificationServices

public class MyNotificationServices extends FirebaseMessagingService {

    private static final int NOTIFICATION_ID = 9999;
    private static final String NOT_LOG = "Notify Log";

    private static String title;
    private static String message;

    public MyNotificationServices() {
        super();
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);


        title = remoteMessage.getNotification().getTitle();
        message = remoteMessage.getNotification().getBody();

        Notification toReturn = defaultSetting().build();

        NotificationEventDispatcher.dispatch(remoteMessage);

        Log.d(NOT_LOG, remoteMessage.getData().toString());

        showNotifications(toReturn);
    }

    private NotificationCompat.Builder defaultSetting(){

        NotificationCompat.Builder notification = new NotificationCompat.Builder(this)
                .setContentText(message)
                .setContentTitle(title)
                .setAutoCancel(true)
                .setLights(Color.BLUE, 500, 500)
                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                .setVibrate(new long[0])
                .setSmallIcon(R.mipmap.ic_launcher);

        return notification;
    }

    private void showNotifications(Notification notification) {
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.notify(NOTIFICATION_ID, notification);
    }
}

This class usa another static class created for dispatch the notification

public class NotificationEventDispatcher {

        private static HashMap<Integer, ArrayList<RegisteredUpdt>> listOfUpdateable = new HashMap<>();

        private static final Integer NUM_CHANNEL = 3;
        private static final Integer MIN_CH = 0;
        private static final Integer MAX_CH = 2;


        public static final Integer CH_0 = 0;
        public static final Integer CH_1 = 1;
        public static final Integer CH_2 = 2;


        private NotificationEventDispatcher(){}

        public static boolean registry(Integer key, Updateable obj, Integer channel){
            if(channel < MIN_CH || channel > MAX_CH){
                Log.d(TAG, "channel out of range");
                return false;
            }
            else{
                if(!listOfUpdateable.containsKey(channel))
                    listOfUpdateable.put(channel, new ArrayList<RegisteredUpdt>());
                ArrayList<RegisteredUpdt> regUpArray = listOfUpdateable.get(channel);
                RegisteredUpdt reg = new RegisteredUpdt(obj, key);
                if(regUpArray.contains(reg)){
                    regUpArray.set(regUpArray.indexOf(reg),reg);
                    return false;
                }
                else{
                    regUpArray = listOfUpdateable.get(channel);
                    regUpArray.add(reg);
                    return true;
                }
            }
        }


        public static boolean unregister(Integer key, Updateable obj, Integer channel){
            RegisteredUpdt reg = new RegisteredUpdt(obj, channel);
            ArrayList<RegisteredUpdt> list = listOfUpdateable.get(channel);
            if(!list.contains(reg))
                return false;
            else{
                list.remove(list.indexOf(reg));
                return true;
            }
        }

        public static void dispatch(RemoteMessage rm){
            if(rm.getData().containsKey("type")){
                int type = Integer.parseInt(rm.getData().get("type"));
                switch (type){
                    case 0:{
                        performCall(listOfUpdateable.get(NotificationEventDispatcher.CH_0), rm);
                        break;
                    }
                    case 1:{
                        performCall(listOfUpdateable.get(NotificationEventDispatcher.CH_1), rm);
                        break;
                    }
                    case 2:{
                        performCall(listOfUpdateable.get(NotificationEventDispatcher.CH_2), rm);
                        break;
                    }
                }
            }
        }

        private static void performCall(ArrayList<RegisteredUpdt> upt, RemoteMessage rm){
            for(RegisteredUpdt reg:upt){
                reg.getUp().update(reg.getUpdatekey(), rm);
            }
        }

        private static class RegisteredUpdt{

            private Updateable up;
            private int updatekey;

            public RegisteredUpdt(Updateable up, int updatekey) {
                this.up = up;
                this.updatekey = updatekey;
            }

            public Updateable getUp() {
                return up;
            }

            public void setUp(Updateable up) {
                this.up = up;
            }

            public int getUpdatekey() {
                return updatekey;
            }

            public void setUpdatekey(int updatekey) {
                this.updatekey = updatekey;
            }

            @Override
            public boolean equals(Object obj) {
                return (((RegisteredUpdt)obj).getUpdatekey() == this.updatekey);
            }
        }


    }

It's really unusual problem, I really don't know how to fix without destroy the Observer architecture.

Every advice is valued. Thanks.

  • 1
    Post the code for your FirebaseMessagingService... but anyway, I don't think that using an adhoc Observable implementation to communicate between a Service and an Activity is the way to go. Instead, you should take a look to LocalBroadcastManager – dglozano Sep 01 '19 at 14:35
  • I add the firebase notification services and the notification dispathcer. I read about LocalBroadcasatManager, it's the better way to manage this kind of update. I'd appreciate it if you could still help me with this problem. The app is for school purposes, I don't want to start over at this point. Thank you very much – Ferdinando D'avino Sep 02 '19 at 23:00
  • I'm putting a few steps to follow to get what causes the problem. 1) Comment out everything inside `public void run(){}` and check if it crashes or not. 2) If it doesn't crash then edit your answer and add following files here: `RemoteMessage`, `Message` and `ChatAdapter`. I'll check if they're causing the problem. – Farid Sep 08 '19 at 05:10
  • Also tell me about your device and Android version. – Farid Sep 08 '19 at 05:11

0 Answers0