Interface from AsyncTask back to Fragment without knowing Interface - android

What I am trying to achive is a single AsynTask that can check if a user has access to a certain fragment in my application that can be called from various fragments.
I am not sure that this is even possible so thought I would ask here, this is my latest attempt
public class HasAccess extends AsyncTask<Void,Void,Void> {
Context ctx;
String AppName;
IInterface i;
int AccessGranted;
public HasAccess(Context _ctx, String _AppName, IInterface _i)
{
this.ctx = _ctx;
this.AppName = _AppName;
this.i = _i;
}
#Override
protected Void doInBackground(Void... params) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
int UserID = Integer.parseInt(prefs.getString("prefs_userid", null));
PermsTableOps PTO = new PermsTableOps(ctx);
int AccessGranted = PTO.hasAccess(AppName,UserID);
return null;
}
#Override
protected void onPostExecute(Integer integer) {
i.Access(AccessGranted);
}
}
So basically from various fragments I would like to be able to call this task, each of these fragments has its own interface, what I want to avoid is creating an AsyncTask for each interface.
I hope this is clear enough!
Edit I think the code above is confusing the issue so I have provided a theoretical example:
Fragment 1 inherits interface 1
Fragment 2 inherits interface 2
AsyncTask 1
Both fragment 1 and fragment 2 call AsyncTask1 - I want a response sent to my fragment from the asynctask whether it is called from either fragment 1 or fragment 2, by sending in the relevant interface.
I hope this is clearer.

Related

How do we cast context to fragment reference?

I have a class 'Common' and a fragment 'FragmentTest'. The 'Common.java' is a general class that have some common functions for other activities..Those functions are accessed by context of each activities.. And here I am passing the fragment's context to a function in that class. I am doing like this
In Fragment :-
Common commonObj = new Common();
commonObj.myfunction(this.getActivity(),"Do you want to Update ?");
And in Class after some operation i'm trying to return back to fragment class.Like this
:-
public void myfunction(Context context , String str){
//....//
if(context.getClass().isInstance(FragmentTest.class)){
**FragmentTest mContext = (FragmentTest)context;**
mContext.FunctionInFragment();
}
}
But i have error in this..Because i cannot cast the context to fragment reference.
Somebody please help..
Firstly you can't cast a Context to a Fragment as Fragment doesn't extend Context. Activity does extend Context which is why when you do this from an Activity what you are trying works.
I'd suggest ideally that the methods in your Common class should be totally unaware of the existence of your Fragment. This way they are not 'coupled' together. To achieve this you can use a callback interface.
Create an interface as follows:
public interface Callback<T> {
onNext(T result);
}
Then you can change your method in Common to the following:
public void myfunction(Callback<Void> callback , String str){
//....//
callback.onNext(null);
}
Then when you call the method in Common from the Fragment you would do it like this:
Common commonObj = new Common();
commonObj.myfunction(
new Callback<Void>() {
#Override
public void onNext(Void result) {
functionInFragment();
}
},
"Do you want to Update ?"
);
If you needed to send data back to the function then you can change the return type of the callback. For instance if you wanted to pass back a string you would use Callback<String> and then the method in the original call would look like this:
new Callback<String>() {
#Override
public void onNext(String result) {
}
}
And in your Common class you would call it like this:
public void myfunction(Callback<String> callback , String str){
//....//
String result = "Hello from common";
callback.onNext(result);
}
You can do something like this:
public void myfunction(BaseFragment fragment, String str){
//....//
if(fragment.getClass().isInstance(FragmentTest.class)){
FragmentTest fr = (FragmentTest) fragment;
fr.FunctionInFragment();
}
}
i.e. using some base fragment(BaseFragment) and inherit from it.

Updating fragment from Activity Using Rxjava Android

I have a simple use case where:
Activity1 create a fragment1
fragment1 after creation notify to activity that it is created and update its activity1 views.
activity1 after getting notification update fragment1 views.
I am using rxandroid , sublibrary rxlifecycle components and android , but i am still in learning phase , there was not even rx-lifecycle tag on stackoverflow , so i am still struggling to understand the flow of this library..
Edit
I prefer not to use EventBus , it's just like everyone shouting at everyone to do something, so Rxjava Observable approach will be much useful
For posting information from fragment to activity, you should use an event bus for informing activity about fragment creation (replacement to the callbacks and the mess they created).
Sample code for event bus with RxJava is:
public class SampleEventsBus {
private static final String TAG = SampleEventsBus.class.getSimpleName();
private static final String TAG2 = SampleEventsBus.class.getCanonicalName();
public static final int ACTION_FRAGMENT_CREATED = 1;
public static final int ACTION_FRAGMENT_OTHER = 2;
private static SampleEventsBus mInstance;
public static SampleEventsBus getInstance() {
if (mInstance == null) {
mInstance = new SampleEventsBus();
}
return mInstance;
}
private SampleEventBus() {}
private PublishSubject<Integer> fragmentEventSubject = PublishSubject.create();
public Observable<Integer> getFragmentEventObservable() {
return fragmentEventSubject;
}
public void postFragmentAction(Integer actionId) {
fragmentEventSubject.onNext(actionId);
}
}
Then from your fragment you can call:
SampleEventsBus.getInstance().postFragmentAction(SampleEventsBus.ACTION_FRAGMENT_CREATED);
from onAttach() or onViewCreated() or any place you prefer.
Also, in activity you will need to put the following code to listet to your event bus:
SampleEventsBus .getInstance().getFragmentEventObservable().subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Integer actionId) {
if(actionId == SampleEventsBus.ACTION_FRAGMENT_CREATED) {
//do any required action
}
}
});
For the second part, i.e. to update the fragment from activity, I won't recommend using this method as it will lead to unnecessary complexity, Instead use the "original way" as:
Create a method in Fragment as updateView(Object obj)
In onNext(), get the desired fragment as SampleFragment fragment = (SampleFragment)getSupportFragmentManager().findFragmentByTag("TAG");
call fragment.updateView(obj);
Hope this helps.
Two points to consider:
Just because you use an EventBus does not mean that it needs to be
global. You can have multiple event buses if you want, and you can just
share a single one between two components (Activity and Fragment).
There are several examples in the RxJava documentation that show
how to implement event bus functionality using RxJava
By Using an event bus, you can simplify things greatly, by disassociating the whole process from the Android lifecycle.

Update TextView for different fragments from AsyncTask

I was looking over this answer and it seemed to only deal with a single textview.
Basically, I have an Android application with n fragments, each of which has a textview that is populated from a remote call to a database. Each time the fragment is selected, that remote call will fire and the textview should be repopulated.
Currently, I am using a central AsyncTask to accomplish this, however I am starting to wonder if it is the correct way to go about doing so (some textviews take too long to update for small amounts of data, some don't get updated at all, etc.).
Here is the code from my RetrieveData class. Essentially, it figures out which textview is to be updated, and then populates that textview.
public class RetrieveData extends AsyncTask<String, String, String[]> {
private int txtViewID = -1;
private Activity mainActivity;
public RetrieveData(Activity a) { mainActivity = a; }
protected String[] doInBackground(String... urls) {
String[] data;
// call web script to return JSON data
...
// figure out which fragment called which script
if (urls[0] == "get_A.php") {
data = parseJSONdata(); // parse out the JSON
txtViewID = R.id.txtViewA; // find INT-based ID
} else if (urls[0] == "get_B.php") {
data = parseOtherJSONdata(); // different type of call
txtViewID = R.id.txtViewB;
} else ... {
...
}
} catch (Exception e) {
System.out.println("Error: " + e.toString());
}
return data;
}
#Override
protected void onPostExecute(String[] op) {
if (txtViewID != -1) { // call was made
TextView tv = (TextView)mainActivity.findViewById(txtViewID);
tv.setText(op[0]);
}
and here is how I call this from a Fragment:
public class MainFragment extends Fragment {
Activity mainActivity;
public MainFragment(Activity a) { mainActivity = a; }
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.main_tab,container,false);
new RetrieveData(mainActivity).execute("get_A.php","1");
return v;
}
}
To me, its very kludgy and probably belies my newness to Android, so any suggestions for improvement are heartily appreciated.
You can do a couple of things to improve the robustness and performance and fix some issues which will creep in later:
Don't use findViewById() outside of init/setup type methods. It is an expensive call as it has to "search" your hierarchy for the ID you are requesting.
Don't use an overloaded constructor for your Fragment which takes the Activity. The Fragment default constructor should be empty. This allows the system to properly re-create your Fragment when configuration changes (screen rotates.) The Fragment will receive its attached Activity at the correct time when its onAttach() method is called, so there is no need to do this.
You shouldn't need the Activity at all for what you're trying to do. Instead, have your Fragment get the correct TextView from your layout in its onCreateView(). What you do from there is really up to you:
Pass the TextView instance to your RetrieveData class constructor as the one to be updated. This eliminates the hard coded IDs in your RetrieveData class, which gets rid of some explicit coupling and is a better approach. This is still very tightly coupled, though, since it depends on having a specific View so still not a great option IMHO.
Have the RetrieveData class define an inner Callback interface and have the Fragment implement it. The constructor for RetrieveData can then take an instance of the Callback interface (e.g. your Fragment instance) and when its onPostExecute() runs it just calls back the Fragment with the appropriate data. Now it is up to your Fragment implementation to make the right decision on what UI element it is hosting to update with the data. It may be a TextView now, but in the future you could make it something else, etc. Now you have decoupled the class from all explicit UI ties and put the responsibility on the thing hosting the UI elements: the Fragment.
Here's a brief example of the 2nd bullet:
public RetrieveData extends AsyncTask<String, String, String[]> {
// Define the interface used to provide results
public interface Callback {
public void onDataLoaded(String[] result);
}
private Callback mCb;
public RetrieveData(Callback cb) {
mCb = cb;
}
...
#Override
public void onPostExecute(String[] result) {
mCb.onDataLoaded(result);
}
}
public MyFragment extends Fragment implements RetrieveData.Callback {
TextView mResult;
RetrieveData mAsyncRetriever;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.main_tab,container,false);
// Get the TextView now where we want to show results.
// This avoids calling findViewById() constantly.
mResult = (TextView)root.findViewById(R.id.example_result);
...
}
#Override
public void onResume() {
// Keep a reference to the AsyncTask so we can properly
// cancel it when our lifecycle events dictate so.
mAsyncRetriever = new RetrieveData(this);
mAsyncRetriever.execute("get_A.php");
}
#Override
public void onPause() {
// If we have a pending data load going on, kill it.
if (mAsyncRetriever != null) {
mAsyncRetriever.cancel(true);
mAsyncRetriever = null;
}
}
#Override
public void onDataLoaded(String[] result) {
// Only pulling the first result provided
mResult.setText(result[0]);
// The RetrieveData is done, get rid of our ref
mAsyncRetriever = null;
}
}

How to desing a reusable DialogFragment

There is some questions already close to this question but they haven't been very helpful for me. So here comes a new one.
I have an Activity which has two tabs. Each tab contains a ListFragment (SherlockListFragment to be exact). One tab shows a list of shopping list objects and the other shows a list of recipe objects. Now I want to create a DialogFragment for renaming a list or a recipe or any other object I might later add to the application.
The solution provided here sounded promising but because ListFragment can not be registered to listen clicks from the dialog I should make my Activity to listen them which is not ideal because then my Fragments would not be independent.
How to get data out of a general-purpose dialog class
Ideally I would like to have my rename dialog as independent and reusable as possible. This far I have invented just one way to do this. Sending the objects className and id to the dialog and then using switch case to fetch the correct object from the database. This way the dialog would be able to update the objects name by itself (if the object has rename method). But the requery to the database sounds just dump because the ListFragment has the object already. And then the dialog would need a new case in the switch for each new kind of object.
Any ideas?
I actually just created a similar sort of dialog fragment to what you're asking about. I was for a fairly large app and it was getting kind of ridiculous the amount of dialog listeners our main activity was extending just to listen for the results of a single dialog.
In order to make something a bit more flexible I turned to using ListenableFuture from Google's Guava concurrent library.
I created the following abstract class to use:
public abstract class ListenableDialogFragment<T> extends DialogFragment implements ListenableFuture<T> {
private SettableFuture<T> _settableFuture;
public ListenableDialogFragment() {
_settableFuture = SettableFuture.create();
}
#Override
public void addListener(Runnable runnable, Executor executor) {
_settableFuture.addListener(runnable, executor);
}
#Override
public boolean cancel(boolean mayInterruptIfRunning) {
return _settableFuture.cancel(mayInterruptIfRunning);
}
#Override
public boolean isCancelled() {
return _settableFuture.isCancelled();
}
#Override
public boolean isDone() {
return _settableFuture.isDone();
}
#Override
public T get() throws InterruptedException, ExecutionException {
return _settableFuture.get();
}
#Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return _settableFuture.get(timeout, unit);
}
public void set(T value) {
_settableFuture.set(value);
}
public void setException(Throwable throwable) {
_settableFuture.setException(throwable);
}
// Resets the Future so that it can be provided to another call back
public void reset() {
_settableFuture = SettableFuture.create();
}
#Override
public void onDismiss(DialogInterface dialog) {
// Cancel the future here in case the user cancels our of the dialog
cancel(true);
super.onDismiss(dialog);
}
Using this class I'm able to create my own custom dialog fragments and use them like this:
ListenableDialogFragment<int> dialog = GetIdDialog.newInstance(provider.getIds());
Futures.addCallback(dialog, new FutureCallback<int>() {
#Override
public void onSuccess(int id) {
processId(id);
}
#Override
public void onFailure(Throwable throwable) {
if (throwable instanceof CancellationException) {
// Task was cancelled
}
processException(throwable);
}
});
This is where GetIdDialog is a custom instance of a ListenableDialogFragment. I can reuse this same dialog instance if needs be by simply calling dialog.reset in the onSuccess and onFailure methods to ensure that the internal Future gets reloaded for adding back to a callback.
I hope this helps you out.
Edit: Sorry forgot to add, in your dialog you can implement an on click listener that does something like this to trigger the future:
private class SingleChoiceListener implements DialogInterface.OnClickListener {
#Override
public void onClick(DialogInterface dialog, int item) {
int id = _ids[item];
// This call will trigger the future to fire
set(id);
dismiss();
}
}
I would maybe just using a static factory pattern of some variation to allow dynamic initialization of the DialogFragment.
private enum Operation {ADD, EDIT, DELETE}
private String title;
private Operation operation;
public static MyDialogFragment newInstance(String title, Operation operation)
{
MyDialogFragment dialogFragment = new DialogFragment();
dialogFragment.title = title; // Dynamic title
dialogFragment.operation = operation;
return dialogFragment;
}
Or.. and I would recommend this more, have a static factory method for each type of operation you will use it for. This allows different dynamic variations to be more concrete and ensures that everything works together. This also allows for informative constructors.
Eg.
public static MyDialogFragment newAddItemInstance(String title)
{
MyDialogFragment dialogFragment = new DialogFragment();
dialogFragment.title = title; // Dynamic title
return dialogFragment;
}
public static MyDialogFragment newEditItemInstance(String title)
{
MyDialogFragment dialogFragment = new DialogFragment();
dialogFragment.title = title; // Dynamic title
return dialogFragment;
}
And then of course create an interface that every calling Activity / Fragment (in which case you need to set this Fragment as the targetFragment and get reference to that target Fragment in your DialogFragment) so that the implementation is taken care of in the target Fragment and nothing to do with the DialogFragment.
Summary: There are various ways of going about this, for simplicity, I would stick with some form of static factory pattern and make clever use of interfaces to separate any the logic from the DialogFragment hence making it more reusable
EDIT: From your comment I would suggest you look at two things:
Target Fragments (See the comment I made on your question). You can invoke methods in your ListFragment from your DialogFragment.
Strategy Pattern. How does the Strategy Pattern work?. This allows you to perform the same operation (with various tailored implementation for each type) on different objects. Very useful pattern.

How to reference the current or main activity from another class

I often find myself needing to access methods that require referencing some activity. For example, to use getWindowManager, I need to access some Activity. But often my code for using these methods is in some other class that has no reference to an activity. Up until now, I've either stored a reference to the main activity or passed the context of some activity to the class. Is there some better way to do this?
If you already have a valid context, just use this:
Activity activity = (Activity) context;
Passing context is better way for refrence Activity.
You can pass Context to another class.
IN Activity ::
AnotherClass Obj = new AnotherClass(this);
IN Another Class
class AnotherClass{
public AnotherClass(Context Context){
}
}
You can implement the necessary methods in your activity and implement a Handler. Then, simply pass a handler instance to your classes, where you can obtain a message for handler and send it to target.
You can make you application instance a singleton, and use it when you need a Context
An example is in this question:
Android Application as Singleton
This way, when you need a Context, you can get it with
Context context = MyApplication.getInstance()
This might not be the cleanest solution, but it has worked well for me so far
I found a way to get the Activity to a non-activity class that I have not seen discussed in forums. This was after numerous failed attempts at using getApplicationContext() and of passing the context in as a parameter to constructors, none of which gave Activity. I saw that my adapters were casting the incoming context to Activity so I made the same cast to my non-activity class constructors:
public class HandleDropdown extends Application{
...
public Activity activity;
...
public HandleDropdown() {
super();
}
public HandleDropdown(Activity context) {
this.activity = context;
this.context = context;
}
public void DropList(View v,Activity context) {
this.activity = context;
this.context = context;
...
}
After doing this cast conversion of Context to Activity I could use this.activity wherever I needed an Activity context.
I'm new to android so my suggestion may look guffy but what if you'll just create a reference to your activity as private property and assign that in OnCreate method? You can even create your CustomActivity with OnCreate like that and derive all your activities from your CustomActivity, not the generic Activity provided by android.
class blah extends Activity{
private Activity activityReference;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityReference = this;
}
}
after that you could use that the way you want, i.e. in
Intent i = new Intent(activityReference, SomeOtherActivity.class)
etc
There are many ways for Activities communication.
you can use:
the startActivityForResult method
a system of broadcast message and receiver (you can broadcast an event from the actual activity, and register a receiver in the target activity. Remember that the target activity must be previously initialized and non finished)
as you say, store a reference of the target activity wherever you need.
We built a framework for this. We have a BaseActivity class that inherits from Activity and it overrides all the lifecycle methods and has some static (class) variables that keep track of the activity stack. If anything wants to know what the current activity is, it just calls a static method in BaseActivity that returns the activity on top of our privately-managed stack.
It is kinda hacky, but it works. I'm not sure I would recommend it though.
Handle the Intent in the class you want to do these methods, and send your information to it in a Bundle like so:
Intent i = new Intent("android.intent.action.MAIN");
i.setComponent(new ComponentName("com.my.pkg","com.my.pkg.myActivity"));
Bundle data = new Bundle();
i.putExtras(data);
startActivityForResult(i);
Then use an OnActivityResultListener to grab the new data.
I solved this by making a singleton class has an instance of the class below as a member.
public class InterActivityReferrer <T> {
HashMap<Integer, T> map;
ArrayList<Integer> reserve;
public InterActivityReferrer() {
map = new HashMap<>();
reserve = new ArrayList<>();
}
public synchronized int attach(T obj) {
int id;
if (reserve.isEmpty()) {
id = reserve.size();
}
else {
id = reserve.remove(reserve.size() - 1);
}
map.put(id, obj);
return id;
}
public synchronized T get(int id) {
return map.get(id);
}
public synchronized T detach(int id) {
T obj = map.remove(id);
if (obj != null) reserve.add(id);
return obj;
}
}
This class can get a T object and return a unique integer assigned to the object by attach(). Assigned integers will not collide with each other unless HashMap fails. Each assigned integer will be freed when its corresponding object is detached by detach(). Freed integers will be reused when a new object is attached.
And from a singleton class:
public class SomeSingleton {
...
private InterActivityReferrer<Activity> referrer = new InterActivityReferrer<>();
...
public InterActivityReferrer<Activity> getReferrer() {return referrer;}
}
And from an activity that needs to be referred:
...
int activityID = SomeSingleton.getInstance().getReferrer().attach(this);
...
Now with this, a unique integer corresponding to this activity instance is returned. And an integer can be delivered into another starting activity by using Intent and putExtra().
...
Intent i = new Intent(this, AnotherActivity.class);
i.putExtra("thisActivityID", activityID);
startActivityForResult(i, SOME_INTEGER);
...
And from the another activity:
...
id refereeID = getIntent().getIntExtra("thisActivityID", -1);
Activity referredActivity = SomeSingleton.getInstance().getReferrer().get(refereeID);
...
And finally the activity can be referred. And InterActivityReferrer can be used for any other class.
I hope this helps.
public static Activity getLaunchActivity()
{
final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
final Method methodApp = activityThreadClass.getMethod("currentApplication");
App = (Application) methodApp.invoke(null, (Object[]) null);
Intent launcherIntent = App.getPackageManager().getLaunchIntentForPackage(App.getPackageName());
launchActivityInfo = launcherIntent.resolveActivityInfo(App.getPackageManager(), 0);
Class<?> clazz;
try
{
clazz = Class.forName(launchActivityInfo.name);
if(clazz != null)
return Activity.class.cast(clazz.newInstance());
}
catch (Exception e)
{}
return null;
}
Just a guess since I haven't done this but it might work.
1) Get your applicationContext by making your Android Application class a Singleton.
2) Get your ActivityManager class from the context.
3) Get a list of RunningTaskInfos using getRunningTasks() on the ActivityManager.
4) Get the first RunningTaskInfo element from the list which should be the most recent task launched.
5) Call topActivity on that RunningTaskInfo which should return you the top activity on the activity stack for that task.
Now, this seems like a LOT more work than any of the other methods mentioned here, but you can probably encapsulate this in a static class and just call it whenever. It seems like it might be the only way to get the top activity on the stack without adding references to the activities.

Categories

Resources