I'm working on a project where I need to connect to a server to get data and draw markers out of that data on a map, whenever the data changes, I need to update the map with the new positions.
I'm using a Roomdatabase, Livedata to observe the changes and a service that connects to the server and sends any update to the repository. But what would be the best way to go about this?
Thanks in advance
EDIT:
here's my code so far, I'll try to explain the way I envision how it should work as I'm going
first I have a gateway Entity
@Entity(tableName = "gateway_table")
public class Gateway {
private float temp;
private float wertX;
private float wertY;
private String title;
private boolean drawn;
private ArrayList<String> ObjectANames = new ArrayList<>();
@PrimaryKey(autoGenerate = true)
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public float getWertX() {
return wertX;
}
public float getWertY() {
return wertY;
}
public void setWertX(float wertX) {
this.wertX = wertX;
}
public void setWertY(float wertY) {
this.wertY = wertY;
}
public boolean isDrawn() {
return drawn;
}
public void setDrawn(boolean drawn) {
this.drawn = drawn;
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public ArrayList<String> getObjectANames() {
return ObjectANames;
}
public Gateway (float wertX, float wertY, String title, boolean drawn, ArrayList<String> ObjectANames){
this.wertX = wertX;
this.wertY = wertY;
this.title = title;
this.drawn = drawn;
this.ObjectANames.addAll(ObjectANames);
}
}
and an Entity ObjectA
@Entity(tableName = "objectA_table")
public class ObjectA {
private String gateway;
private String name;
private boolean drawn;
@PrimaryKey(autoGenerate = true)
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public String getGateway() {
return gateway;
}
public void setGateway(String gateway) {
this.gateway = gateway;
}
public boolean isDrawn() {
return drawn;
}
public void setDrawn(boolean drawn) {
this.drawn = drawn;
}
public ObjectA(String gateway, String name, boolean drawn) {
this.gateway = gateway;
this.name = name;
this.drawn = drawn;
}
}
Basically each gateway has a position on the map (from the two floats ), a title, a boolean and An arraylist that contains names from the ObjectA. An ObjectA has a name, a String containing the gateway it's assigned to and a boolean. So a Gateway can have more than one ObjectA but each ObjectA has only one gateway.
in both of the DAOS I have a method findGateway/ObjectA in which I give the title or the name and I get the gateway or the objectA that has that name back.
this is the Viewmodel :
public class ViewModel extends AndroidViewModel {
private Repository repository;
private LiveData<List<ObjectA>> allObjectA;
private LiveData<List<Gateway>> allGateways;
private MutableLiveData<SocketService.MyBinder> binderViewModel = new MutableLiveData<>();
public ViewModel(@NonNull Application application) {
super(application);
repository = new Repository(application);
allObjectA = repository.getAllObjectA();
allGateways = repository.getAllGateways();
}
public void insert(ObjectA objectA) throws InterruptedException {
repository.insert(objectA);
}
public void delete(ObjectA objectA) throws InterruptedException {
repository.delete(objectA);
}
public void deleteAllObjectA() throws InterruptedException {
repository.deleteAllObjectA();
}
public void update(ObjectA objectA) throws InterruptedException {
repository.update(objectA);
}
public ObjectA findObjectA(String ObjectAName) throws InterruptedException { return repository.findObjectA(ObjectAName);}
public LiveData<List<ObjectA>> getAllObjectA(){
return allObjectA;
}
public void insert(Gateway gateway) throws InterruptedException {
repository.insert(gateway);
}
public void delete(Gateway gateway) throws InterruptedException {
repository.delete(gateway);
}
public void deleteAllGateways() throws InterruptedException {
repository.deleteAllGateways();
}
public void update(Gateway gateway) throws InterruptedException {
repository.update(gateway);
}
public Gateway findGateway(String gatewayDesignation) throws InterruptedException { return repository.findGateway(gatewayDesignation);}
public LiveData<List<Gateway>> getAllGateways(){
return allGateways;
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
SocketService.MyBinder binder = (SocketService.MyBinder) iBinder;
binderViewModel.postValue(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
binderViewModel.postValue(null);
}
};
public ServiceConnection getServiceConnection(){
return serviceConnection;
}
public LiveData<SocketService.MyBinder> getMyBinder(){
return binderViewModel;
}
}
This is my service:
public class SocketService extends Service {
//public static final String IP_ADDRESS = "172.17.100.77";
// public static final int SERVER_PORT = 5566;
public static final int CONNECTION_STATUS=0;
private Repository repository;
private final IBinder myBinder = new MyBinder();
Thread thread;
PrintWriter output;
BufferedReader input;
Socket socket;
MediaPlayer player;
ArrayList<ObjectA> objectAArrayList;
@Override
public IBinder onBind(Intent intent) {
System.out.println("I am in Ibinder onBind method");
return myBinder;
}
public class MyBinder extends Binder {
public SocketService getService() {
System.out.println("I am in Localbinder ");
return SocketService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
repository = new Repository(getApplication());
player = MediaPlayer.create(getApplicationContext(), Settings.System.DEFAULT_RINGTONE_URI);
player.setLooping(true);
player.start();
}
private void sendMessageConnected() {
EventBus.getDefault().post(new IntentServiceResult(CONNECTION_STATUS, "Song is playing"));
}
private void sendMessageObjectGone(ObjectA objectA, ObjectA objectAOld) {
EventBus.getDefault().post(new IntentServiceResult(1, objectA, objectAOld));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
objectAArrayList = new ArrayList<>();
objectAArrayList.add(new ObjectA("gate3","ObjectA1",false));
objectAArrayList.add(new ObjectA("gate2","ObjectA2",false));
objectAArrayList.add(new ObjectA("gate2","ObjectA3",false));
objectAArrayList.add(new ObjectA("gate4","ObjectA1",false));
objectAArrayList.add(new ObjectA("gate2","ObjectA1",false));
objectAArrayList.add(new ObjectA("gate3","ObjectA3",false));
objectAArrayList.add(new ObjectA("gate3","ObjectA2",false));
objectAArrayList.add(new ObjectA("gate4","ObjectA3",false));
thread = new Thread(new connectSocket());
thread.start();
return START_STICKY;
}
class connectSocket implements Runnable {
@Override
public void run() {
while (player.isPlaying()) {
sendMessageConnected();
for (int i = 0; i< objectAArrayList.size(); i++){
try {
if(repository.findObjectA(objectAArrayList.get(i).getName()) == null){
repository.insert(objectAArrayList.get(i));
repository.update(objectAArrayList.get(i));
Gateway gateway = repository.findGateway(objectAArrayList.get(i).getGateway());
gateway.getObjectANames().add(objectAArrayList.get(i).getName());
if(repository.findGateway(gateway.getTitle()).isDrawn()){
gateway.setDrawn(true);
repository.update(gateway);
}else{
gateway.setDrawn(false);
repository.update(gateway);
}
Thread.sleep(2000);
}else {
ObjectA objectA = repository.findObjectA(objectAArrayList.get(i).getName());
ObjectA ObjectAold = repository.findObjectA(objectAArrayList.get(i).getName());
Gateway oldGateway = repository.findGateway(objectA.getGateway());
objectA.setGateway(objectAArrayList.get(i).getGateway());
repository.update(objectA);
Gateway newGateway = repository.findGateway(objectAArrayList.get(i).getGateway());
if(repository.findGateway(oldGateway.getTitle()).isDrawn() && repository.findGateway(objectAArrayList.get(i).getGateway()).isDrawn())
{
sendMessageObjectGone(objectA, ObjectAold);
}else
{
oldGateway.getObjectANames().remove(ObjectAold.getName());
newGateway.getObjectANames().add(objectA.getName());
repository.update(oldGateway);
repository.update(newGateway);
}
ObjectA objectA1 = repository.findObjectA(objectAArrayList.get(i).getName());
objectA1.setDrawn(false);
repository.update(objectA1);
Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
/*try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}*/
player.stop();
}
}
I do not have access to the server yet so I'm just running a simulation. I'm starting a player and as long as it's on the service should keep on working. I initialize at the start some updates to some ObjectA and I go through them while updating the repository each time.
And last but not least the Map Activity, but first I'd like to explain how it's supposed to work. Each Gateway can have multiple ObjectA but on the map only three of them should be drawn next to each gateway. so before drawing a marker I check always if one of the three positions are free.
this is the GatewayMarker Class that has an array of booleans which is used to see which positions are free
public class GatewayMarker extends MapMarker {
private Gateway gateway;
private boolean[] posTaken;
public GatewayMarker(Context context, Gateway gateway, boolean[] posTaken) {
super(context, gateway.getWertX(), gateway.getWertY(), gateway.getTitle());
this.gateway = gateway;
this.posTaken = posTaken;
setImageResource(R.drawable.gateway_foreground);
}
public float getXfirstPos() {
return gateway.getWertX() + 0.05f;
}
public float getYfirstPos() {
return gateway.getWertY() + 0.05f;
}
public float getXSecondPos() {
return gateway.getWertX() - 0.05f;
}
public float getYSecondPos() {
return gateway.getWertY() + 0.05f;
}
public float getXThirdPos() {
return gateway.getWertX();
}
public float getYThirdPos() {
return gateway.getWertY() - 0.05f;
}
public boolean[] getPosTaken() {
return posTaken;
}
}
this is the ObjectAMarker
public class ObjectAMarker extends MapMarker {
private int pos;
public ObjectAMarker(Context context, float x, float y, String name, int pos) {
super(context, x, y, name);
this.pos = pos;
setImageResource(R.drawable.kuh2_foreground);
}
public int getPos() {
return pos;
}
}
And the Map Activity
public class MapMarkerActivity extends AppCompatActivity {
public static final int REQUEST_CONNECTION_STATUS = 0;
private MapViewExt mapView;
private TextView textView;
private EditText textX;
private EditText textY;
private Button button;
private Button buttonDelete;
private Boolean calledFirstTime = true;
private Boolean gatewayMoved = false;
private FrameLayout frameLayout;
private Observer<List<Gateway>> observerGateways;
private Observer<List<ObjectA>> observerObjectA;
private ArrayList<GatewayMarker> gatewayMarkers;
private ArrayList<ObjectAMarker> ObjectAMarkers;
private ViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_marker);
textX = findViewById(R.id.edit_text_x);
textY = findViewById(R.id.edit_text_y);
button = findViewById(R.id.button);
buttonDelete = findViewById(R.id.button_delete);
frameLayout = findViewById(R.id.frame_layout_show_barn);
textView = findViewById(R.id.text_view_connection_mapmarker);
viewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication())).get(ViewModel.class);
buttonDelete.setEnabled(false);
button.setEnabled(false);
mapView = makeMapView();
if (mapView != null) {
mapView.setId(R.id.mapview_id);
mapView.setSaveEnabled(true);
frameLayout.addView(mapView);
}
}
private MapViewExt makeMapView() {
// hier Code to create the map
}
private GatewayMarker findGatewayMarker(String title) {
GatewayMarker gatewayMarker1 = null;
for (GatewayMarker gatewayMarker : gatewayMarkers) {
if (gatewayMarker.getName().equals(title)) {
gatewayMarker1 = gatewayMarker;
}
}
return gatewayMarker1;
}
private ObjectAMarker findObjectAMarker(String name) {
ObjectAMarker objectAMarker = null;
for (ObjectAMarker objectAMarker1 : ObjectAMarkers) {
if (objectAMarker1.getName().equals(name)) {
objectAMarker = objectAMarker1;
}
}
return objectAMarker;
}
private ObjectAMarker addNewObjectAMarker(MapViewExt mapView, float x, float y, String name, int pos) {
ObjectAMarker objectAMarker = new ObjectAMarker(this, x, y, name, pos);
mapView.addMarker(objectAMarker);
return objectAMarker;
//MarkerApiKt.addMarker(mapView, marker, x, y, -0.5f, -0.5f, 0f, 0f);
}
private GatewayMarker addNewGatewayMarker(MapViewExt mapView, Gateway gateway) {
boolean[] posTaken = new boolean[3];
posTaken[0] = false;
posTaken[1] = false;
posTaken[2] = false;
GatewayMarker gatewayMarker = new GatewayMarker(this, gateway, posTaken);
mapView.addMarker(gatewayMarker);
return gatewayMarker;
}
@Override
protected void onResume() {
super.onResume();
EventBus.getDefault().register(this);
gatewayMarkers = new ArrayList<>();
ObjectAMarkers = new ArrayList<>();
observerGateways = new Observer<List<Gateway>>() {
@Override
public void onChanged(List<Gateway> gateways) {
for (Gateway gateway : gateways) {
if (!gateway.isDrawn()) {
if (calledFirstTime || gatewayMoved) {
gatewayMoved = false;
GatewayMarker gatewayMarker = addNewGatewayMarker(mapView, gateway);
gatewayMarkers.add(gatewayMarker);
gateway.setDrawn(true);
try {
viewModel.update(gateway);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < gateway.getObjectANames().size(); i++) {
ObjectAMarker objectAMarker;
if (!gatewayMarker.getPosTaken()[0]) {
objectAMarker = addNewObjectAMarker(mapView, gatewayMarker.getXfirstPos(), gatewayMarker.getYfirstPos(), gateway.getObjectANames().get(i), 0);
ObjectAMarkers.add(objectAMarker);
try {
ObjectA objectA = viewModel.findObjectA(gateway.getObjectANames().get(i));
objectA.setDrawn(true);
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatewayMarker.getPosTaken()[0] = true;
} else if (!gatewayMarker.getPosTaken()[1]) {
objectAMarker = addNewObjectAMarker(mapView, gatewayMarker.getXSecondPos(), gatewayMarker.getYSecondPos(), gateway.getObjectANames().get(i), 1);
ObjectAMarkers.add(objectAMarker);
try {
ObjectA objectA = viewModel.findObjectA(gateway.getObjectANames().get(i));
objectA.setDrawn(true);
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatewayMarker.getPosTaken()[1] = true;
} else if (!gatewayMarker.getPosTaken()[2]) {
objectAMarker = addNewObjectAMarker(mapView, gatewayMarker.getXThirdPos(), gatewayMarker.getYThirdPos(), gateway.getObjectANames().get(i), 2);
ObjectAMarkers.add(objectAMarker);
try {
ObjectA objectA = viewModel.findObjectA(gateway.getObjectANames().get(i));
objectA.setDrawn(true);
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatewayMarker.getPosTaken()[2] = true;
return;
}
}
}
} else {
GatewayMarker gatewayMarker = findGatewayMarker(gateway.getTitle());
for (int i = 0; i < gateway.getObjectANames().size(); i++) {
try {
if (!viewModel.findObjectA(gateway.getObjectANames().get(i)).isDrawn()) {
if (!gatewayMarker.getPosTaken()[0]) {
ObjectAMarker objectAMarker = findObjectAMarker(gateway.getObjectANames().get(i));
ObjectAMarkers.remove(objectAMarker);
ObjectAMarkers.add(addNewObjectAMarker(mapView, gatewayMarker.getXfirstPos(), gatewayMarker.getYfirstPos(), gateway.getObjectANames().get(i), 0));
try {
ObjectA objectA = viewModel.findObjectA(gateway.getObjectANames().get(i));
objectA.setDrawn(true);
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatewayMarker.getPosTaken()[0] = true;
} else if (!gatewayMarker.getPosTaken()[1]) {
ObjectAMarker objectAMarker = findObjectAMarker(gateway.getObjectANames().get(i));
ObjectAMarkers.remove(objectAMarker);
ObjectAMarkers.add(addNewObjectAMarker(mapView, gatewayMarker.getXSecondPos(), gatewayMarker.getYSecondPos(), gateway.getObjectANames().get(i), 1));
try {
ObjectA objectA = viewModel.findObjectA(gateway.getObjectANames().get(i));
objectA.setDrawn(true);
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatewayMarker.getPosTaken()[1] = true;
} else if (!gatewayMarker.getPosTaken()[2]) {
ObjectAMarker objectAMarker = findObjectAMarker(gateway.getObjectANames().get(i));
ObjectAMarkers.remove(objectAMarker);
ObjectAMarkers.add(addNewObjectAMarker(mapView, gatewayMarker.getXThirdPos(), gatewayMarker.getYThirdPos(), gateway.getObjectANames().get(i), 2));
try {
ObjectA objectA = viewModel.findObjectA(gateway.getObjectANames().get(i));
objectA.setDrawn(true);
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatewayMarker.getPosTaken()[2] = true;
} else {
return;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
calledFirstTime = false;
}
};
observerObjectA = new Observer<List<ObjectA>>() {
@Override
public void onChanged(List<ObjectA> objectAS) {
}
};
viewModel.getAllObjectA().observe(this, observerObjectA);
viewModel.getAllGateways().observe(this, observerGateways);
}
@Override
protected void onPause() {
super.onPause();
List<ObjectA> allKühe = viewModel.getAllObjectA().getValue();
List<Gateway> allGateways = viewModel.getAllGateways().getValue();
for (ObjectA objectA : allKühe) {
objectA.setDrawn(false);
try {
viewModel.update(objectA);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Gateway gateway : allGateways) {
gateway.setDrawn(false);
try {
viewModel.update(gateway);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
viewModel.getAllGateways().removeObserver(observerGateways);
viewModel.getAllObjectA().removeObserver(observerObjectA);
calledFirstTime = true;
for (int i = 0; i < ObjectAMarkers.size(); i++) {
MarkerApiKt.removeMarker(mapView, ObjectAMarkers.get(i));
}
ObjectAMarkers.clear();
for (int i = 0; i < gatewayMarkers.size(); i++) {
MarkerApiKt.removeMarker(mapView, gatewayMarkers.get(i));
}
gatewayMarkers.clear();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void getconnectionstatus(IntentServiceResult intentServiceResult) throws InterruptedException {
if (intentServiceResult.getResult() == REQUEST_CONNECTION_STATUS) {
textView.setText(intentServiceResult.getResultvalue());
}
if (intentServiceResult.getResult() == 1) {
ObjectA objectA = intentServiceResult.getResultObjectA();
ObjectA objectAOld = intentServiceResult.getResultObjectAOld();
Gateway gateway = viewModel.findGateway(objectA.getGateway());
Gateway gatewayOld = viewModel.findGateway(objectAOld.getGateway());
gatewayOld.getObjectANames().remove(objectAOld.getName());
gateway.getObjectANames().add(objectA.getName());
viewModel.update(gatewayOld);
viewModel.update(gateway);
ObjectAMarker objectAMarker = findObjectAMarker(objectAOld.getName());
GatewayMarker gatewayMarker = findGatewayMarker(gatewayOld.getTitle());
gatewayMarker.getPosTaken()[objectAMarker.getPos()] = false;
MarkerApiKt.removeMarker(mapView, objectAMarker);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
Basically what the problem is, when I open the map Activity it works normally, but let's say I open another Activity on top of the mapActivity and a change occurs in the database, when I get back to the mapActivity I find two instances of the same Marker. I noticed through the Database Inspector that I end up with two of the same String in the ArrayList of the Gateaway Object, which shouldn't happen.
The other Problem is that for now the service is set to make a change and sleep for 2 seconds, this is probably not gonna stay that way but when I do not let it sleep, even when I'm in the Map Activity I do get inconsistencies.
EDIT 2: Database:
@androidx.room.Database(entities = {ObjectA.class, Gateway.class}, version = 26)
@TypeConverters({Converters.class})
public abstract class Database extends RoomDatabase {
private static volatile Database instance;
public abstract ObjectADAO cowDAO();
public abstract GatewayDAO gatewayDAO();
public static synchronized Database getInstance(Context context){
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(),
Database.class,"database")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
}