未连接适配器;单击“后退”按钮并再次启动应用程序时跳过布局

时间:2018-06-23 12:14:37

标签: android android-recyclerview recycler-adapter

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。

当我单击“后退”按钮,然后再次启动该应用程序时。我得到的只是一个空白屏幕,即

enter image description here

已更新的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();
                    }
                });
    }
}

非常感谢您的任何建议,

5 个答案:

答案 0 :(得分:6)

单击后退按钮并重新启动应用程序时,将启动MainActivity的新实例。

同时,您的DatabaseManager单个。它的引用存储为 static 变量。它在活动娱乐中幸存下来。它会一直存在,直到进程终止。

因此,当您第二次运行queryAllInsects时,回调将发送到MainActivity实例,该实例不再可见。

您不应在MainActivity中保留对DatabaseManager的引用。这是内存泄漏,因为无法收集垃圾

答案 1 :(得分:1)

问题很可能是您在onCreate()中而不是onResume()中加载数据。当您按回“关闭应用程序”时,不一定要从内存中清除UI堆栈。这就是为什么当您返回应用程序时,它不会再次调用onCreate(),也不会再次加载数据。

一切保持不变,只是将数据加载从onCreate()移到onResume()。这样,无论何时向用户显示屏幕,数据都将加载。

答案 2 :(得分:1)

很少观察到

  1. 您仍然将MainActivity传递给BugsDbHelper类,请注意那里的引用。
  2. 在Singleton类中包括“清除方法”可能是一个好主意,应该在活动的onStop()或onDestroy()中调用它。首选onStop(),因为不能保证立即调用onDestroy()。
  3. Singleton类中的“清理方法”应执行以下操作: a)取消对您要求作为构造函数中的依赖项的参数,对象,上下文或回调的任何引用。 b)如果Singleton类创建了具有上下文相关性的“新”对象,请确保在这些类中也包含类似的清除方法。
  4. 为避免崩溃和片段/活动中的内存泄漏,请确保要清理onStop()中的回收者视图/适配器。回调可以随时接收,如果在后台进行活动时发生回调,则势必会收到一个“强制关闭”的幸运饼干。
  5. 关注活动/片段生命周期。很多问题只是因为忽略了生命周期回调。这些是有原因的,利用它们。

答案 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 ;
    }
}