Android 方向更改后无法重新启动加载程序

Android 方向更改后无法重新启动加载程序,android,Android,我想用一个演示来展示: enter code here @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button button = (Button) findViewById(R.id.button1); button.setOnClickListener

我想用一个演示来展示:

enter code here
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button button = (Button) findViewById(R.id.button1);
    button.setOnClickListener(buttonClickListener);
}

private OnClickListener buttonClickListener = new OnClickListener() {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        startMyLoader();
    }

};

private void startMyLoader() {
    getLoaderManager().destroyLoader(0);
    getLoaderManager().restartLoader(0, null, myLoaderListener);
}

/**
 * The listener for the group metadata loader.
 */
private final LoaderManager.LoaderCallbacks<Cursor> myLoaderListener 
    = new LoaderCallbacks<Cursor>() {

    @Override
    public CursorLoader onCreateLoader(int id, Bundle args) {
        return new CursorLoader(LoaderDemoActivity.this, 
        ContactsContract.Contacts.CONTENT_URI, 
        null, null, null, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        cursor.moveToPosition(-1);
        if (cursor.moveToNext()) {
            Context context = getApplicationContext();
            CharSequence text = "Load finished!";
            int duration = Toast.LENGTH_SHORT;

            Toast toast = Toast.makeText(context, text, duration);
            toast.show();
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
    }
};
enter code here
在此处输入代码
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
按钮按钮=(按钮)findViewById(R.id.button1);
button.setOnClickListener(ButtonClicklListener);
}
私有OnClickListener按钮ClickListener=新OnClickListener(){
@凌驾
公共void onClick(视图v){
//TODO自动生成的方法存根
startMyLoader();
}
};
私有void startMyLoader(){
getLoaderManager().destroyLoader(0);
getLoaderManager().restartLoader(0,null,myLoaderListener);
}
/**
*组元数据加载程序的侦听器。
*/
专用最终LoaderManager.LoaderCallbacks myLoaderListener
=新的LoaderCallbacks(){
@凌驾
公共游标加载程序onCreateLoader(int-id,Bundle-args){
返回新的游标装入器(LoaderDemoActivity.this,
Contacts contract.Contacts.CONTENT\u URI,
空,空,空,空);
}
@凌驾
public void onLoadFinished(加载器,光标){
游标。移动位置(-1);
if(cursor.moveToNext()){
Context=getApplicationContext();
CharSequence text=“加载完成!”;
int duration=Toast.LENGTH\u SHORT;
Toast Toast=Toast.makeText(上下文、文本、持续时间);
toast.show();
}
}
@凌驾
公共void onLoaderReset(加载器){
}
};
在这里输入代码
方向改变后,我点击了按钮, 可以调用onCreateLoader, 但不会调用onLoadFinished

这似乎很奇怪


提前感谢您的帮助。

来自android开发网站

“它们在运行时自动重新连接到最后一个加载程序的光标 在配置更改后重新创建。因此,它们不需要 重新查询他们的数据。”

据我所知,即使我们明确启动加载程序,加载程序也不会启动。因为我们正在调用的destroy在被销毁后实际上应该调用
onLoaderReset()
。但该方法不会在方向更改后调用,而是在更改之前调用

但我可能在这方面错了。这是我的假设。进一步讨论将不胜感激。

您不需要(也不应该)销毁您的
加载程序来重新加载它<代码>加载程序
类旨在可重用

改用
initLoader
。例如:

getLoaderManager().initLoader(0, null, myLoaderListener);
如果要强制重新加载allready注册加载程序:

getLoaderManager().getLoader(0).forceLoad();
如果您不确定配置更改事件发生后是否存在
Loader
实例allready,请使用
initLoader
而不是
getLoader
来检索您可以调用
forceLoad()
Loader
实例

getLoaderManager().initLoader(0,null,myLoaderListener).forceLoad()


如果您使用支持库,那么即使在第一次实例化之后也要使用
forceLoad
——可能有一个bug——我提醒自己在这个论坛上有一些关于它的问题——尝试搜索以前的帖子。

我想我已经找到了原因

在activityoncreate中,它将加载所有LoaderManger(它自己的或它的子片段) 来自非配置实例

    if (mLastNonConfigurationInstances != null) {
        mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
    }
在Activity onStart中,它将尝试启动自己的LoaderManger

    if (!mLoadersStarted) {
        mLoadersStarted = true;
        if (mLoaderManager != null) {
            mLoaderManager.doStart();
        } else if (!mCheckedForLoaderManager) {
            mLoaderManager = getLoaderManager(-1, mLoadersStarted, false);
        }
        mCheckedForLoaderManager = true;
    }

但在更改配置后,mLoaderManager==null,因此它不会启动它。 这就是问题所在! 如果尝试启动属于此loaderManager的loader,它将失败

void installLoader(LoaderInfo info) {
    mLoaders.put(info.mId, info);
    if (mStarted) {
        // The activity will start all existing loaders in it's onStart(),
        // so only start them here if we're past that point of the activitiy's
        // life cycle
        info.start();
    }
}
请注意在LoaderManager启动时将设置为“true”的mStarted值

解决这个问题有两种方法

  • 在onCreate()中调用getLoaderManger(),它将重新分配mLoaderManager 并准备好在Subsuqent onStart()中启动

    公共LoaderManager getLoaderManager(){ if(mLoaderManager!=null){ 返回mLoaderManager; } mCheckedForLoaderManager=true; mLoaderManager=getLoaderManager(-1,mloaderStarted,true); 返回mLoaderManager; }

  • 让加载程序位于片段中。因为在片段的onStart()中, 它将启动自己的LoaderManager

    如果(!mloaderstarted){ mloaderstarted=true; if(!mCheckedForLoaderManager){ mCheckedForLoaderManager=true; mLoaderManager=mActivity.getLoaderManager(mIndex,mloaderstarted,false); } if(mLoaderManager!=null){ mLoaderManager.doStart(); } }


  • 我也面临同样的问题。请在onCreate中尝试调用this.getSupportLoaderManager()。
    它解决了我的问题。希望它也能帮助您

    确保您在使用片段时没有检查
    savedStateInfo
    ,然后在activity
    onCreate中调用加载程序

    @Override
    public void onCreate(Bundle savedInstanceState) {
    
        // used to not overlap fragments
        if (savedInstanceState != null) {
            return null;
        }
    
        loadFragments();
    
        getSupportLoaderManager().restartLoader(LISTS_LOADER, null, this);
    }
    

    如果您需要检查
    savedInstanceState
    片段,您可以检查加载程序完成加载后应创建的任何类变量,因为活动在旋转时会被破坏,但在向后旋转时会从以前的状态提升。

    “但在配置更改后,mLoaderManager==null,因此它不会启动它”-如果是这样,则在调用
    getLoaderManager().destroyLoader(0)时会得到NullPOinterException-您可能没有抓住要点;),请使用forceLoad,不要每次都销毁重新创建加载程序,因为它违反了加载程序的设计方式。mLoaderManager将由“getLoaderManager(-1,MLOAderStarted,false”分配如果它为null。因此它不会抛出NullPointerException。我必须重新创建加载程序,因为在其创建过程中应该更改URI之类的内容。这显然像是bug。查看LoaderManagerImpl中的源代码,它的onRetain将mStarted设置为false。唯一将mStarted设置为true的位置是在doStart()中。如果doStart()不会被调用,不会启动新创建的加载程序(请参阅加载程序)