Android Context without being in an activity? And

2020-06-08 14:09发布

I'll try really hard to turn this into one comprehensive question:

I'm writing a method to get a String that contains the name of an Android device's city, as determined by the LocationManager and getLastKnownLocation() and all that.

Then I realized I'd need to do the same thing again in another activity, so why not just make an entirely separate class (LocationFinder) that I could use across my program, instead of writing duplicate code everywhere?

But I've run into problems that confuses me. For instance, if I make this class (LocationFinder), should it extend Activity, even though it is never actually visualized? All this class would do is have a variety of getters like getLastKnownCity() or getCurrentCity() and return strings. I assumed it wouldn't HAVE to extend the Activity class, since it's really not an activity.

But then what Context do I use for:

Geocoder geocoder = new Geocoder(Context context, Locale locale)

?

This made me assume it MUST be an activity. So I extended Activity, and replaced the constructor with

@Override
protected void onCreate(..............

but for some reason, that never ends up getting called, even when I put

String city = new LocationFinder().getLastKnownCity();

My very first line of LocationFinder's onCreate() is

System.out.println("HEY!")

and it never even gets to that. I get a null pointer at android.internal.os.LoggingPrintStream.println() and other stuff.

Plus, there's a bunch of system constants that come from Activity classes. For instance, I need to get at LOCATION_SERVICE, which is a String, which I can't get without extending Activity. Sure, I could cheat and just put in the literal string, but that feels wrong.

3条回答
Luminary・发光体
2楼-- · 2020-06-08 14:32
should it extend Activity, even though it is never actually visualized?

No. From the Android docs

An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View)

Think of an Activity as a screen the user sees.

But then what Context do I use for Geocoder geocoder = new Geocoder(Context context, Locale locale)

The Activity class extends Context, as do a lot of other classes including Application. A context provides access to resources associated with the class which extends the context.

You only need, and should only use, an Activity context when required to interact with resources associated with that Activity and methods implemented by the concrete Activity class. When you do need that access to that context, then you would pass it to the class needing access, typically as an argument to a constructor of that class.

If you ever do pass an Activity context outside of the activity extending it, make sure that the scope and lifecycle of the reference is less than or equal to the extending activity otherwise you will leak large amounts of memory if the activity is destroyed since the garbage collector cannot free the memory since there is a reference to the context.

If you take a look at the constructor for Geocoder you will see that it takes a Context as an argument, as you know. There is a clue as to why the Context is needed in the description:

     Geocoder(Context context, Locale locale)
Constructs a Geocoder whose responses will be localized for the given Locale.  [1]: 

The reason the Context is required is to gain access to system information about the platform locales and the current system locale.

So in your example, you could simply pass the Application context to the constructor, which you can get a reference to with getApplicationContext()

For instance, I need to get at LOCATION_SERVICE, which is a String, which I can't get without extending Activity

You can get it from the application context.

查看更多
我只想做你的唯一
3楼-- · 2020-06-08 14:35

When constructing your class, you can have a constructor that takes in a Context and assigns it a local Context object within your class.

public class LocationFinder {
     private Context myContext;
     private Geocoder geocoder;

     public LocationFinder(Context context)
     {
         myContext = context;
         geocoder = new Geocoder(myContext);
     }

}

And then when you try to access this class, make sure you initialise it like:

public class TestActivity extends Activity {
     protected void onCreate(Bundle savedInstanceState)
     {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
          LocationFinder lFinder = new LocationFinder(getApplication());
     }
}

Of course, you can't access a context from every class that you will be running. So a reference to a View can suffice.

LocationFinder lFinder = new LocationFinder(anyView.getApplication());
查看更多
够拽才男人
4楼-- · 2020-06-08 14:35

EDIT: If possible, use frogmanx's answer. This should only be used when his answer is not possible to use. (ie. singletons that need a context right off the bat.)

Sounds like you should extend Application and not Activity.

Make your Application something like this:

public class MyApplication extends Application {
    private static MyApplication instance;

    public MyApplication() {
        instance = this;
    }

    public static MyApplication getInstance() {
         return instance;
    }

Then add this attribute to the application tag of the manifest:

 <application android:name=".your.package.MyApplication" ... />

After all that, you can get a Context by calling MyApplication.getInstance() from anywhere.

查看更多
登录 后发表回答