安卓Volley - 屏幕旋转问题

17
我们如何使用Volley处理方向变化、Activities/Fragments的停止/恢复?
我知道对于GET请求,如果服务器发送正确的HTTP头,响应将被缓存,并且第二次尝试发起该请求时,我们将获得缓存的响应。
但是对于POST呢? 假设我发起了一个POST请求(即注册,我希望它只发生一次),我的应用程序转到后台,请求在应用程序仍处于后台时完成,然后我回到应用程序。 如何获取该请求的响应,或者如果请求仍在等待,则如何重新连接到它?
据我所知,Volley几乎没有为此提供支持。 我是对的吗? 有没有使用Volley轻松解决上述情况的方法?

5
我可以进行翻译。原文的意思是:当一个请求还未完成时,我该如何获取它的响应或重新连接到它?即使您的应用程序不在前台运行,也不会阻止线程的运行。您认为Request请求尚未被传递给您的原因是什么?使用保留的片段进行异步操作,这样无论屏幕方向如何更改,您的异步操作都有一个稳定的基础来进行通信。 - CommonsWare
由于某种原因,我一直认为Volley不会在后台交付响应(就像Robospice一样),但你是对的。我们必须调用requestQueue.cancel(...)来停止传递。我仍在考虑一种好的简单方法来正确处理在stop/resume期间重新传递响应。 - Ovidiu Latcu
4个回答

8
Volley并没有提供开箱即用的机制来解决这个问题,但你可以考虑使用Square的Otto库来优雅地处理类似你所遇到的情况。
实现Volley请求的监听器,以便将成功响应发布到Bus中,并将其封装为事件对象,例如“RegisterEventSuccess”(由您自定义)。让您的Activity或Fragment使用Otto的@Subscribe机制订阅此事件类型。例如,一个Activity发起了Volley请求并因屏幕方向改变而死亡,另一个Activity实例(也注册到Otto总线)就可以收到包含Volley请求响应的事件。
希望这对你有帮助。

这个解决方案听起来确实很有趣。你在哪里实现了它?你遇到了什么问题吗? - Rasmus
1
不想重开一个已经结束的问题,但我正在寻找一些使用Volley + Otto进行请求/响应管理的示例,这个人提供了一个非常干净的例子,将两者融合在一起。https://github.com/tslamic/AndroidExamples/tree/master/HttpBinVolley - Selecsosi
5
使用这个解决方案,如果在活动之间过渡(第一个活动的 onDestroy 和新活动的 onCreate 之间)时网络响应返回,你将如何处理可能性?在这种情况下,两个活动都不会注册到总线并等待事件。@Producer 方法可以解决这个问题,但它们似乎不适合作为通用的网络缓存,并且与原始模式不太一致。 - Kyle Ivey
@kyle-ivey:我在这里的答案中提供了解决方案https://dev59.com/FGQm5IYBdhLWcg3w8Sve#23686294。它是一个解决方案,但可能不是唯一的解决方案。 PS:我首先考虑了“生产者”方法,但因为它不符合我的模式而放弃了它。 - Nilzor

4
我现在尝试解决@kyle-ivey的问题,即在onPause()onResume()之间到达的响应将被丢弃。这是一个真正的问题,因为我在一个实时应用程序中遇到过。我的方法基于Thomas Moerman的答案中实现的事件总线模式,尽管我重新从头开始实现了一个示例应用程序。它依赖于Otto Event bus LibraryGsonVolley。它是在IntelliJ 13 Ultimate中使用Maven来解决依赖关系实现的。 解决方案:我添加了一个类作为HTTP响应缓冲区,它接管了在Activity过渡期间监听事件的责任。完成后,Activity主动轮询可能在Activity与事件总线断开连接时到达的任何响应。它在onPauseonResume事件中挂钩/关闭,与事件总线注册方式相似:
@Override
protected void onPause() {
    super.onPause();
    ServiceLocator.ResponseBuffer.startSaving(); // The buffer takes over
    ServiceLocator.EventBus.unregister(this);    // Unregistering with Otto
}

@Override
protected void onResume() {
    ServiceLocator.EventBus.register(this);         // Re-registering
    ServiceLocator.ResponseBuffer.stopAndProcess(); // Process any responses buffered
}

这里是ResponseBuffer类的实现。
注意事项1:如果活动从未恢复,并且在任何未来的活动中都没有调用stopAndProcess()stopAndPurge(),则缓冲区可能会导致内存泄漏。请注意如何使用它。一个安全的模式是在所有活动的onResume()中使用stopAndProcess()
注意事项2:它不是线程安全的。如果在线程切换开始保存和注销事件总线之间出现,则可能会收到两次或零次事件。
示例包括一些UI和支持类的测试代码,但如果您想在单独的项目中利用此模式,则需要以下包中的主要类:
  • nilzor.ottovolley.core
  • nilzor.ottovolley.messages
请查看github仓库OttoVolleyDoneRight,其中包含完整的示例和用于测试的用户界面。

1

我遇到了使用Volley库从网络下载数据的问题,但有一个简单的解决方案。如果你的应用程序按照Google的建议使用片段,则应该在onCreateView方法中设置setRetainInstance(true);以防止用户在加载数据时旋转屏幕导致崩溃。

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View theView = inflater.inflate(R.layout.fragment_studios, container, false);
    setRetainInstance(true);
    lvStudios = (ListView) theView.findViewById(R.id.lvStudios);
  return theView;
}

1
非常古老的话题,但这仍然是最佳模式。 - Masoud Dadashi

0
假设您有一个登录按钮,点击Btn后通过volley调用服务器,突然屏幕方向更改,volley重新启动,并且应用程序崩溃。
因此,我的解决方案是:在单击Btn时首先锁定屏幕方向,当Volley响应到来时您解锁屏幕方向...

...........这里 单击时---lockDeviceRotation(true) //锁定 响应时--lockDeviceRotation(false) //解锁

public void lockDeviceRotation(boolean value) {
    if (value) {
        int currentOrientation = getResources().getConfiguration().orientation;
        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
        }
    } else {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接