How can I call getContentResolver in android? - android

I'm writing a library class to encapsulate some of my logic in my first Android app. One of the functions which I want to encapsulate is a function which queries the address book. As such, it needs a ContentResolver. I'm trying to figure out how to keep the library functions black-boxed... that is, to avoid having each Activity pass in its own context to get a ContentResolver.
Problem is I cannot for the life of me figure out how to get a ContentResolver from within my library function. I can't find an import that contains getContentResolver. Googling said to use getContext to get a Context on which to call getContentResolver, but I can't find an import containing getContext either. Next posts said to use getSystemService to get an object to call getContext. BUT - I can't find any import containing getSystemService either!
So I'm stuck wondering, how can I get a ContentResolver within an encapsulated library function, or am I pretty much stuck having every calling Activity pass in a reference to its own context?
My code is something basically like this:
public final class MyLibrary {
private MyLibrary() { }
// take MyGroupItem as a class representing a projection
// containing information from the address book groups
public static ArrayList<MyGroupItem> getGroups() {
// do work here that would access the contacts
// thus requiring the ContentResolver
}
}
getGroups is the method where I was looking to avoid having to pass in a Context or ContentResolver if I could, as I was hoping to have it cleanly black-boxed.

You can use like this:
getApplicationContext().getContentResolver() with the proper context.
getActivity().getContentResolver() with the proper context.

Have each library function call pass in a ContentResolver... Or extend Application to keep hold of a context and access it statically.

Here is how I wound up doing this, for any who may find this thread in the future:
I used sugarynugs' method of creating a class that extends Application, and then added the appropriate registration in the application manifest file. The code for my application class is then:
import android.app.Application;
import android.content.ContentResolver;
import android.content.Context;
public class CoreLib extends Application {
private static CoreLib me;
public CoreLib() {
me = this;
}
public static Context Context() {
return me;
}
public static ContentResolver ContentResolver() {
return me.getContentResolver();
}
}
Then, to get a ContentResolver in my library class, my function code is such:
public static ArrayList<Group> getGroups(){
ArrayList<Group> rv = new ArrayList<Group>();
ContentResolver cr = CoreLib.ContentResolver();
Cursor c = cr.query(
Groups.CONTENT_SUMMARY_URI,
myProjection,
null,
null,
Groups.TITLE + " ASC"
);
while(c.moveToNext()) {
rv.add(new Group(
c.getInt(0),
c.getString(1),
c.getInt(2),
c.getInt(3),
c.getInt(4))
);
}
return rv;
}

A bit hard without seeing more of how you are coding your library, but I don't see another option then to use the context, and so pass that when calling that class.
A 'random' class does not have the environment to get a contentresolver: you need a context.
Now it's not too strange to actually pass your (activity) context to your class. From http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
On Android, a Context is used for many operations but mostly to load and
access resources. This is why all the
widgets receive a Context parameter in
their constructor. In a regular
Android application, you usually have
two kinds of Context, Activity and
Application. It's usually the first
one that the developer passes to
classes and methods that need a
Context
(emphasis mine)

Related

How to test an Activity that uses a ContentProvider without affecting production database?

THE PROBLEM
I have two Android classes that I want to test:
CommentContentProvider, which extends ContentProvider and is backed by a SQLiteDatabase.
CommentActivity, which extends Activity and accesses CommentContentProvider indirectly through a ContentResolver.
I currently have two test classes:
CommentContentProviderTest, which extends ProviderTestCase2<CommentContentProvider> and uses a MockContentResolver. This works fine.
CommentActivityTest, which extends ActivityInstrumentationTestCase2<CommentActivity>. This works fine, except for the parts of CommentActivity that access CommentContentProvider.
The problem is that, when CommentActivity accesses CommentContentProvider, it does so through the standard ContentResolver:
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver().query(...);
Thus, when CommentActivityTest is run, it launches CommentActivity, which accesses (read and write) the production database, as shown in the above two lines.
My question is how to make CommentActivity use the standard ContentResolver in production but MockContentResolver during test.
RELATED QUESTIONS
This differs from Android unit testing with ContentProviders and other questions I've found about testing ContentProviders because those can extend android.test classes designed for testing ContentProviders while I need to extend a class for testing an Activity.
This is similar to How to inject a dependency when testing an Android activity without a third-party framework?, which was also posted by me but is unanswered. I am now willing to use a third-party framework if it will help.
Query using MockContentResolver leads to NullPointerException is related and leads to the solution in Option 1 below, but I do not know if it is the best solution in my case.
POSSIBLE SOLUTIONS
It would be nice if I could inject a ContentResolver (possibly a MockContentResolver or RenamingDelegatingContext) through the Intent that starts CommentActivity, but I can't do that, since Contexts are not Parcelable.
Which of the following options are best, or is there a better option?
OPTION 1
Add a debug flag to the Intent that starts CommentActivity:
public class CommentActivity extends Activity {
public static final String DEBUG_MODE = "DEBUG MODE";
private ContentResolver mResolver;
#Override
protected void onCreate(Bundle savedInstanceState) {
:
// If the flag is not present, debugMode will be set to false.
boolean debugMode = getIntent().getBooleanExtra(DEBUG_MODE, false);
if (debugMode) {
// Set up MockContentResolver or DelegatingContextResolver...
} else {
mResolver = getContentResolver();
}
:
}
I don't like this option because I don't like to put test-related code in my non-test classes.
OPTION 2
Use the abstract factory pattern to pass a Parcelable class that either provides the real ContentProvider or a MockContentProvider:
public class CommentActivity extends Activity {
public static final String FACTORY = "CONTENT RESOLVER FACTORY";
private ContentResolver mResolver;
#Override
protected void onCreate(Bundle savedInstanceState) {
:
ContentResolverFactory factory = getIntent().getParcelableExtra(FACTORY);
mResolver = factory.getContentResolver(this);
:
}
where I also have:
public abstract class ContentResolverFactory implements Parcelable {
public abstract ContentResolver getContentResolver(Context context);
}
public abstract class RealContentResolverFactory extends ContentResolverFactory
public ContentResolver getContentResolver(Context context) {
return context.getContextResolver();
}
}
public abstract class MockContentResolverFactory extends ContentResolverFactory
public ContentResolver getContentResolver(Context context) {
MockContentResolver resolver = new MockContentResolver();
// Set up MockContentResolver...
return resolver;
}
}
In production, I pass in (via an intent) an instance of RealContentResolverFactory, and in test I pass in an instance of MockContentResolverFactory. Since neither has any state, they're easily Parcelable/Serializable.
My concern about this approach is that I don't want to be "that guy" who overuses design patterns when simpler approaches exist.
OPTION 3
Add the following method to CommentActivity:
public void static setContentResolver(ContentResolver) {
:
}
This is cleaner than Option 1, since it puts the creation of the ContentResolver outside of CommentActivity, but, like Option 1, it requires modifying the class under test.
OPTION 4
Have CommentActivityTest extend ActivityUnitTestCase<CommentActivity> instead of ActivityInstrumentationTestCase2<CommentActivity>. This lets me set CommentActivity's context through setActivityContext(). The context I pass overrides the usual getContentResolver() to use a MockContentResolver (which I initialize elsewhere).
private class MyContext extends RenamingDelegatingContext {
MyContext(Context context) {
super(context, FILE_PREFIX);
}
#Override
public ContentResolver getContentResolver() {
return mResolver;
}
}
This works and does not require modifying the class under test but adds more complexity, since ActivityUnitTestCase<CommentActivity>.startActivity() cannot be called in the setUp() method, per the API.
Another inconvenience is that the activity must be tested in touch mode, and setActivityInitialTouchMode(boolean) is defined in ActivityInstrumentationTestCase2<T> but not ActivityUnitTestCase<T>.
FWIW, I am being a little obsessive about getting this right because I will be presenting it in an Android development class I am teaching.
Option 2 seems best to me. I'm not bothered about the use of a factory; I'm more bothered by the intent causing a change in behavior at a distance. But the other solutions put non-production code in the production code, so what you are testing isn't much like how things work in production. Hope that helps.

Error while trying to execute Sugar database CRUD operation

I (having mediocre developing skills) actually try to use Sugar as a database wrapper for my android project.
Therefore, I was following along the "Getting-Started-Guide" (http://satyan.github.io/sugar/getting-started.html) to get ready as soon as possible.
I created a class for my entities, called DataSet.java :
import com.orm.SugarRecord;
public class DataSet extends SugarRecord{
int someData;
double evenMoreData;
public DataSet(Context ctx){
super(ctx);
}
public DataSet(Context ctx,
int someData,
long evenMoreData) {
super(ctx);
this.someData = someData;
this.evenMoreData = evenMoreData;
}
}
I call the class in the following way:
someGreatClass something;
someMoreGreatCode somemore;
DataSet dataSet = new DataSet(
ctx, // Here Eclipse throws the error
something.method(),
somemore.anothermethod());
DataSet.save();
When I try to build this and to push it onto my device, Eclipse refuses to compile and throws this error:
ctx cannot be resolved to a variable
Considering the fact that I'm relatively new to Android development, the error may be obvious and I hope to get a tip how to solve this.
P.S.: Furthermore, I don't fully get the developer's statement in the getting-started-Note:
Please retain one constructor with Context argument. (This constraint will be removed in subsequent release.)
Thank you very much!
// Edit: Did edit the class name from LocationDataSet to Data set for clarification
First of all, the getting-started-note tells you that you need a constructor with only a context parameter, you did this here so that's ok
public DataSet(Context ctx){
super(ctx);
}
about
ctx cannot be resolved to a variable
I think you don't have a variable called ctx, I don't know if you're familiar with android context? (basically a context is a service or an activity), if you're using this code in an activity or a service, just use the 'this' keyword and not the ctx variable
The code you provide doesn't really show what you're doing, but you showed us the code from 'DataSet', but the error happens with a LocationDataSet? And you're calling save on DataSet?
The save method must be called on an object, not a class.
Also don't forget that sugar needs the special application class in the manifest
UPDATE with example:
Your dataset class (the sugarrecord) should look like this, that's ok in your code as far as I can see
public class DataSet extends SugarRecord<DataSet>{
private String someData;
public DataSet(Context c){
super(c);
}
public DataSet(Context c, String someData){
super(c);
this.someData = someData;
}
}
An activity that uses the record should look like this
public class SomeActivity extends Activity {
public void someMethodThatUsesDataSet(){
// Create a dataset object with some data you want the save and a context
// The context we use here is 'this', this is the current instance of SomeActivity,
// you absolutely need this, I think this is what you're doing wrong,
// you can't use ctx here because that's not a known variable at this point
DataSet example = new DataSet(this, "data you want to save");
// Tell Sugar to save this record in the database
example.save();
}
}

call getString(R.strings....) from class?

Is there a way to use the getString method from a seperate class?
I have a string stored in my strings xml, I'd like to use that string in an object... but the method isn't even available in the object...
any tips?
getString() is a method of the Context class¹. If you need it inside a seperate class (that does not extend Context), it's usually best to provide it as a seperate argument to the method that needs it.
Example:
public void logString(Context c, int stringId) {
Log.d("TAG", c.getString(stringId));
}
One thing is important: Never store the context inside the separate class.
Provide an argument. Otherwise you will leak memory and disrupt the whole android lifecycle if the object that stores the context lives longer than the object where the context originally belongs to (e.g. an activity).
¹ getString() can also be used from the Resources class - which you can get via Context.getResources()
the solution here is to make sure your object has a reference to the application context
Class Swag{
private Context ctx;
public Swag(Context ctx){
this.ctx = ctx;
}
public void doSomething(){
String something = ctx.getResources().getString(R.string.somestring);
...
}
// or like this
public void makeUpperCase(Context appContext){
appContext.getResources().getString(R.string.super_string_swag_yolo);
}
}
obviously you'd have to supply the context when creating an object or when caling the method
resouce file: values/strings.xml
<resources>
<string name="app_name">App name</string>
<resources>
java
import android.content.res.Resources;
Resources.getSystem().getString(R.string.app_name);//result : App name
edit:
The below will NOT work. I read this on another site and assumed it worked, but I just tried it in my app and kept getting an error. Problem is, it will compile but you will get a runtime exception.
This will work from any java class:
import android.content.res.Resources
Resources.getSystem().getString(R.string.blah);
if you cannot pass a context as parameter, create another class, where you put all your static data.
example :
public class StaticData {
public static String BASE_URL = "https://stackoverflowrocks.com";
}
and get that string from your other class by calling directly
StaticData.BASE_URL
nice and clean.
This works, but for SYSTEM resources only:
import android.content.res.Resources
Resources.getSystem().getString(R.string.blah);
Reference: https://stackoverflow.com/a/40917607/8994882
Try this in your java file:
String myString = getResources().getString(R.string.MY_STRING)
Now use this string object.

How to call method from another class without passing context?

I am currently trying to call a method from a utility class that will reference a new cursor created for this utility method. Unfortunately, my new class will not let me create the cursor without context. I have tried numerous ways of passing context from the calling activity, but get null pointer exceptions in most cases.
Here is the portion of my code:
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Tools.pickRandomItem();
}
});
and in the Tools Class:
public static void pickRandomItem() {
Cursor cur = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
null, null,MediaColumns.TITLE + " ASC");
}
Using the above code it throws an error on getContentResolver(), and all attempts I've made to pass context have failed.
I am fairly new to programming for Android, and don't fully understand the concept of contexts. Any help you could provide would be greatly appreciated!
Create a class that extends Application for your project (you have to declare it in the Manifest too), in the Application make a
private static MyApplication app
in the onCreate() of it assign it to the field
app = this;
and make a
public static MyApplication get()
in it. When you need a Context you can use a
MyApplication.get()
A few hints on Context:
Get Context in you View via getContext() and pass it to getContentResolver(context).
Use application-context approach as described here by #apps.
Don't store context inside Activity or Views. This leads to memory leaks.

Passing a cursor to an activity?

Is this possible? I am trying to open a SQLite database cursor in one activity, and pass it to another activity.
Another way to do this which might be easier is to create an Application class for your app. This is guaranteed to be created only once, and exists for the lifetime of your app. Among other things, it can provide a "data hub" capability for your app so different Activities can share data easily. So, for your cursor, you'd simply use a member variable of the Application class like so (warning, I copied this code from my app and edited it here, so no guarantee of compilation. Just something to show the idea.):
package com.jcascio.k03;
import android.app.Application;
import android.database.Cursor;
// use your application's name instead of "K03Application"
public class K03Application extends Application {
public final String TAG = "K03";
Cursor sharedCursor; // this cursor can be shared between different Activities
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onTerminate() {
super.onTerminate();
}
public Cursor getSharedCursor()
{
return this.sharedCursor;
}
public void setSharedCursor(Cursor c)
{
this.sharedCursor = c;
}
}
The application object can be fetched from any Activity using
this.getApplication()
// You cast it to your Application sub-class and call the Cursor accessor function
Cursor c = ((K03Application)this.getApplication()).getSharedCursor();
So, your first Activity would fetch some goo from the database, which is returned to it as a Cursor. This activity would call setSharedCursor in the application. Then it would launch the second Activity, which would call getSharedCursor in its onCreate function (or any other function for that matter) to retrieve the cursor.
I personally don't know of any simple ways to do this. It might be easier just to make the query again in the destination activity.
You should write your own Cursor which will implement Parcelable interface. In this case you can put your cursor to parcel and send it to another Activity through putExtra(). In target Activity you can explode (in fact just find it through handler) Cursor through one of Parcel methods (related to Binder).

Categories

Resources