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.