Android Studio 3.2 Canary 18
kotlin_version = 1.2.50
我有一个使用recyclerview和适配器的简单应用程序。当应用启动时,加载所有数据。 但是,当我单击“后退”按钮并再次启动该应用程序时。它不会显示数据(空白)。 如果我从内存中清除该应用程序并启动该应用程序。数据将正常加载。
我正在从sqlite加载数据,并且每次都加载数据。因为它填充了insectDataModelList
。
进入RecyclerView.java
源代码后,原因是mAdapter
为空。但是,我有
将适配器设置为recyclerview时检查适配器是否正确。
void dispatchLayout() {
if (mAdapter == null) {
Log.e(TAG, "No adapter attached; skipping layout");
// leave the state in START
return;
}
...
}
我的MainActivity.java是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是科特林。
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)
}
}
我使用rxjava2进行查询的数据库
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();
}
});
}
}
首次安装应用程序时,一切都会按预期进行。如果清除内存不足。 但是,仅当您单击后退按钮,然后尝试启动该应用程序时,它才会加载任何数据 因为mAdapter在RecyclerView类中为null。
当我单击“后退”按钮,然后再次启动该应用程序时。我得到的只是一个空白屏幕,即
已更新的DatabaseManager类,该类删除了单例并使用了弱引用来确保MainActivity实例被垃圾回收。
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();
}
});
}
}
非常感谢您的任何建议,
答案 0 :(得分:6)
单击后退按钮并重新启动应用程序时,将启动MainActivity
的新实例。
同时,您的DatabaseManager
是单个。它的引用存储为 static 变量。它在活动娱乐中幸存下来。它会一直存在,直到进程终止。
因此,当您第二次运行queryAllInsects
时,回调将发送到MainActivity
的旧实例,该实例不再可见。
您不应在MainActivity
中保留对DatabaseManager
的引用。这是内存泄漏,因为无法收集垃圾。
答案 1 :(得分:1)
问题很可能是您在onCreate()中而不是onResume()中加载数据。当您按回“关闭应用程序”时,不一定要从内存中清除UI堆栈。这就是为什么当您返回应用程序时,它不会再次调用onCreate(),也不会再次加载数据。
一切保持不变,只是将数据加载从onCreate()移到onResume()。这样,无论何时向用户显示屏幕,数据都将加载。
答案 2 :(得分:1)
很少观察到
答案 3 :(得分:1)
将这两行放在onResume()中,然后从onCreate()中删除并尝试。
DatabaseManager databaseManager = DatabaseManager.getInstance(this);
databaseManager.queryAllInsects("friendlyName");
答案 4 :(得分:1)
我建议进行以下更改:
MainActivity ,您在活动中编写的代码越少越好,请将所有数据检索部分移至DatabaseManager
。还要设置一次RecyclerView
,并仅在适当的时候更新数据集:
public class MainActivity extends AppCompatActivity {
private List<InsectDataModel> insectDataModelList = new ArrayList<>();
private Disposable disposable;
private RecyclerView rvInsects;
private InsectAdapter insectAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setupAdapter();
//Request Data, take advantage of RxJava to load data asynchronously
DatabaseManager.getInstance(this)
.queryAllInsects("friendlyName")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<InsectDataModel>>() {
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onSuccess(List<InsectDataModel> response) {
insectDataModelList.clear();
insectDataModelList.addAll(response);
insectAdapter.notifyDatasetChanged();
}
@Override
public void onError(Throwable e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});;
}
private void setupAdapter() {
//Setup RecyclerView Only need to be called once
rvInsects = (RecyclerView)findViewById(R.id.recycler_view);
LayoutManager layoutManager = new LinearLayoutManager(this); // LinearLayoutManager is Vertical by default
rvInsects.setLayoutManager(layoutManager); // You don't event have to define it as RecyclerView use LinearLayoutManager.Vertical by default
rvInsects.setHasFixedSize(true);
insectAdapter = new InsectAdapter(insectDataModelList);
rvInsects.setAdapter(insectAdapter);
}
@Override
protected void onDestroy() {
//Dispose observer if activity is destroyed to prevent memory leak
if(disposable != null && !disposable.isDisposed())
disposable.dispose();
super.onDestroy();
}
}
在 DatabaseManager 中,我们没有观察数据源(游标)并通过回调通知请求者(活动),而是得到了数据流并将其传递给呼叫者以观察:
public class DatabaseManager {
private static DatabaseManager sInstance;
private BugsDbHelper mBugsDbHelper;
public static synchronized DatabaseManager getInstance() {
if (sInstance == null) {
sInstance = new DatabaseManager();
}
return sInstance;
}
private DatabaseManager() {
// Move the actualy database initiation to application class or singleton
mBugsDbHelper = BugsDbHelper.getInstance(); // or ApplicationController.getDbHelper();
}
@SuppressLint("CheckResult")
public SingleObserver<List<InsectDataModel>> queryAllInsects(String sortOrder) {
final InsectStorageInteractorImp insectStorageInteractorImp
= new InsectStorageInteractorImp(new InsectStorageImp(mBugsDbHelper.getReadableDatabase()));
insectStorageInteractorImp.getAllSortedInsects(sortOrder)
.map(new Function<Cursor, List<Object>>() {
@Override
public List<Object> apply(Cursor cursor) throws Exception {
InsectInteractorMapper insectInteractorMapper = new InsectInteractorMapperImp();
return insectInteractorMapper.map(cursor);
}
});
}
}
现在,这里的解决方案是依靠RxJava
将回调模式更改为观察者模式。因此,我们没有通过活动(回调)并等待被调用,而是获得了数据 steram (可观察)并观察用于响应。这样可以一起消除泄漏问题,并提高可读性和可维护性。
也不要忘记将Database
初始化移动到Application类或Singleton实例,以防止进行多个实例化。更简单的解决方案是:
public class ApplicationController extends Application {
private BugsDbHelper mBugsDbHelper;
@Override
public void onCreate() {
super.onCreate();
mBugsDbHelper = new BugsDbHelper(this);
}
public BugsDbHelper getDbHelper(){
return mBugsDbHelper ;
}
}