I'm getting java.util.ConcurrentModificationException and i don't know why.
In the Logcat it points to this code but i don't see anything that could cause a ConcurrentModificationException.
private void initRecyclerView() {
Main.musicList = Main.songs.songs;
Log.d(TAG, "Main.musicList: " + String.valueOf(Main.musicList));
if ((Main.musicList != null) && (!Main.musicList.isEmpty())) {
// Connects the song list to an adapter
// (Creates several Layouts from the song list)
allSongsAdapter = new AllSongsAdapter(getActivity(), Main.musicList);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerViewSongs.setLayoutManager(linearLayoutManager);
recyclerViewSongs.setHasFixedSize(true);
recyclerViewSongs.setAdapter(allSongsAdapter);
}
}
Main.musicList is a public static ArrayList musicList = null; in another class.
And Main.songs.songs is a public ArrayList songs = null; in my class where i get all the songs on the device and populate the arraylist with them.
in onDestroy i call:
musicList = null;
EDIT
Ok i found the problem, when i'm not calling onDestroy musicList = null there is no ConcurrentModificationException.
But how do i dereference an arraylist in onDestroy so it can be garbage collected?
EDIT
So the problem wasn't the onDestroy call, the error occurs when i open the app and my arraylists are populated with all songs, then i close the app and reopen it and then the exception is thrown.
How i populate the songs array
songs = new ArrayList<>();
// Columns retrieved from the system database (MediaStore.Audio.Media).
String[] projection1 = {
SONG_ID,
SONG_TITLE,
SONG_ARTIST,
SONG_ALBUMID,
SONG_ALBUM,
SONG_FILEPATH,
SONG_DURATION,
SONG_YEAR,
};
// Limits results to only show MUSIC files.
// It's a SQL "WHERE" clause - it becomes `WHERE IS_MUSIC NOT EQUALS ZERO`.
final String musicsOnly = SONG_IS_MUSIC + "!=0";
// Querying the Media DATABASE.
cursor = resolver.query(musicUri, projection1, musicsOnly, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
do {
// Creating a SONG from the VALUES in each column.
Song song = new Song(cursor.getLong(cursor.getColumnIndexOrThrow(SONG_ID)),
cursor.getString(cursor.getColumnIndexOrThrow(SONG_FILEPATH)));
song.setTitle(cursor.getString(cursor.getColumnIndexOrThrow(SONG_TITLE)));
song.setArtist(cursor.getString(cursor.getColumnIndexOrThrow(SONG_ARTIST)));
song.setAlbumID(cursor.getLong(cursor.getColumnIndexOrThrow(SONG_ALBUMID)));
song.setAlbum(cursor.getString(cursor.getColumnIndexOrThrow(SONG_ALBUM)));
song.setDuration(cursor.getLong(cursor.getColumnIndexOrThrow(SONG_DURATION)));
song.setYear(cursor.getInt(cursor.getColumnIndexOrThrow(SONG_YEAR)));
// Using the previously created maps to add the current song GENRE.
String currentGenreID = songIdToGenreIdMap.get(Long.toString(song.getId()));
String currentGenreName = genreIdToGenreNameMap.get(currentGenreID);
song.setGenre(currentGenreName);
// Adding the Song to the global array list 'songs'.
songs.add(song);
} while (cursor.moveToNext());
}
}catch (Exception e){
// Exception caught because no songs were found.
Log.e(TAG, "Exception caught because no songs were found!", e);
throw new Exception();
}finally {
if (cursor != null ){
cursor.close();
}
}
Here is a high level approach which will allow GC to clear your memory properly.
Inside your Activity class define member:
private List<Song> mMySongs;
In onCreate method, you init RecyclerView and then read songs to the array:
// getSongs is your models method where you read the songs and return them as array
mMySongs = getSongs();
// Enter code to create the adapter and set it for RecyclerView
Now that you are using strong reference instead of static reference, when your Activity is destroyed, GC can clear up the memory and when you relaunch the Activity it will query the songs again.
Related
I am making an Android SMS app.
I have used a RecyclerView to show all the messages.
SmsAdapter.java :
public class SmsAdapter extends RecyclerView.Adapter<SmsAdapter.SmsViewHolder>{
private static final String TAG = " [MY_DEBUG] ";
ArrayList<String> sms_messages_list;
Context context;
public SmsAdapter(Context ct, ArrayList<String> array_list){
context = ct;
sms_messages_list = array_list;
}
public void insert(int position, String new_sms) {
Log.d(TAG, "SmsAdapter: insert(): adding a new message at position + " + position);
sms_messages_list.add(position, new_sms);
notifyDataSetChanged();
}
//more code here, might add if you guys ask so.
created the SmsAdapter object and initializing it with an item at index 0 in MainActivity.java :
sms_messages_list.add(0, "dummy");
sms_adapter = new SmsAdapter(this, sms_messages_list);
messages.setAdapter(sms_adapter);
messages.setLayoutManager(new LinearLayoutManager(this));
where sms_messages_list is an ArrayList and messages is the RecyclerView
I am using AsyncTask to read database in background thread (in the doInBackground()) and then adding items to the SmsAdapter inside the onPostExecute().
onPostExecute() :
#Override
protected void onPostExecute(ArrayList<String> msg_list) {
super.onPostExecute(msg_list);
MainActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return;
}
int j=0;
try {
while (j < msg_list.size()) {
activity.sms_adapter.insert(j, msg_list.get(j).toString());
j++;
}
}
catch (Exception e){
Log.d(TAG, "onPostExecute: exception : " + e);
}
db1.startTransaction(); // this line I did not put in original question and this is what caused the porblem ... replaced it with db1.endTransaction() and code works
db1.close();
}
where the msg_list is an ArrayList which has all the SMS messages in String format(one String item = one SMS)
////
EDIT 1 : Here is the entire AsyncTask code :
https://pastebin.com/q495VjMs
////
When I run this, I only see the one item that is "dummy" and the activity does not even respond. The database is correctly read, the msg_list has all the messages as expected. I am unable to find where I am going wrong in this. Please help!
App not responding
No other items showing in RecyclerView
The only error in logcat:
2020-07-30 20:09:56.344 578-599/system_process E/ActivityManager: ANR in com.example.mynewsmsapp_kotlin (com.example.mynewsmsapp_kotlin/.MainActivity)
PID: 2455
Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 2. Wait queue head age: 7166.5ms.)
PS: Excuse me for the weird name of my app. It does not make sense I know.
Please make this code to like this :
public SmsAdapter(Context ct, ArrayList<String> array_list){
this.context = ct;
this.sms_messages_list.addAll(array_list);
}
//////////////////////////////////////////////////////////////////////////////
int j=0;
try {
while (j < msg_list.size()) {
activity.sms_adapter.insert(msg_list.get(j).toString());
j++;
}
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
public void insert(String new_sms) {
sms_messages_list.add(new_sms);
notifyDataSetChanged();
}
Earlier I by mistakely put db.startTransaction() instead of db.endTransaction() at the end of method onPostExecute(). Now I made it db.endTrasaction() and the code works.
I am working on a book app and I save the book contents in SQLite database which I placed in my assets folder in Android Studio. But now I want to retrieve the values in the database in an activity in my app, but with the method I have seen online I can only retrieve one value from the database.
Please bear with me because I don't know if I'm properly explaining what I want but let me show the code so I can explain better.
listItem = new ArrayList<>();
database = new Database(this);
// song = String.valueOf(database.getChapter());
SQLiteDbHelper dbHelper = new SQLiteDbHelper(this);
dbHelper.openDatabase();
modelArrayList = dbHelper.getDetails();
adapter = new SongAdapter(modelArrayList);
chapFind = findViewById(R.id.chap_find);
chapGo = findViewById(R.id.chap_go);
String tokenID = FirebaseInstanceId.getInstance().getToken();
Model count = modelArrayList.get(0);
final String chap = String.valueOf(count.getIds());
Toast.makeText(MainActivity.this,chap,Toast.LENGTH_LONG).show();
mDatabasse = FirebaseDatabase.getInstance().getReference().child("Token");
chapGo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String num = chapFind.getText().toString();
if (num.equals(chap)){
Toast.makeText(MainActivity.this,"very good!",Toast.LENGTH_LONG).show();
}else {
Toast.makeText(MainActivity.this,"very bad",Toast.LENGTH_LONG).show();
}
}
});
}
So what I am trying to do with this code - is to get user input of the chapter they would like to read. Then - check if that chapter exists in the database and if it does app will send user to another activity where it will fully display the content of that chapter
I am using RealmRecyclerViewAdapter and is using RealmResult as my dataset.
Here's the Adapter code :
private RealmList<MessageItem> mDataset;
public ChatRoomAsUserAdapter(Context context, #Nullable RealmList<MessageItem> data) {
super(data, true);
this.context = context;
this.userId = Utilities.getUserInformation(context, SharedPreferencesList.userIDString);
this.mDataset = data;
}
Here's how I find the RealmResult in Activity for chat room initialization :
AppController.realm.beginTransaction();
// Getting chat inside the specific room id
ChatRoomWrapper chatFriendRealmResult = AppController.realm.where(ChatRoomWrapper.class)
.equalTo(ChatRoomWrapperFields.ROOM_ID, roomId).findFirst();
if (chatFriendRealmResult != null){
chatRoomAsUserAdapter = new ChatRoomAsUserAdapter(this, chatFriendRealmResult.message);
AppController.realm.commitTransaction();
}
...
After the initialization, I make an API call and in the result :
chatRoomWrapper.message.deleteAllFromRealm();
chatRoomWrapper.message.addAll(api.res.message_item_list);
Appcontroller.realm.copyToRealmOrUpdate(chatRoomWrapper);
And I get this error :
java.lang.ArrayIndexOutOfBoundsException: rowIndex is less than 0.
at io.realm.internal.LinkView.nativeGetTargetRowIndex(Native Method)
at io.realm.internal.LinkView.getTargetRowIndex(LinkView.java:82)
at io.realm.RealmList.get(RealmList.java:452)
Does that mean that the content of RV is changed unexpectedly? I am using RealmRecyclerViewAdapter which should handle that right?
UPDATE :
Here's an example on how I get the data :
getItem(position).getMsg_date();
where
public class MessageItem extends RealmObject {
...
public String msg_date = "";
}
EDIT :
This is how I update the adapter after getting information from server :
AppController.realm.beginTransaction();
// Create new ChatFriendWrapper and input the Room ID into it to update
// the Room ID if found
ChatRoomWrapper chatRoomWrapper = AppController.realm.where(ChatRoomWrapper.class)
.equalTo("room_id", roomId).findFirst();
if (chatRoomWrapper != null) {
if (output.flag.equals(Constants.flagRefresh)) {
counter = 0;
chatRoomWrapper.message.deleteAllFromRealm();
chatRoomWrapper.message.addAll(output.message_item_list);
MIApplication.realm.copyToRealmOrUpdate(chatRoomWrapper);
manager.scrollToPosition(0);
} else if (output.flag.equals(Constants.flagLoad)) {
chatRoomWrapper.message.addAll(output.message_item_list);
AppController.realm.copyToRealmOrUpdate(chatRoomWrapper);
}
} else {
ChatRoomWrapper newChatRoomWrapper = new ChatRoomWrapper();
newChatRoomWrapper.room_id = roomId;
newChatRoomWrapper.message.addAll(output.message_item_list);
AppController.realm.copyToRealmOrUpdate(newChatRoomWrapper);
setupChatRoom();
}
AppController.realm.commitTransaction();
The culprit was the usage of position instead of holder.getAdapterPosition() to get the item. I used to do this :
viewholder.message.setText(getItem(position).message);
now it's
viewholder.message.setText(getItem(holder.getAdapterPosition()).message);
And it cleared the error. I suddenly remember when I used to have problems with infinite scrolling in RecyclerView, I came across this kind of error too.
I'm going to ask a huge favor here.
I have a view that when it opens, it should show every beverage from the database, and show that on the screen.
It also has to add a + button, an amount label next to it, and a - button. This should be done for every item.
The tables I'm getting the items from is called dhh_item by the way.
Now, I've got this:
public ArrayList<Item> getBeverages(Item item) {
ArrayList<Item> items = new ArrayList<>();
if (item != null) {
// First open a database connnection
DatabaseConnection connection = new DatabaseConnection();
if (connection.openConnection()) {
// If a connection was successfully setup, execute the SELECT statement.
ResultSet resultset = connection.executeSQLSelectStatement(
"SELECT * FROM dhh_item ");
if (resultset != null) {
try {
while (resultset.next()) {
String itemName = resultset.getString("itemName");
String status = resultset.getString("status");
String description = resultset.getString("description");
int price = resultset.getInt("price");
Item newItem = new Item(itemName, status, description, price);
items.add(newItem);
}
} catch (SQLException e) {
System.out.println(e);
items.clear();
}
}
// else an error occurred leave array list empty.
// We had a database connection opened. Since we're finished,
// we need to close it.
connection.closeConnection();
}
}
return items;
}
Is this correct in any way. Would I retrieve any data at all? (The .getString()'s are correct.)
Now, this method is inside of another Class (ItemDAO).
Can I call this from my view? How would I get it to make a new label + button for each?
Thanks a lot for those who could help me out on this one!
At the end, it should be looking like this:
for each beverage in the table.
Sounds rather straigh forward
Collection<Item> items=dao.getBeverages(someItem) // get all items
for(Item item:items){
label=new JLabel(item.getYourItemNameOrLabelOrhatever) // this will be the "coca-cola"
incButton=new JButton(incrementButtonAction); // craete/get some action
decButton=new JButton(decrementButtonAction); // same here
counter=new JLabel("0");
yourContainer.add(label);
yourContainer.add(incButton);
yourContainer.add(label);
yourContainer.add(decButton);
yourContainer.revalidate();
}
I am creating a sample gallery app, I am trying to store Gallery Items in local sqlite database
Methods for Adapter.class :
public List<String> getImagePath()
{
ArrayList<String> paths = new ArrayList<String>();
String selectQuery = "SELECT * FROM " + Databaseconnect.TABLE_FILE;
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst())
{
do {
paths.add(cursor.getString(3).toString());
// Log.d("getPathImage:", cursor.getString(0).toString());
} while (cursor.moveToNext());
}
return paths;
}
than
MuAdapter= new muadapter(Activityname.this);
mudapter.open();
ArrayList<String> list =getImagePath();
but I'm having an error On
ArrayList<String> list =getImagePath();
How to initialize this method? Please Give me a solution.
MuAdapter= new muadapter(Activityname.this);
// this line is broke. Use something like "MuAdapter muAdapter = new ..."
mudapter.open();
// then this line can work
ArrayList<String> list =getImagePath();
// not sure about this, but don't you need an objectrefernece here, that you call getImagePath() on?!