从 AsyncTask 返回数据到 MainActivity 的最佳方法
Posted
技术标签:
【中文标题】从 AsyncTask 返回数据到 MainActivity 的最佳方法【英文标题】:Best way to return data to MainActivity from AsyncTask 【发布时间】:2020-02-27 00:54:23 【问题描述】:我在我的应用程序中使用ASyncTask
通过 REST API 从 Web 服务 (Bitly) 获取一些数据(短 URL)。
当ASyncTask
完成时,我想将结果传回我的MainActivity
。
使用AsyncTask
的onPostExecute
方法可以将数据返回到MainActivity
。
我已经阅读并阅读了有关如何执行此操作的信息,似乎有两种通用方法。
最初我使用的是“WeakReference”方法,即在 AsyncTask
类的开头创建对 MainActivity
的弱引用,如下所示:
private class getShortURL extends AsyncTask<String, Void, String>
private WeakReference<MainActivity> mainActivityWeakReference;
myASyncTask(MainActivity activity)
mainActivityWeakReference = new WeakReference<>(activity);
etc etc
使用这种方法,您的 AsyncTask
类位于您的 MainActivity
类之外,因此需要通过弱引用来引用很多东西。
这工作得很好(除了我怀疑——可能是错误的——这个弱引用可能是偶尔出现 NPE 的原因),但后来我找到了另一种做事方式。
第二种方法涉及将ASyncTask
类移动到MainActivity
类中。
通过这种方式,我能够直接访问 MainActivity
类中可访问的所有内容,包括在 MainActivity
中定义的 UI 元素和方法。这也意味着我可以访问字符串等资源,并且可以生成toasts
来告知用户正在发生的事情。
在这种情况下,可以删除上面的整个 WeakReference
代码,并且可以将 AsyncTask
类设为私有。
然后我也可以直接在onPostExecute
中执行此类操作,或者将其保存在MainActivity
中的一个方法中,我可以直接从onPostExecute
调用:
缩短进度条.setIndeterminate(false);
short_progress_bar.setVisibility(View.INVISIBLE);
if (!shortURL.equals(""))
// Set the link URL to the new short URL
short_link_url.setText(shortURL);
else
CommonFuncs.showMessage(getApplicationContext(), getString(R.string.unable_to_shorten_link));
short_link_url.setHint(R.string.unable_to_shorten_link);
(请注意,CommonFuncs.showMessage()
是我自己对 toast 函数的封装,以便于调用)。
但是,android Studio 随后会发出警告“AsyncTask
类应该是静态的,否则可能会发生泄漏”。
如果我将方法设为静态,则会收到一条警告,指出我想从 onPostExecute
调用的来自 MainActivity
的方法不能被调用,因为它是非静态的。
如果我将 MainActivity
中的那个方法设为静态方法,那么它就无法访问字符串资源和任何其他非静态方法——我就掉进了兔子洞!
如果我只是将代码从MainActivity
中的方法移动到onPostExecute
方法中,正如您所期望的那样,情况也是如此。
所以...
WeakReference
方法实际上是更正确、更安全的方法吗?
如果我使用WeakReference
方法,我如何创建像toasts
这样需要在UI 线程上运行并访问字符串的东西
来自MainActivity
的资源等?
我在某处读到了有关创建interface
的信息,但有点迷路了,再也找不到了。这也不会像WeakReference
那样对MainActivity
有同样的依赖,这是一件坏事吗?
我真的在寻找有关如何将一些数据返回到 MainActivity
和从 AsyncTask
的 UI 线程的最佳实践指南,该指南是安全的,不会有内存泄漏的风险。
【问题讨论】:
奇怪的故事。我在活动中有很多 AsyncTasks,但从未收到过这样的警告。请从任务中删除您自己的祝酒词,看看是否有什么不同。 删除 toast 没有任何区别。事实上,在我添加 toasts 之前很久我就遇到了这些问题。 【参考方案1】:将 AsyncTask 作为非静态方法真的是一件坏事吗? (我的应用在 AS 中似乎可以很好地处理此警告,但我显然不想在我的应用中造成内存泄漏。
是的,你的 Views 和你的 Context 会泄露。
旋转足够多,您的应用将崩溃。
WeakReference 方法实际上是更正确、更安全的方法吗?
这是一头死猪的口红,在这种情况下,WeakReference 更像是一种破解而不是解决方案,绝对不是正确的解决方案。
您正在寻找的是一种来自 something 的事件总线形式,它比 Activity 寿命更长。
您可以为此使用保留的 Fragments* 或 Android 架构组件 ViewModel。
您可能需要引入观察者模式(但不一定是 LiveData)。
如果我使用 WeakReference 方法,如何创建需要在 UI 线程上运行的 toast 以及从 MainActivity 访问字符串资源等内容?
不要在doInBackground()
中运行那种东西。
我真的在寻找关于如何将一些数据从 AsyncTask 中返回到 MainActivity 和 UI 线程的最佳实践指南,该 AsyncTask 是安全的并且不会有内存泄漏的风险。
最简单的方法是使用this library(或者写一些你自己做同样事情的东西,取决于你),将EventEmitter
放入ViewModel
,然后订阅/取消订阅这个EventEmitter在你的 Activity 中。
public class MyViewModel: ViewModel()
private final EventEmitter<String> testFullUrlReachableEmitter = new EventEmitter<>();
public final EventSource<String> getTestFullUrlReachable()
return testFullUrlReachableEmitter;
public void checkReachable()
new testFullURLreachable().execute()
private class testFullURLreachable extends AsyncTask<Void, Void, String>
...
@Override
public void onPostExecute(String result)
testFullUrlReachableEmitter.emit(result);
在你的活动/片段中
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// ...
private EventSource.NotificationToken subscription;
@Override
protected void onStart()
super.onStart();
subscription = viewModel.getTestFullUrlReachable().startListening((result) ->
// do `onPostExecute` things here
);
@Override
protected void onStop()
super.onStop();
if(subscription != null)
subscription.stopListening();
subscription = null;
【讨论】:
哇....有很多东西要吸收!我需要一段时间才能理解它。谢谢。 @EpicPandaForce 顺便说一句,我并没有尝试在 doInBackground() 方法中弹出 toast 或访问字符串资源,仅在 onPostExwcute() 方法中。 OK..为此苦苦挣扎。ViewModelProviders
无法解决。 onStart()
代码包含一个我以前没有使用过的 lambda - 另一个可以深入了解这一点的兔子洞。另外,当我添加它时,ViewModel 的东西也不满意(ViewModel 上的Invalid method declaration; return type required.
)。
我还更新了原始问题,因为第一个代码示例显示 WeakReference 实际上来自错误的方法(这也让我在理解这个解决方案时有些困惑,因为我必须翻译方法名称 - 哦!)以上是关于从 AsyncTask 返回数据到 MainActivity 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
如何将变量从 AsyncTask 返回到 Android 中的 Google Map 活动