简体   繁体   English

如何判断我的上下文是否仍然有效?

[英]How can I tell if my context is still valid?

I'm working with a fairly common situation right now - download some data over the web, then update a view to display it.我现在正在处理一个相当普遍的情况 - 通过网络下载一些数据,然后更新视图以显示它。 Clearly, I want to do the web download in the background, and then update the view on the main UI thread.显然,我想在后台进行网络下载,然后在主 UI 线程上更新视图。 Now looking at my code, I'm a little worried about my Activity and its UI elements being killed off before I update them.现在查看我的代码,我有点担心我的 Activity 及其 UI 元素在我更新它们之前被杀死。 Here's the essence of what I have in mind:这是我想到的本质:

Thread update = new Thread() {
    public void run() {
        final Data newData = requestData();                     
        if (newData != null) {
            post(new Runnable() {
                public void run() {
                    Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
};
update.start();

It seems possible that while I'm downloading data, the activity may be destroyed.似乎有可能在我下载数据时,活动可能会被破坏。 What happens then?那会发生什么? Will my thread continue to execute?我的线程会继续执行吗? Will I end up trying to access dead objects?我最终会尝试访问死对象吗?

Usually I do this by AsycTask, but the work seemed simple enough this time to just inline the threads-launching-threads stuff.通常我通过 AsycTask 来做这件事,但这次的工作似乎很简单,只需内联线程启动线程的东西。 Will I make things any better by using an AsyncTask instead?我会通过使用 AsyncTask 使事情变得更好吗?

If your Context is an Activity , you can check if it is finishing or has finished with the isFinishing() method:如果您的Context是一个Activity ,您可以使用isFinishing()方法检查它是否正在完成或已完成:

if ( context instanceof Activity ) {
    Activity activity = (Activity)context;
    if ( activity.isFinishing() ) {
        return;
    }
}
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();

What you really want to use is an AsyncTaskLoader .您真正想要使用的是AsyncTaskLoader These are my new favorite classes in the Android API.这些是我在 Android API 中最喜欢的新类。 I use them all the time and they were made to solve problems just like this.我一直在使用它们,它们就是为了解决这样的问题。 You won't have to worry about when to stop your download or anything like that.您不必担心何时停止下载或类似的事情。 All the threading logic is taken care of for you, including telling the thread to stop if the activity has been closed.所有线程逻辑都会为您处理,包括告诉线程在活动已关闭时停止。 Just say what it is you want to do in the loadInBackground() method.只需在loadInBackground()方法中说出您想做什么。 Note that if you are developing for an API lower than 3.0, you can still access all the loaders via the Android Support Package .请注意,如果您正在为低于 3.0 的 API 进行开发,您仍然可以通过Android 支持包访问所有加载器。

If you use anonymous classes, they will have an internal reference to the outer class, so it's not like it becomes inaccessible all of a sudden because other references have been cleared.如果您使用匿名类,它们将具有对外部类的内部引用,因此它不会因为其他引用已被清除而突然变得不可访问。 AsyncTask actually doesn't change anything, it uses similar mechanics for notifying about results. AsyncTask实际上不会改变任何东西,它使用类似的机制来通知结果。

You can use loaders , they are designed to be in sync with the activity lifecycle.您可以使用loader ,它们旨在与活动生命周期同步。 They are available only since Android 3.0, but you can use support package to work with them on any device with 1.6 or later.它们仅在 Android 3.0 之后可用,但您可以使用支持包在任何 1.6 或更高版本的设备上使用它们。

There is even a simpler solution, you can just use a boolean field which indicates whether activity has gone away.还有一个更简单的解决方案,您可以只使用一个布尔字段来指示活动是否已经消失。 You should set this field in onPause() (or whenever you think you won't need the notifications anymore) and check for it when you show toast.你应该在onPause()设置这个字段(或者当你认为你不再需要通知时)并在你显示 toast 时检查它。 You won't even have to use synchronization, since this field is confined to the main thread, so it's absolutely safe.您甚至不必使用同步,因为该字段仅限于主线程,因此绝对安全。 By the way, if you change this field somewhere else than in onDestroy() , don't forget to add a statement which resets your field back in the counterpart method.顺便说一句,如果您在onDestroy()以外的其他地方更改此字段,请不要忘记添加一条语句,将您的字段重置回对应方法。

public class MyActivity extends Activity {
    private boolean activityDestroyed = false;

    @Override
    protected void onDestroy() {
        activityDestroyed = true;
    }

    private void updateData() {
        new Thread() {
            @Override
            public void run() {
                final Data newData = requestData();                     
                if (newData == null) return;                                              

                runOnUiThread(new Runnable() {
                    public void run() {
                        if (activityDestroyed) return;
                        Toast.makeText(MyActivity.this, "Blah",
                                Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }.start();
    }
}

I usually use Weak Reference to avoid leaking context in views我通常使用Weak Reference来避免在视图中泄漏上下文

Weak Reference for Context上下文的弱引用

private var mContext: WeakReference<Context?>? = null

Assign Context分配上下文

mContext = WeakReference(appContext)

Get Context获取上下文

mContext .get()

Validate Context验证上下文

  if (mContext?.get() is Activity &&
        (mContext?.get()  as Activity).isFinishing){
            return
    }

Kurtis is right.库蒂斯是对的。 However, if you REALLY want to keep it simple, you can try this:但是,如果你真的想保持简单,你可以试试这个:

class MyActivity extends Activity {
    static MyActivity context;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        MyActivity.context = this;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        MyActivity.context = null;
    }
}

And then you just use MyActivity.context in your class (and check for null there).然后你只需在你的类中使用MyActivity.context (并在那里检查 null )。 If you want the toast to not even show up when your app is in the background, use onPause/onResume instead.如果您希望在您的应用程序处于后台时甚至不显示 Toast,请改用 onPause/onResume。

Again, this is the quick and lazy approach.同样,这是快速而懒惰的方法。 AsyncTask or AsyncTaskLoader is how you should be doing things. AsyncTask 或 AsyncTaskLoader 是您应该如何做事。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM