accessing data from concurrentHashMap while it gets updated in background android - android

I have a static concurrentHashMap object which is been updated in background. While it is getting updated, I want to access the values from it in another thread. I am using concurrentHashMap which I understand from the documentation and thinks that it would suit this scenrio
Here is what I am doing
for (Map.Entry<String, ArrayList<property>> entry : alldata.entrySet())
{
udata.put(entry.getKey(), new ArrayList<property>(entry.getValue()));
}
in above code I am updating udata on getting data from server but in background.
While in another thread I am accessing some info out of it ..
for (String s: sTypes)
{
if(jprocess.udata != null)
{
if (jprocess.udata.get(s) != null)
{
if (jprocess.udata.get(s).size() > 0) {
if (xcor < jprocess.udata.get(s).size())
if (xcor != -1) {
allData.add(jprocess.udata.get(s).get(xcor));
}
}
}
}
}
but when I try to access any indexfrom it I can not access anything I hope it is clear what I want..
I already tried ConcurrentHashMap which should work for this situation but may be I did not understand it well..

If you are using ConcurrentHashMap, then there is not point it will not work. I strongly doubt that you are trying to access wrong index or you do not have synchronization between inserting and accessing the particular index.
For that just check the size of udata and value of xcor in your code and in each if condition.
In addition, read this carefully and for sure you will be convinced that ConcurrentHashMap is right solution for your issue.

Related

Android AccessibilityService performAction() method not working

I am developing an accessibility service for Android. The service calls an app, and that app has a RecyclerView. Then I want to click on an element of the RecyclerView with performAction(AccessibilityNodeInfo.ACTION_CLICK) but it is not working. I know there are a few similar questions but none of them works for me. Also I checked the official documentation for the class of the performAction method https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo
This is my code:
#Override
public void onAccessibilityEvent(Accessibility event){
AccessibilityNodeInfo source = event.getSource();
if(source != null){
List<AccessibilityNodeInfo> list = source.findAccessibilityNOdeInfosByText("mystring");
list.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
This is my configuration xml file:
<accessibility-srvice xmlns...
android:accessibilityFeedbackType = "feedbackGeneric"
android:AccessibilityFlags = "flagDefault"
android:canPerformGestures = "true"
android:canRetrieveWIndowCOntent = "true"
I think I misunderstood something, but i don't know what can be. Any help is appreciated.
The simple answer is that while finding the node by text is fine, that particular node was not the node with the desired onClick event. The solution is to call
list.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK)
The clarifying discussion is below
I think .performAction(AccessibilityNodeInfo.ACTION_CLICK) is right, but there might be some other concerns. Sorry for posting as an answer but a comment is too small.
Are you sure the onAccessibilityEvent is being called? I don't think that is the right event, but I can't be sure. Maybe put a log in there to ensure it's calling the event when you expect it to be called.
Also, looking at the source might restrict your search, maybe instead of event.getSource() try using rootInActiveWindow (I use Kotlin so it might have a method, see https://developer.android.com/reference/android/accessibilityservice/AccessibilityService#getRootInActiveWindow(int))
EDIT: 28 March 2022
I have run this code on my own accessibility service and it does click the button. But it's very prone to overflow.
var ranOnce = false // prevent overflow
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
if (event == null) return
if (event.eventType == TYPE_WINDOW_STATE_CHANGED) return
if (event.source != null && !ranOnce) {
val nodeList = rootInActiveWindow.findAccessibilityNodeInfosByText("Menu")
//event.source.findAccessibilityNodeInfosByText("Menu") // <-- always nothing in list
Log.d("onAccessibilityEvent", "List of nodes: $nodeList")
if (nodeList.size > 0) {
android.util.Log.d("onAccessibilityEvent", "Node info: ${nodeList[0]}")
ranOnce = true
nodeList[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) //<-- caused an infinite loop!
} else {
Log.d("onAccessibilityEvent", "No nodes found")
}
} else {
Log.d("onAccessibilityEvent", "event.source is null!")
}
}

LiveMutableData and copies update to same value?

I have a MutableLiveData variable in my AppRepository which is updated and contains my data. This I have no issues with. I also have the following observable to trigger a UI update with the data it holds in my onCreateView function:
viewModel.projectWithContent.observe(viewLifecycleOwner, {
pwc = it
counterList = it.counterList
})
When I tap either to increase or decrease the counter count and then try to push the update to my Room database, it skips it. I have the following check currently:
if(counterList != null) {
try {
for(counter: Counter in counterList!!) {
if(counter.counter_count != pwc?.counterList!![
pwc?.counterList!!.indexOf(counter)
].counter_count) {
Log.i(LOG_TAG, "Hello")
} else {
Log.i(LOG_TAG, "Goodbye")
}
}
} catch(e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
It'll always go to Goodbye.
Now. If I put the following just below try
Log.i(LOG_TAG, "PWC: ${pwc?.counterList!![0].counter_count}, " +
"CPWC: ${counterList!![0].counter_count}," +
"VMPWC: ${viewModel.projectWithContent.value?.counterList!![0].counter_count}")
It provides the following output:
PWC: 70, CPWC: 70,VMPWC: 70
Is this a side effect of what I'm doing or?
Thanks
Like #Tenfour04 says, your condition is actually checking they don't match, so "Goodbye" is the output when they do match.
If you don't mind (this is a little long), I just want to recommend some stuff because I feel like you're making life hard for yourself with all the null-checking that's going on - the logic of the code was really hard to read, and I'm guessing that's why you didn't notice the flipped logic too!
First: the ? null safety stuff (and !! which is the opposite of safe, never use it unless you know you have good reason) is there because you have nullable variable types. Normally the IDE would smart cast them to non-null once you've done a null check (like on your first line) - but because they're vars, they can be changed at any time.
That means that a variable that wasn't null before could be now, so you're forced to null-check every single time you access it. But even if the types weren't nullable, because they're vars, they can still change, and the thing you were looking at a moment ago is something different now.
The simple solution is to just make a new variable:
val counters = counterList
if (counters != null) {
...
}
// or if you want to use one of kotlin's scope functions
counterList?.let { counters ->
...
}
Because that new one is a val, it's not going to change what it's pointing at! Once it's null-checked, it's always going to be non-null, so you don't need to use ? anymore.
You have a couple of variables to make - you want to make sure pwc isn't null, and also their counterLists. A quick way to do that is with pwc?.counterList - if pwc is null, it will return null. Otherwise it will move to the next step, and return counterList, which may be null. (Using !! is saying that it definitely never will be null, in which case it shouldn't be nullable at all!)
And you don't actually care about pwc anyway - you're just comparing its counterList to the other, so why don't we pare it back to just those?
val counters = counterList
val pwcCounters = pwc?.counterList
if (counters != null && pwcCounters != null) {
try {
for(counter: Counter in counters) {
if(counter.counter_count != pwcCounters[
pwcCounters.indexOf(counter)
].counter_count) {
Log.i(LOG_TAG, "Hello")
} else {
Log.i(LOG_TAG, "Goodbye")
}
}
} catch(e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
There's more we could do here, but just by cleaning up those nulls and using the specific variables we want to work with, does that feel easier to read? And more importantly, easier to understand what's happening and what could happen?
Might be worth throwing it in a function too, stops the call site getting cluttered with these temp variables:
fun doThing(counters: List<Counter>?, pwcCounters: List<Counter>?) {
if (counters == null || pwcCounters == null) return
// do the stuff
}
// when you want to do the thing:
doThing(counterList, pwc?.counterList)
So all your null checking is out of the way, your "temp variables" are the fixed parameters passed to the function, it's all nice and neat.
I know this is a long post for such a short bit of code, but it's a good habit to get into - if you're writing code where you're working with nullable vars and you're wrestling with the null safety system, or you keep repeating yourself to access a particular variable nested inside another object, you can make things a lot easier for yourself! You can imagine how wild this could all get for more complex code.
Also if you care, this is how I'd personally write it, if it helps!
fun doThing(counters: List<Counter>?, pwcCounters: List<Counter>?) {
if (counters == null || pwcCounters == null) return
// for (counter in Counters) is fine too I just like this version
counters.forEach { counter ->
// find returns the first item that matches the condition, or null if nothing matches,
// so no need to handle any exceptions, just handle the potential null!
// (this is a really common Kotlin pattern, lots of functions have a "returns null on failure" version)
val pwcCounter = pwcCounters.find { it == counter }
// remember pwcCounter can be null, so we have to use ? to access its count safely.
// If it evaluates to null, the match just fails
if (counter.count == pwcCounter?.count) Log.i(LOG_TAG, "Hello")
else Log.i(LOG_TAG, "Goodbye")
}
}
I also renamed counter_count to just count since it's a property on a Counter anyway. I feel like counter.count is easier to read than counter.counter_count, y'know? It's the little things

Why ion-infinite-scroll keeps calling on scrolling in Android?

I am using 'ion-infinite-scroll' in html to load more items from server for this i am using below code
<ion-infinite-scroll immediate-check="false" on-infinite="getListOfAreas()" distance="1%">
</ion-infinite-scroll>
Here, getListOfAreas() function is called when I scroll screen to bottom and it fetches data from server.This is getListOfAreas() function defined on controller
$scope.getListOfAreas = function (shoudlShowLoader) {
AreaBusiness.getAreasListing(shoudlShowLoader, function(serviceResponse) {
$scope.$broadcast('scroll.infiniteScrollComplete');
if (serviceResponse != null) {
var isSuccess = serviceResponse.Success;
if (isSuccess) {
}
}
}
}
On browser, i have debugged some how
$scope.$broadcast('scroll.infiniteScrollComplete');
above line of code keeps calling and spinner keep rotating.I don't know the reason. Am I missing anything?
$scope.$broadcast('scroll.infiniteScrollComplete'); only lets ionic know that the current page of data has been fetched and that it's safe to now fetch the next page. This does not indicate that all data has finished loading.
Looks like the official recommendation is to add an ng-if to the scroll delegate and remove it from dom once there is not more data to load.
Example:
<ion-infinite-scroll
ng-if="moreDataCanBeLoaded()"
icon="ion-loading-c"
on-infinite="loadMoreData()">
</ion-infinite-scroll>
Source: https://ionicframework.com/docs/api/directive/ionInfiniteScroll/
I have resolved the issue, after spending sometime. The issue which I found, I was not adding those elements into the list which were to be shown on UI. So, list was not being updated on controller that is why it kept calling.So I updated my code and added those fetched items from service to list associated to ui as mentioned in my below code
$scope.$broadcast('scroll.infiniteScrollComplete');
if (serviceResponse != null) {
var isSuccess = serviceResponse.Success;
if (isSuccess) {
if ($scope.areas != undefined && $scope.areas.length > 0) {
pushDataToPOIList(serviceResponse.PointOfInterestData);
} else {
$scope.areas = serviceResponse.PointOfInterestData;
}
pushDataToVehiclesList() method I added that is pushing new data into list to update list and on UI as well. This helped me to sort out the problem.

Android Parse SDK issue

I'm using parse to store my data and do a lot of queries while using my program.
The issue is that after about +/-20 similar queries, parse findInBackground() or getFirstInBackground() doesn't return a callback and app stuck at that possition.
My query code:
ParseQuery<OptionCodeDTO> mQuery;
mQuery = ParseQuery.getQuery(OptionCodeDTO.class);
mQuery.whereEqualTo("code", prCode);
mQuery.getFirstInBackground(new GetCallback<OptionCodeDTO>() {
#Override
public void done(OptionCodeDTO optionCodeDTO, ParseException e) {
if (isVisible()) {
if (e == null) {
OptionCode opCode = new OptionCode(optionCodeDTO);
mCodes.push(opCode);
printCodes();
prDescrLayout.setVisibility(View.VISIBLE);
prDescProgress.setVisibility(View.GONE);
mPRLable.setVisibility(View.GONE);
} else {
if (e.getCode() == ParseException.CONNECTION_FAILED) {
mPrDescr.setText(R.string.dtc_lookup_check_network);
} else if (e.getCode() == ParseException.OBJECT_NOT_FOUND) {
mPrDescr.setText(R.string.pr_lookup_code_not_found);
} else {
mPrDescr.setText(R.string.dtc_lookup_other_problems);
}
prDescrLayout.setVisibility(View.VISIBLE);
prDescProgress.setVisibility(View.GONE);
}
}
}
});
First of all, if your app ANRs (application not responding) because of something from UI thread that relies on background threads, that is incorrect architecture.
Probably you have to optimize your app's interaction with Parse. Generally it is a bad practice to make lots of saveInBackground, for example from inside a loop. You can add objects, those need to be saved, to a list and then use ParseObject.saveAllInBackground(objectList)
Also an idea to optimize is to use local storage - android's built in SQLite. For example if your app relies on something being saved to Parse, the logic is like this:
When saving object first you save to local DB and run a saveInBackground method.
When fetching objects you first fetch from your local DB and then run a getInBackground method, which inside a callback persists the information to your local DB.
This way you will make your app usable without internet connection.

Network query returns local datastore pointer

I have recently managed to switch to Local Datastore, moving all the needed parts of the database to the client. Furthermore I send a push notification whenever data changes so that the client data stays up to date.
Now my problem is that one of the pointers in my data keeps returning the old pointer after updating.
Here is the code that I use:
public ParseQuery<CircuitUnit> circuitUnits() {
ParseQuery<CircuitUnit> query = CircuitUnit.getQuery();
query.include(CircuitUnit.circuits);
query.include(CircuitUnit.guard);
query.setLimit(1000);
return query;
}
circuitUnits().findInBackground(
new FindCallback<CircuitUnit>() {
#Override
public void done(List<CircuitUnit> objects, ParseException e) {
Log.d(TAG, "Pinning circuitUnits " + objects.size());
// debugging loop of results
for (CircuitUnit circuitUnit : objects) {
if (circuitUnit.getObjectId().equals("TXEZDch6wK")) {
Guard guard = circuitUnit.getGuard();
Log.d(TAG, "Guard: " + guard.getObjectId());
}
}
// This function updates the Local datastore with the newly fetched objects
// updateServerDataPin(objects, PinsGlobal.CIRCUIT_UNITS,
e, callback);
}
});
The Logged output is: "Guard: L440gHXKTY" where it should be SDDg23rR4h
I have added a screenshot of the CircuitUnit table showing that SDDg23rR4h is indeed the expected objectId of the pointer.
I tried to create a sample project showing the issue as part of the bug report for parse.com, but that returned the correct pointer.
My theory is then that this problem lies in having data in the Local Datastore which somehow interferes with the result. Not that I understand why, cause I am clearly querying the network and not locally.
It seems as if the include statements are ignored and simply filled with the known data from the Local Datastore.
Has anyone experienced something similar or have a possible explanation to this behaviour?

Categories

Resources