有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java从AsyncTask向MainActivity返回数据的最佳方式

我在应用程序中使用ASyncTask,通过REST API从web服务(Bitly)获取一些数据(一个短URL)

ASyncTask完成时,我想将结果传递回我的MainActivity

通过使用AsyncTaskonPostExecute方法,可以将数据返回到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调用该方法: 缩短进度条。setUndeterminate(假); 缩短进度条。设置可见性(视图不可见)

    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随后发出警告,“类应该是静态的,否则可能会发生泄漏”

如果我将该方法设置为静态,则会收到一条警告,即我想从MainActivity调用的onPostExecute方法不能被调用,因为它是非静态的

如果我把MainActivity中的那个方法变成一个静态方法,那么它就不能访问字符串资源和任何其他非静态的方法了——我要去兔子洞

如果我将代码从MainActivity中的方法移到onPostExecute方法中,正如您所预料的那样,情况也是如此。 所以

  • 将AsyncTask作为非静态方法真的是件坏事吗?(我的 应用程序似乎可以在AS中使用此警告,但我显然不行 想在我的应用程序中创建内存泄漏
  • 实际上,{}方法是更正确、更安全的方法吗
  • 如果我使用WeakReference方法,如何创建需要在UI线程和访问字符串上运行的toasts之类的东西 来自MainActivity的资源等

我在某个地方读到了关于创建interface的内容,但有点迷路了,再也找不到了。这难道不会像WeakReference那样依赖MainActivity吗?这是一件坏事吗

我真的在寻找关于如何从一个安全且不存在内存泄漏风险的AsyncTask将一些数据返回到MainActivity和UI线程的最佳实践指导


共 (1) 个答案

  1. # 1 楼答案

    Is having an AsyncTask as a non-static method really a bad thing? (My app seems to work fine with this warning in AS, but I obviously don't want to be creating a memory leak in my app.

    是的,你的观点和背景会泄露出去

    旋转足够多,你的应用程序就会崩溃

    Is the WeakReference approach actually a more correct and safer approach?

    这是一只死猪上的唇膏,这种情况下的弱引用比一个解决方案更像是一个破解,绝对不是 EME>正确的解决方案。

    你要寻找的是某个事件总线的一种形式,它比活动时间更长

    您可以使用保留片段*或Android架构组件ViewModel

    您可能需要引入观察者模式(但不一定是LiveData)

    If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string resources etc from the MainActivity?

    不要在^{中运行那种东西

    I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.

    最简单的方法是使用this library(或者自己写一些做同样事情的东西,由你决定),把EventEmitter放到ViewModel中,然后在你的活动中订阅/取消订阅这个EventEmitter

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