Android Studio 3.2 Canary 18
kotlin_version = 1.2.50
I have a simple app that uses a recyclerview and adapter. When the app starts is load all the data. However, when I click the back button and start the app again. It won't display the data (blank). If I clear the app from memory and start the app. The data will load as normal.
I am loading the data from sqlite and the data is loaded each time. as it populates the insectDataModelList
.
After going into the RecyclerView.java
source code the reason is the mAdapter
is null. However, I have
checked that the adapter is correct when I set it to the recyclerview.
void dispatchLayout() {
if (mAdapter == null) {
Log.e(TAG, "No adapter attached; skipping layout");
// leave the state in START
return;
}
...
}
My MainActivity.java is Java
public class MainActivity extends AppCompatActivity {
private RecyclerView rvInsects;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
rvInsects = (RecyclerView)findViewById(R.id.recycler_view);
DatabaseManager databaseManager = DatabaseManager.getInstance(this);
databaseManager.queryAllInsects("friendlyName");
}
private void setupAdapter(List<InsectDataModel> insectDataModelList) {
final LayoutManager layoutManager = new LinearLayoutManager(
this, LinearLayoutManager.VERTICAL, false);
rvInsects.setLayoutManager(layoutManager);
rvInsects.setHasFixedSize(true);
final InsectAdapter insectAdapter = new InsectAdapter(insectDataModelList);
rvInsects.setAdapter(insectAdapter);
insectAdapter.notifyDataSetChanged();
}
/* Callback from database */
public void loadAllInsects(final Cursor cursor) {
InsectInteractorMapper insectInteractorMapper = new InsectInteractorMapperImp();
final List<InsectDataModel> insectDataModelList = insectInteractorMapper.map(cursor);
/* data loaded with 24 items */
setupAdapter(insectDataModelList);
}
}
InsectAdapter.kt is Kotlin.
class InsectAdapter(private val insectList: MutableList<InsectDataModel>)
: RecyclerView.Adapter<InsectAdapter.CustomInsectHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomInsectHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.insect_row_item, parent, false)
return CustomInsectHolder(view)
}
override fun onBindViewHolder(holder: CustomInsectHolder, position: Int) {
holder.tvFriendlyName.text = insectList[position].friendlyName
holder.tvScientificName.text = insectList[position].scientificName
}
override fun getItemCount(): Int {
return insectList.size
}
class CustomInsectHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivDangerLevel: DangerLevelView = itemView.findViewById(R.id.ivDangerLevel)
val tvFriendlyName: TextView = itemView.findViewById(R.id.tvFriendlyName)
val tvScientificName: TextView = itemView.findViewById(R.id.tvScientificName)
}
}
The database I use rxjava2 to do the query
public class DatabaseManager {
private static DatabaseManager sInstance;
private MainActivity mainActivity;
private BugsDbHelper mBugsDbHelper;
public static synchronized DatabaseManager getInstance(MainActivity context) {
if (sInstance == null) {
sInstance = new DatabaseManager(context);
}
return sInstance;
}
private DatabaseManager(MainActivity context) {
mBugsDbHelper = new BugsDbHelper(context);
mainActivity = context;
}
@SuppressLint("CheckResult")
public void queryAllInsects(String sortOrder) {
final InsectStorageInteractorImp insectStorageInteractorImp
= new InsectStorageInteractorImp(new InsectStorageImp(mBugsDbHelper.getReadableDatabase()));
insectStorageInteractorImp.getAllSortedInsects(sortOrder)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Cursor>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onSuccess(Cursor cursor) {
mainActivity.loadAllInsects(cursor);
disposable.dispose();
}
@Override
public void onError(Throwable e) {
disposable.dispose();
}
});
}
}
Everything works as expected when the apps installs for the first time. And if you clear it out of memory. However, its only when you click the back button, and then try and start the app it will not load any data because of the mAdapter being null in the RecyclerView class.
When I click the back button and then start the app again. All I get is a blank screen i.e.
Updated DatabaseManager class that removes the singleton and used a weakreference to ensure that the MainActivity instance is garbage collected.
public class DatabaseManager {
private WeakReference<MainActivity> mainActivity;
private BugsDbHelper mBugsDbHelper;
public DatabaseManager(MainActivity context) {
mBugsDbHelper = new BugsDbHelper(context);
mainActivity = new WeakReference<>(context);
}
@SuppressLint("CheckResult")
public void queryAllInsects(String sortOrder) {
final InsectStorageInteractorImp insectStorageInteractorImp
= new InsectStorageInteractorImp(new InsectStorageImp(mBugsDbHelper.getReadableDatabase()));
insectStorageInteractorImp.getAllSortedInsects(sortOrder)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Cursor>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onSuccess(Cursor cursor) {
mainActivity.loadAllInsects(cursor);
disposable.dispose();
}
@Override
public void onError(Throwable e) {
disposable.dispose();
}
});
}
}
Many thanks for any suggestions,
When you click the back button and relaunch the app, a new instance of MainActivity
is started.
At the same time, your DatabaseManager
is a singleton. Its reference is stored as a static variable. It survives the activity recreation. It will live until the process is killed.
So, when you run queryAllInsects
for the second time, the callback is sent to the old instance of MainActivity
, which is not visible anymore.
You should not keep a reference to MainActivity
in DatabaseManager
. It's a memory leak, because it cannot be garbage collected.
Few observations:
Put this 2 lines in onResume() and remove from onCreate() and try it.
DatabaseManager databaseManager = DatabaseManager.getInstance(this);
databaseManager.queryAllInsects("friendlyName");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With