How should ActivityRetainedLifecycle.OnClearedListener be used? - android

I'm injecting an #ActivityRetainedScoped component via Hilt. The component is registering a listener so I want to make sure it cleans up after itself and doesn't leak anything.
I have seen ActivityRetainedLifecycle.OnClearedListener in the JavaDocs for Hilt but haven't seen any examples of how to use it.
E.g. using WifiManager as an example, at the moment I'm doing this:
#ActivityRetainedScoped
public class Wifi {
#Inject
public Wifi(
#NonNull final Application application,
#NonNull final ActivityRetainedLifecycle activityRetainedLifecycle,
#NonNull final WifiManager wifiManager
) {
final Context applicationContext = application.getApplicationContext();
final IntentFilter intentFilter = /* Init intentFilter */
final WifiScanReceiver wifiScanReceiver = /* Init wifiScanReceiver */
applicationContext.registerReceiver(wifiScanReceiver, intentFilter);
activityRetainedLifecycle.addOnClearedListener(() -> {
applicationContext.unregisterReceiver(wifiScanReceiver);
});
}
}
It feels self-explanatory but I've been burned by assuming stuff like this before and can't find much online to validate my assumption on it.
Is that the correct way to 'tear down' an activity-retained component that has external dependencies to make sure it doesn't leak?

Yes, according to the documentation.
And Thanks! I found it thanks to your question.
Interestingly it also explains the mechanics of it
Specifically this will be invoked during Activity.onDestroy() when Activity.isChangingConfigurations() is false.

Related

What is the point of making RxBleClient non-static?

In the RxAndroidBle sample application, the Application class is:
public class SampleApplication extends Application
private RxBleClient rxBleClient;
/**
* In practise you will use some kind of dependency injection pattern.
*/
public static RxBleClient getRxBleClient(Context context) {
SampleApplication application = (SampleApplication) context.getApplicationContext();
return application.rxBleClient;
}
#Override
public void onCreate() {
super.onCreate();
rxBleClient = RxBleClient.create(this);
RxBleClient.setLogLevel(RxBleLog.DEBUG);
}
}
Since we are guaranteed that there will only be one instance of Application, and we only want one instance of RxBleClient, isn't it simpler to write:
public class SampleApplication extends Application {
private static RxBleClient rxBleClient;
public static RxBleClient getRxBleClient() {
return rxBleClient;
}
#Override
public void onCreate() {
super.onCreate();
rxBleClient = RxBleClient.create(this);
RxBleClient.setLogLevel(RxBleLog.DEBUG);
}
}
What is the benefit of the more complex approach?
And what is meant by "some kind of dependency injection pattern"? Could we see an example? Again, what is the benefit?
As Marcos Vasconcelos said, the first approach could be slightly easier to test though it is not a good design anyway.
Dependency injection is a way to obtain a testable code which one could throughly test by (for instance) providing (mocked) dependencies at the construction time.
To see examples of dependency injection you can go to the sources of RxAndroidBle and check out how the classes are designed.
An example could be RxBleConnectionImpl class which has quite a lot of dependencies that are injected. Thanks to that it was possible to create extensive test suite for it.
RxAndroidBle sample application was written as a sample that is easy to comprehend. It does not necessarily follows the best patterns as these are not always easy to follow. The main goal of the sample is to show the usage of the library API.

Unit Testing a BroadcastReceiver

I've been trying to figure out how to unit test my BroadcastReceiver and I have looked at StackOverflow and other websites but I can't find the solution to my problem.
In my mainActivity I have the following two functions:
private void registerNetRcvr(){
if (!isRcvrRegistered) {
isRcvrRegistered = true;
registerReceiver(receiver, new IntentFilter("android.net.wifi.STATE_CHANGE"));
registerReceiver(receiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
}
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
NetworkHandler.NetworkInfo netInfo = NetworkHandler.handleBcastReceiver(context);
if (netInfo!=null){
handleInfoChange(netInfo);
} else {
handleInfoChange(null);
}
}
};
The registerNetRcvr is called from within the onResume function (and equally I have an unregister called from onPause).
As can be seen from the above, I have a function (handleBcastReceiver) that is called to handle the onReceive event and thus have another class that then has a variety of private functions which are called from this function.
Just to slightly complicate matters... upon the onReceive function being called, as detailed above the 'handleBcastReceiver' function would then need to 'retreive' the correct data... and, as such would make a call to actually retrieve the appropriate system data as follows:
private static NetworkInfo getWiFiNetworkInfo(Context context) {
ConnectivityManager connManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
return connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
}
I believe that I should emulate the behaviour of the onReceive callback but also I guess emulate what would be returned from the system during the call to getWiFiNetworkInfo.
My thinking is that I should be using RoboElectric to do this as it can shadow the BroadcastReceiver/ConnectivityManager. I've also looked at Mockito however I believe that RoboElectric is what I require for this but I accept that I could well be wrong. As far as I'm aware, RoboElectric allows me to emulate 'the Android system' and Mockito would allow me to mock my own classes.
At present I just can't see how to test the BroadcastReceiver. Additionally I'm not clear upon whether I should be running the emulator to do this or simply 'run' my unit test as the 'shadow' should contain everything I need. Finally, if I was to shadow the BroadcastReceiver, how do I get WIFI to be 'enabled' or 'disabled' through this shadow process? In fact, should I be emulating a BroadcastReceiver or just the ConnectivityManager... I'm genuinely confused!
What I have done so far is to create a BroadcastReceiverTest class within the test section of my app (not the 'androidTest' section). I can do normal simple unit tests on functions, I'm just stuck with how to emulate this 'system' behaviour.
As always, any help is greatly appreciated.
So, I think I've solved it... :) If anyone with more experience wishes to give me feedback on this, it'd be much appreciated.
I feel that I don't need to care about the 'broadcast receiver' more so I need to be bothered with shadowing the ConnectivityManager as this is what the 'handleBcastReceiver' function will ultimately read from. So, really that is what is important as it's the 'feed' into my functions which drives how they behave. When the onReceive function is triggered, all it will do is 'handleBcastReceiver' function and then update the UI with the result, and hence it's not what I really need to test here.
I created the following and it's working for me. If I modify my test data, tests will fail as appropriate so I believe that this is satisfactory. I hope others may find it useful and as I mentioned, I'm very happy to receive feedback on whether what I've done is or isn't correct.
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 23, manifest = "/src/main/AndroidManifest.xml")
public class NetworkStateHandlerUnitTest {
private static String NETWORK_TYPE_HSDPA = "HSDPA";
private ConnectivityManager connectivityManager;
private ShadowConnectivityManager shadowConnectivityManager;
private Context context;
#Before
public void setUp() {
}
#Test
public void handleBroadcastReceiverWIFIConnected() {
context = RuntimeEnvironment.application.getApplicationContext();
connectivityManager = getConnectivityManager();
shadowConnectivityManager = Shadows.shadowOf(connectivityManager);
//Create shadow Network Info - Connected, WIFI, No Subtype, available, connected
NetworkInfo networkInfoShadow = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.CONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, true);
shadowConnectivityManager.setNetworkInfo(ConnectivityManager.TYPE_WIFI, networkInfoShadow);
NetworkHandler.NetworkInfo networkInfoResponse;
//Call the function under test
networkInfoResponse = NetworkHandler.handleBcastReceiver(context);
//Validate the response
assertEquals(networkInfoResponse.getNetworkType(), ConnectivityManager.TYPE_WIFI);
}
private ConnectivityManager getConnectivityManager() {
return (ConnectivityManager) RuntimeEnvironment.application.getSystemService(context.CONNECTIVITY_SERVICE);
}
}
I do, of course have more than just this one test but felt this would be enough to help anyone else who finds themselves similarly stuck with this :)
Pleased to say that eventually I managed to test the BroadcastReceiver listener as well... in case you want to know how to do this...
#Test
public void handleBrcastRcvrWifiConn() {
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
TextView tvConnStatus = (TextView) activity.findViewById(R.id.tvConnectionState);
context = RuntimeEnvironment.application.getApplicationContext();
connectivityManager = getConnectivityManager();
shadowConnManager = Shadows.shadowOf(connectivityManager);
//Create shadow Network Info - Connected, WIFI, No Subtype, available, connected
NetworkInfo netInfoShadow = ShadowNetworkInfo.newInstance(NetworkInfo.DetailedState.CONNECTED,
ConnectivityManager.TYPE_WIFI, 0, true, true);
shadowConnManager.setNetworkInfo(ConnectivityManager.TYPE_WIFI, networkInfoShadow);
//Trigger BroadcastReceiver
RuntimeEnvironment.application.sendBroadcast(new Intent("android.net.wifi.STATE_CHANGE"));
//Validate result by checking value of textView
assertEquals("Connected", tvConnectionState.getText());
}

unit test a service based on BroadcastReceiver getting list of wifi's SSID and returning Rx.Observable

I am trying to test unit my ServiceHelper class that is providing a list of Wifi's SSID to show in my view.
So I am providing an observable using RxJava + RxAndroid. As you can see I have chosen the Observable.create() to create from native my observable (Arraylist of Items) witch is depending on a BroadcastReciever inner class.
I need to test unit this method. I don't have a good idea about how to test or mock this kind of service that does depend on Sensors.
public class WifiPhoneServiceHelper {
private Context context;
private ArrayList<String> ssidList = new ArrayList<>();
private List<ScanResult> scanResultList;
private WifiManager wifiManager;
public WifiPhoneServiceHelper(Context context) {
this.context = context;
}
public Observable<ArrayList<String>> getObservableSsidWifiList() {
return Observable.create(new Observable.OnSubscribe<ArrayList<String>>() {
#Override
public void call(final Subscriber<? super ArrayList<String>> subscriber) {
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
scanResultList = wifiManager.getScanResults();
for (int i = 0; i < scanResultList.size(); i++) {
String ssidDetected = scanResultList.get(i).SSID;
ssidList.add(ssidDetected);
}
subscriber.onNext(ssidList);
}
}
};
context.registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
if (!wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(true);
}
wifiManager.startScan();
subscriber.add(new MainThreadSubscription() {
#Override
protected void onUnsubscribe() {
context.unregisterReceiver(receiver);
Timber.d("unregisterReceiver BroadcastReceiver SCAN_RESULTS_AVAILABLE_ACTION");
if (!subscriber.isUnsubscribed()) {
subscriber.unsubscribe();
}
}
});
}
});
}
In order to unit test your code you need to description of all functionality into separate interface and then implement it for Android and create mock implementation for unit tests (Mockito).
Alex effectively suggests that you create an interface between your code and the Wi-Fi system, and in production code have it implemented by Android's WifiManager, but in testing, implemented by your own test harness.
You could certainly do that but another option is Robolectric. This is an Android unit test framework that can be used to mock large parts of the Android system without doing as much groundwork as you would with Mockito. Specifically it can mock the Wi-Fi system and therefore you can have it pretend that a given network exists or doesn't. The advantages to this are you can largely leave your Wi-Fi using code as it is, without heavy refactoring for test, but you still don't need a real device and it's not dependent on the real world's networking environment.
In particular look for examples based around ShadowWifiManager. The other half of the puzzle is producing event broadcasts to stimulate your code, but this should also be feasible.
It's beyond the scope of this answer to fully describe the establishment of a Robolectric test environment, but hopefully that gives you something to begin researching around.

What version of RoboGuice works with Android SDK 8?

That's probably part one of my question.
Basically I'm struggling with the actual injection for version 1.1.2. I've read the couple of pages on the site, and I feel I'm missing something.
Basically I've done the RoboApplication extension. I've overridden the addApplicationModules method. I've even made a module.
My module looks like this:
public class DataRepository extends AbstractAndroidModule
{
#Override
protected void configure() {
/*
* This tells Guice that whenever it sees a dependency on a TransactionLog,
* it should satisfy the dependency using a DatabaseTransactionLog.
*/
bind(IDataBaseAdapter.class).to(DataBaseAdapter.class);
}
}
In my adapter I have this:
public class DataBaseAdapter implements IDataBaseAdapter
{
private DataBaseHelper _dbHelper;
private SQLiteDatabase _db;
#Inject
protected static Provider<Context> contextProvider;
public DataBaseAdapter()
{
_dbHelper = new DataBaseHelper(contextProvider.get());
}
}
If I don't do there, where is the opportune place for the chunk of code to reside... where I associate injectors?
Finally... my Application has an injection of it like so:
public class MyApplication extends RoboApplication
{
public MyApplication()
{
super();
}
public MyApplication(Context context)
{
super();
attachBaseContext(context);
}
#Override
protected void addApplicationModules(List<Module> modules)
{
modules.add(new DataRepository());
}
#Inject
private IDataBaseAdapter adapter;
public IDataBaseAdapter getAdapter()
{
return adapter;
}
public void setAdapter(IDataBaseAdapter value)
{
adapter = value;
}
...
}
I'm trying to use the Inject attribute as shown. For example:
#Inject
private IDataProvider provider;
A couple of reasons why I'm lost is that I come from a .NET and Flash/ActionScript background plus I've only used StructureMap instead of Ninject (in the .NET world), which I've heard Guice is designed with some of the ideas of Ninject in mind. Could someone help me figure out this small piece?
I'd really like to focus on using 1.1.2 instead of jumping to 2.x of RoboGuice... especially since it is still in beta, so I hope you all don't mind.
Thanks again,
Kelly
Android is quite different from standalone / hosted java application. You do not have main() , but you have certain activity units, which are managed by android framework (activities, services , broadcast receivers)
DI is a technique which allows you to eliminate booler plate code by wiring together
parts in good object oriented way.
As your unit of work is mostly activity, you shall do wiring / creation of your collaborating objects in onCreate() method , and there are dedicated onResume() and onPause() methods (see actviity lifecycle)
Rule of thumb is, does this thing needs to be restarted every time activity loses it focus? If yes, initialize / destroy it in inResume() / onPause(), otherwise - in onCreate()
And if you like to share objects withing entire application ( running in same JVM ) , it is OK to use singleton pattern in android. So you may just have singleton injector factory , and cosult it from everywhere:
InjectorFactory.getInstance(<context if necessary?>).getInstance(whatever you need);
OK, I've figured out what was needed, but I'm not quite sure why after seeing all the information floating out there.
I basically made this change, and now my test passes.
public class DataBaseAdapter implements IDataBaseAdapter
{
private DataBaseHelper _dbHelper;
private SQLiteDatabase _db;
#Inject
public DataBaseAdapter(Provider<Context> contextProvider)
{
_dbHelper = new DataBaseHelper(contextProvider.get());
}
}
While I like using constructors as the tool for injecting, I wonder why it had to work this way, considering that examples I have seen are some kind of reflection class injection.
Anyway, that's this part. Hopefully someone else will find this useful.
Cheers,
Kelly

What's the difference between the various methods to get an Android Context?

In various bits of Android code I've seen:
public class MyActivity extends Activity {
public void method() {
mContext = this; // since Activity extends Context
mContext = getApplicationContext();
mContext = getBaseContext();
}
}
However I can't find any decent explanation of which is preferable, and under what circumstances which should be used.
Pointers to documentation on this, and guidance about what might break if the wrong one is chosen, would be much appreciated.
I agree that documentation is sparse when it comes to Contexts in Android, but you can piece together a few facts from various sources.
This blog post on the official Google Android developers blog was written mostly to help address memory leaks, but provides some good information about contexts as well:
In a regular Android application, you
usually have two kinds of Context,
Activity and Application.
Reading the article a little bit further tells about the difference between the two and when you might want to consider using the application Context (Activity.getApplicationContext()) rather than using the Activity context this). Basically the Application context is associated with the Application and will always be the same throughout the life cycle of your app, where as the Activity context is associated with the activity and could possibly be destroyed many times as the activity is destroyed during screen orientation changes and such.
I couldn't find really anything about when to use getBaseContext() other than a post from Dianne Hackborn, one of the Google engineers working on the Android SDK:
Don't use getBaseContext(), just use
the Context you have.
That was from a post on the android-developers newsgroup, you may want to consider asking your question there as well, because a handful of the people working on Android actual monitor that newsgroup and answer questions.
So overall it seems preferable to use the global application context when possible.
Here's what I've found regarding the use of context:
1) . Within an Activity itself, use this for inflating layouts and menus, register context menus, instantiating widgets, start other activities, create new Intent within an Activity, instantiating preferences, or other methods available in an Activity.
Inflate layout:
View mView = this.getLayoutInflater().inflate(R.layout.myLayout, myViewGroup);
Inflate menu:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
this.getMenuInflater().inflate(R.menu.mymenu, menu);
return true;
}
Register context menu:
this.registerForContextMenu(myView);
Instantiate widget:
TextView myTextView = (TextView) this.findViewById(R.id.myTextView);
Start an Activity:
Intent mIntent = new Intent(this, MyActivity.class);
this.startActivity(mIntent);
Instantiate preferences:
SharedPreferences mSharedPreferences = this.getPreferenceManager().getSharedPreferences();
2) . For application-wide class, use getApplicationContext() as this context exist for the lifespan of the application.
Retrieve the name of the current Android package:
public class MyApplication extends Application {
public static String getPackageName() {
String packageName = null;
try {
PackageInfo mPackageInfo = getApplicationContext().getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), 0);
packageName = mPackageInfo.packageName;
} catch (NameNotFoundException e) {
// Log error here.
}
return packageName;
}
}
Bind an application-wide class:
Intent mIntent = new Intent(this, MyPersistent.class);
MyServiceConnection mServiceConnection = new MyServiceConnection();
if (mServiceConnection != null) {
getApplicationContext().bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
3) . For Listeners and other type of Android classes (e.g. ContentObserver), use a Context substitution like:
mContext = this; // Example 1
mContext = context; // Example 2
where this or context is the context of a class (Activity, etc).
Activity context substitution:
public class MyActivity extends Activity {
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
}
Listener context substitution:
public class MyLocationListener implements LocationListener {
private Context mContext;
public MyLocationListener(Context context) {
mContext = context;
}
}
ContentObserver context substitution:
public class MyContentObserver extends ContentObserver {
private Context mContext;
public MyContentObserver(Handler handler, Context context) {
super(handler);
mContext = context;
}
}
4) . For BroadcastReceiver (including inlined/embedded receiver), use the receiver's own context.
External BroadcastReceiver:
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
sendReceiverAction(context, true);
}
private static void sendReceiverAction(Context context, boolean state) {
Intent mIntent = new Intent(context.getClass().getName() + "." + context.getString(R.string.receiver_action));
mIntent.putExtra("extra", state);
context.sendBroadcast(mIntent, null);
}
}
}
Inlined/Embedded BroadcastReceiver:
public class MyActivity extends Activity {
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final boolean connected = intent.getBooleanExtra(context.getString(R.string.connected), false);
if (connected) {
// Do something.
}
}
};
}
5) . For Services, use the service's own context.
public class MyService extends Service {
private BroadcastReceiver mBroadcastReceiver;
#Override
public void onCreate() {
super.onCreate();
registerReceiver();
}
private void registerReceiver() {
IntentFilter mIntentFilter = new IntentFilter();
mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
this.mBroadcastReceiver = new MyBroadcastReceiver();
this.registerReceiver(this.mBroadcastReceiver, mIntentFilter);
}
}
6) . For Toasts, generally use getApplicationContext(), but where possible, use the context passed from an Activity, Service, etc.
Use context of the application:
Toast mToast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG);
mToast.show();
Use context passed from a source:
public static void showLongToast(Context context, String message) {
if (context != null && message != null) {
Toast mToast = Toast.makeText(context, message, Toast.LENGTH_LONG);
mToast.show();
}
}
And last, don't use getBaseContext() as advised by Android's framework developers.
UPDATE: Add examples of Context usage.
I read this thread a few days ago, asking myself the same question. My decision after reading this was simple: always use applicationContext.
However, I encountered a problem with this, I spent a few hours to find it, and a few seconds to solve it... (changing one word...)
I am using a LayoutInflater to inflate a view containing a Spinner.
So here are two possibilities:
1)
LayoutInflater layoutInflater = LayoutInflater.from(this.getApplicationContext());
2)
LayoutInflater layoutInflater = LayoutInflater.from(this.getBaseContext());
Then, I am doing something like this:
// managing views part
View view = ContactViewer.mLayoutInflater.inflate(R.layout.aViewContainingASpinner, theParentView, false);
Spinner spinner = (Spinner) view.findViewById(R.id.theSpinnerId);
String[] myStringArray = new String[] {"sweet","love"};
// managing adapter part
// The context used here don't have any importance -- both work.
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this.getApplicationContext(), myStringArray, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
theParentView.addView(view);
What I noticed: If you instantiated your linearLayout with the applicationContext, then when you click on the spinner in your activity, you will have an uncaught exception, coming from the dalvik virtual machine (not from your code, that's why I have spent a lot of time to find where was my mistake...).
If you use the baseContext, then that's all right, the context menu will open and you will be able to choose among your choices.
So here is my conclusion: I suppose (I have not tested it further) than the baseContext is required when dealing with contextMenu in your Activity...
The test has been done coding with API 8, and tested on an HTC Desire, android 2.3.3.
I hope my comment have not bored you so far, and wish you all the best. Happy coding ;-)
First, I agree that we should use appcontext whenever possible. then "this" in activity. i've never had a need for basecontext.
In my tests, in most cases they can be interchanged. In most cases, the reason you want to get a hold of a context is to access files, preferences, database etc. These data is eventually reflected as files in your app's private data folder (/data/data/). No matter which context you use, they'll be mapped to the same folder/files so you are OK.
That's what I observed. Maybe there are cases you should distinguish them.
In some cases you may use Activity context over application context when running something in a thread. When thread completes execution and you need to return the result back to the caller activity, you need that context with a handler.
((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
In simple words
getApplicationContext() as the method name suggest will make your app aware of application wide details which you can access from anywhere in the app. So you can make use of this in service binding, broadcast registration etc. Application context will be alive till the app exits.
getActivity() or this will make your app aware of the current screen which is visible also the app level details provided by application context. So whatever you want to know about the current screen like Window ActionBar Fragementmanger and so are available with this context. Basically and Activity extend Context. This context will alive till the current component(activity) is alive
The confusion stems from the fact that there are numerous ways to
access Context, with (on the surface) no discernible differences.
Below are four of the most common ways you may be able to access
Context in an Activity.
getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new
What is a Context?
I personally like to think of Context as the state of your application at any given time. The application Context represents a global or base configuration of your application and an Activity or Service can build upon it and represents a configuration instance of your Application or a transitive state for it.
If you look at the source for android.content.Context, you see that Context is an abstract class and the comments on the class are as follows:
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It
allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.
What I take away from this is that Context provides a common implementation to access application level as well as system level resources. Application level resources may be accessing things like String resources [getResources()] or assets [getAssets()] and system-level resource is anything that you access with Context.getSystemService().
As a matter of fact, take a look at the comments on the methods and they seem to reinforce this notion:
getSystemService(): Return the handle to a system-level service by name. The class of the returned object varies by the requested name.
getResources(): Return a Resources instance for your application’s package.
getAssets(): Return a Resources instance for your application’s package.
It may be worth pointing out that in the Context abstract class, all of the above methods are abstract! Only one instance of getSystemService(Class) has an implementation and that invokes an abstract method. This means, the implementation for these should be provided mostly by the implementing classes, which include:
ContextWrapper
Application
Activity
Service
IntentService
Looking at the API documentation, the hierarchy of the classes looks like this:
Context
| — ContextWrapper
|— — Application
| — — ContextThemeWrapper
|— — — — Activity
| — — Service
|— — — IntentService
Since we know that Context itself is not providing any insight, we move down the tree and take a look at the ContextWrapper and realize that there isn't much there either. Since Application extends ContextWrapper, there isn't much to look at over there either since it doesn't override the implementation provided by ContextWrapper. This means that the implementation for Context is provided by the OS and is hidden from the API. You can take a look at the concrete implementation for Context by looking at the source for the ContextImpl class.
I've only used this and getBaseContext when toasting from an onClick (very green noob to both Java and android). I use this when my clicker is directly in the activity and have to use getBaseContext in an anonymous inner clicker. I'm guessing that is pretty much the trick with getBaseContext, it is perhaps returning the context of the activity in which the inner class is hiding.

Categories

Resources