I have an activity which instantiate a class with an interface. How I can get the android context within MyClass with only the reference to the interface?
public class TestActivity extends Activity implements MyInterface {
#Override
public void onCreate(final Bundle savedInstanceState) {
final MyClass myClass = new MyClass(this);
}
#Override
public void onChange() {
}
}
public interface MyInterface {
void onChange();
}
public class MyClass {
public MyClass(MyInterface myInterface) {
// how to get context from myInterface ???
}
}
public class MyClass {
public MyClass(MyInterface myInterface) {
// Get Context
Context context = null;
if (myInterface instanceOf Context)
context = (Context)myInterface;
}
}
If your Activity (which extends Context) is implementing MyInterface and you pass that to MyClass, you just need to cast it to the appropriate type.
The context is only available in Activity classes or global Context, if you have to use the context, change your code:
public class TestActivity extends Activity implements MyInterface {
#Override
public void onCreate(final Bundle savedInstanceState) {
final MyClass myClass = new MyClass(this);
}
#Override
public void onChange() {
}
}
public interface MyInterface {
void onChange();
}
public class MyClass {
public MyClass(TestActivity activity) {
//now, you can use the context of your activity or do a cast
// to your interface
MyInterface interface = (MyInterface) activity;
}
}
I think you approached it a bit wrong, but I might miss understood your design concept what you want to achieve.
Create your Interface in the Class where you want to obtain the Context from and the use implement like you did in your activity.
YourContextClass() {
private MyInterface interface;
onCreate() {
interface.onChange(this);
}
public interface MyInterface() {
void onChange(Context context);
}
}
Then in your Activity class implement MyInterface, and inside the method, you can obtain the Context.
YourActivity implements MyInterface {
private Context context;
...
void onChange(Context context) {
this.context = context;
}
}
But this is only necessary if you really need the context from Interface... otherwise, in you Activity, I see you are extendingActivity, which gives you access to the Context with getContext();
Related
Android Studio 3.0 Canary 8
I am trying to inject my MainActivity into my Adapter. However, my solution works ok, but I think its a code smell and not the right way to do it.
My adapter snippet looks like this the but I don't like about this is that I have to cast the Activity to MainActivity:
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
private MainActivity mainActivity;
public RecipeAdapter(Activity activity, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
this.mainActivity = (MainActivity)activity;
}
#Override
public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/* Using the MainActivity to call a callback listener */
mainActivity.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()));
}
});
return recipeListViewHolder;
}
}
In my Module, I pass the Activity in the module's constructor and pass it to the Adapter.
#Module
public class RecipeListModule {
private Activity activity;
public RecipeListModule() {}
public RecipeListModule(Activity activity) {
this.activity = activity;
}
#RecipeListScope
#Provides
RecipeAdapter providesRecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
return new RecipeAdapter(activity, viewHolderFactories);
}
}
In My Application class I create the components and I am using a SubComponent for the adapter. Here I have to pass the Activity which I am not sure is a good idea.
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
recipeListComponent = createRecipeListComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.exoPlayerModule(new ExoPlayerModule())
.build();
}
public RecipeListComponent createRecipeListComponent(Activity activity) {
return recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
}
My Fragment I inject like this:
#Inject RecipeAdapter recipeAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent(getActivity())
.inject(this);
}
Even though the above design works, I think it's a code smell as I have to cast the Activity to the MainActivity. The reason I use the Activity as I want to make this module more generic.
Just wondering if there is a better way
=============== UPDATE USING INTERFACE
Interface
public interface RecipeItemClickListener {
void onRecipeItemClick(Recipe recipe);
}
Implementation
public class RecipeItemClickListenerImp implements RecipeItemClickListener {
#Override
public void onRecipeItemClick(Recipe recipe, Context context) {
final Intent intent = Henson.with(context)
.gotoRecipeDetailActivity()
.recipe(recipe)
.build();
context.startActivity(intent);
}
}
In my module, I have the following providers
#Module
public class RecipeListModule {
#RecipeListScope
#Provides
RecipeItemClickListener providesRecipeItemClickListenerImp() {
return new RecipeItemClickListenerImp();
}
#RecipeListScope
#Provides
RecipeAdapter providesRecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
return new RecipeAdapter(recipeItemClickListener, viewHolderFactories);
}
}
Then I use it through constructor injection in the RecipeAdapter
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
private RecipeItemClickListener recipeItemClickListener;
#Inject /* IS THIS NESSESSARY - AS IT WORKS WITH AND WITHOUT THE #Inject annotation */
public RecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
this.recipeItemClickListener = recipeItemClickListener;
}
#Override
public RecipeListViewHolder onCreateViewHolder(final ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
recipeItemClickListener.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()), viewGroup.getContext());
}
});
return recipeListViewHolder;
}
}
Just one question, is the #Inject annotation need for the constructor in the RecipeAdapter. As it works with or without the #Inject.
Do not pass Activities into Adapters - This is a really bad practice.
Inject only the fields you care about.
In your example: Pass an interface into the adapter to track the item click.
If you need a MainActivity then you should also provide it. Instead of Activity declare MainActivity for your module.
#Module
public class RecipeListModule {
private MainActivity activity;
public RecipeListModule(MainActivity activity) {
this.activity = activity;
}
}
And your Adapter should just request it (Constructor Injection for non Android Framework types!)
#RecipeListScope
class RecipeAdapter {
#Inject
RecipeAdapter(MainActivity activity,
Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
// ...
}
}
If you want your module to use Activity and not MainActivity then you will need to declare an interface as already mentioned. You adapter would then declare the interface as its dependency.
But in some module you will still have to bind that interface to your MainActivity and one module needs to know how to provide the dependency.
// in some abstract module
#Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface
Addressing the updated part of the question
Just one question, is the #Inject annotation need for the constructor in the RecipeAdapter. As it works with or without the #Inject.
It works without it because you're still not using constructor injection. You're still calling the constructor yourself in providesRecipeAdapter(). As a general rule of thumb—if you want to use Dagger properly—don't ever call new yourself. If you want to use new ask yourself if you could be using constructor injection instead.
The same module you show could be written as follows, making use of #Binds to bind an implementation to the interface, and actually using constructor injection to create the adapter (which is why we don't have to write any method for it! Less code to maintain, less errors, more readable classes)
As you see I don't need to use new myself—Dagger will create the objects for me.
public abstract class RecipeListModule {
#RecipeListScope
#Binds
RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener);
}
Personally I would do the following trick
public class MainActivity extends AppCompatActivity {
private static final String TAG = "__ACTIVITY__";
public static MainActivity get(Context context) {
// noinspection ResourceType
return (MainActivity)context.getSystemService(TAG);
}
#Override
protected Object getSystemService(String name) {
if(TAG.equals(name)) {
return this;
}
return super.getSystemService(name);
}
}
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
public RecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
}
#Override
public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity mainActivity = MainActivity.get(v.getContext());
if(recipeListViewHolder.getAdapterPosition() != -1) {
mainActivity.onRecipeItemClick(
getRecipe(recipeListViewHolder.getAdapterPosition()));
}
}
});
return recipeListViewHolder;
}
}
I know I can use getApplicationContext() to get Context from sub class of ListActivity.
but PublicPar is common class, how can I get Context from this class.
public class SMSMain extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Context my=getApplicationContext();
}
}
public class PublicPar {
public static void SetNotification(){
}
}
If you have a common (helper-type) class like your PublicPar class, the best you can do is to pass context as a parameter to each method:
public static void SetNotification(Context context) {
}
Remember to not set this context to any PublicPar class variable to avoid leaking it.
Try this. It should work:
public class SMSMain extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// Context my=getApplicationContext();
new PublicPar().SetNotification(SMSMain.this)
}
}
public class PublicPar {
public static void SetNotification(final Context context ){
// You can proceed with using the context here.
}
}
If you don't want to pass the Context around as part of constructor argument, you can expose a static method in the application.
public class MyApplication extends Application {
private static MyApplication myinstance;
public MyApplication() {
myinstance = this;
}
public static Context getAppContext() {
myinstance.getApplicationContext();
}
}
I have an Activity and non Activity class. How to call a method in Activity class from non Activity class
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
DataClass dc = new DataClass();
dc.show();
}
public void call(ArrayList<String> arr) {
// Some code...
}
}
public class DataClass {
public void show(ArrayList<String> array) {
// Here I want to send this ArrayList values into the call
// method in activity class.
MainActivity act = new MainActivity();
act.call(array);
}
}
Just create a callback interface inside the DateClass.
public DateClass {
public interface IDateCallback {
void call(ArrayList<String> arr);
}
private IDateCallback callerActivity;
public DateClass(Activity activity) {
callerActivity = (IDateCallback)activity;
}
...
}
public void show(ArrayList<String> array) {
callerActivity.Call(array);
...
}
//And implements it inside your activity.
public class MainActivity extends Activity
implements IDateCallback {
public void call(ArrayList<String> arr) {
}
}
Well there are several things you could do. I think the easiest for you would be to send the Context into DataClass like so:
DataClass dc =new DataClass();
dc.show(this);
And in your DataClass save the context into a global var Context context. Then use it like so:
((MainActivity)context).call(array);
((MainActivity)getContext).array();
Just make a singleton like:
TeacherDashboardSingleton:
public class TeacherDashboardSingleton {
public Teacher_Dashboard aa;
private static final TeacherDashboardSingleton ourInstance = new TeacherDashboardSingleton();
public static TeacherDashboardSingleton getInstance() {
return ourInstance;
}
}
myActivity class:
onCreate(....){
....
TeacherDashboardSingleton.getInstance().aa = this;
....
}
this will create an object of same instance as in activity
now you can use it from anywhere
MyActivity implements a CustomListener defined in MyClass.java. I have a function defined in MyClass that should trigger the listener and do some action(finish() MyActivity) defined in the activity. Somehow, I am getting null reference to the listener. Where am I going wrong?
Please refer to the code below!
MyActivity.java
import com.project.MyClass.CustomListener;
public class MyActivity extends Activity implements CustomListener {
Context context;
CustomListener listener;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
myClass = new MyClass(context);
myClass.setOnCustomListener(listener);
}
public void doThisWhenTriggered()
{
...
}
}
MyClass.java
public class MyClass{
private Contect ctx;
ArrayList<CustomListener> listeners = new ArrayList<CustomListener>();
public MyClass(Context context)
{
super();
this.ctx = context
}
public interface CustomListener
{
public void doThisWhenTriggered();
}
public void setOnCustomListener(CustomListenerListener listener)
{
this.listeners.add(listener);
}
public void generateTrigger()
{
CustomListener listener = listeners.get(0);
if(listener != null)
listener.doThisWhenTriggered();
else
Log.d("MyAPP", "Listener is NULL");
}
}
MyBroadcastReceiver.java
public void onReceive(Context context, Intent intent) {
...
callMyClass(Context context)
}
public void callMyClass(Context context)
{
MyClass myClass= new myClass(context);
myClass.generateTrigger(context);
}
EDIT
The problem was BECAUSE of accessing different instances of MyClass in BroadcastReceiver and Listeners list as correctly pointed out by #Binyamin Sharet
The solution is to declare the listener list as static and also public void generateTrigger() as static so that the receiver's callMyClass looks like :
public void callMyClass(Context context)
{
MyClass.generateTrigger(context);
}
You never instantiate listener (declared as CustomListener listener;) and therefore its always null, you just need to set the activity as the listener, as it implements the interface:
myClass.setOnCustomListener(this);
As seen in your code, you create a new instance of the class in the receiver, so the listener you set does not exist in the listeners list of new instance, since the list is not static.
Its because
MyClass myClass = new myClass(context);
in MyBroadcastReceiver.java. This will create a new instance.
So I think it will be better to use MyClass.java as Singleton.
public class MyClass {
private Contect ctx;
ArrayList<CustomListener> listeners = new ArrayList<CustomListener>();
private static final MyClass singletonMyClass = new MyClass();
private MyClass() {
}
public static CustomListner getInstance() {
return singletonMyClass;
}
public interface CustomListener {
public void doThisWhenTriggered();
}
public void setOnCustomListener(CustomListenerListener listener) {
this.listeners.add(listener);
}
public void generateTrigger() {
CustomListener listener = listeners.get(0);
if (listener != null)
listener.doThisWhenTriggered();
else
Log.d("MyAPP", "Listener is NULL");
}
}
from MyActivity.java you can call
MyClass myClass = MyClass.getInstance();
myClass.setOnCustomListener(listener);
and similarly in MyBroadcastReceiver.java
public void callMyClass(Context context)
{
MyClass myClass= MyClass.getInstance();
myClass.generateTrigger();
}
Hope this helps!!!.
MAIN ACTIVITY
public class MyActivity() extends Activity
{
onCreate()
{
MyClass myobj=new MyClass();
}
public void Mymethod()
{}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass()
{
MyClass(Context context)
{
}
}
I tried to call Mymethod() from an instance of MyClass.
I would really appreciate any help. Thanks.
Why not just pass the activity to the constructor like
public class MyActivity extends Activity {
onCreate(){
MyClass myobj=new MyClass(MyActivity.this);
}
public void myMethod(){
}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass{
public MyClass(MyActivity act) {
act.myMethod();
}
}
Make that method as static so you can call without creating the class object
public static void Mymethod()
{}
and call like this way
MainActivity.Mymethod();
This is probably the best way to do it. This is how I'm doing it. It's called a Singleton Design Pattern:
public class MyActivity extends Activity {
private static MainActivity instance;
public static MainActivity getInstance() {
if(instance==null){
setInstance(this);
}
return instance;
}
public static void setInstance(MainActivity instance) {
MainActivity.instance = instance;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setInstance(this);
}
}
If I'm understanding you correctly I believe you can solve your problems using an interface as a callback.
////ACTIVITY/////////////////////////////////
public class MyActivity() extends Activity {
onCreate()
{
MyClass myObj=new MyClass();
//Set the listener on the object. Created as anonymous
myObj.setListener(new MyClass.Listener() {
myMethod();
});
}
}
public void myMethod(){
}
//////Custom Class//////////////////
public class MyClass {
Listener mListener;
public interface Listener {
public void onInterestingEvent();
}
public void setListener(Listener listener) {
mListener = listener;
}
public void someUsefulThingTheClassDoes() {
//Do your code here and when you're ready to call the activity's method do this
mListener.onInterestingEvent();
}
}
I had an inner class that I wanted to pull out into a more general library "Helper" class. I had the same issue you do. I got around it by making the helper class abstract, with a single abstract method. Then in my project package I extended the helper class with a constructor call in the specific class.
public class MyActivity extends Activity {
onCreate() {
MyHelperClass = new MyHelperClass(this, "foobar");
}
public void myMethod() {
// Code...
}
}
// In a different file
public class MyHelperClass extends HelperClass {
private MyActivity mInstance;
public MyHelperClass(MyActivity act, String data) {
super();
this.mInstance = act;
this.mActivity = act; // Useful for calling generic Activity methods in the HelperClass
this.mData = data;
}
protected void callMyActivityMethod() {
mInstance.myMethod();
}
}
// In a different file
public abstract class HelperClass {
protected Activity mActivity;
protected String mData;
public HelperClass() {
// Subclass will set variables
}
protected abstract void callMyActivityMethod();
// More code for all the other stuff the class does
}
In this way, I have a helper class that contains the vast majority of the "work", and all I have to do is make a subclass with the constructor and one method in order to get access to the calling activity's method of interest.
You have to pass instance of MainActivity into another class, then you can call everything public (in MainActivity) from everywhere.
MainActivity.java
public class MainActivity extends AppCompatActivity {
// Instance of AnotherClass for future use
private AnotherClass anotherClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Create new instance of AnotherClass and
// pass instance of MainActivity by "this"
anotherClass = new AnotherClass(this);
}
// Method you want to call from another class
public void myMethod(){
...
}
}
AnotherClass.java
public class AnotherClass {
// Main class instance
private MainActivity mainActivity;
// Constructor
public AnotherClass(MainActivity activity) {
// Save instance of main class for future use
mainActivity = activity;
// Call method in MainActivity
mainActivity.myMethod();
}
}
In MainActivity.class file
You have to pass MainActivity context from MainActivity Class. Then in MyClass you have to Get MainActivity context. Remember Context and MyActivity are two different reference.
public class MyActivity extends Activity
{
onCreate(){
MyClass myobj=new MyClass(MyActivity context);
}
public void Mymethod(){}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass()
{
MyActivity context;
MyClass(MyActivity context)
{
this.context = context;
this.context.Mymethod();
//Or you can directly use activity context
context.Mymethod();
}
}
I decided to write the HelperClass MyClass as an inner class of MyActivity class. This allows it full access to parent class but the bad thing is now MyClass is restricted to MyActivity class only.
public class MyActivity() extends Activity
{
onCreate()
{
MyClass myobj=new MyClass();
}
public void myMethod()
{
}
}
//INNER CLASS
public class MyClass
{
public MyClass()
{
}
//I can directly access the MyMethod
myMethod();
}