Calling function of current running activity - java

Lets say I have multiple running Activities; A, B, and C. Each share a similar optionsmenu with some differences in their execution ("start" option might be slightly different in Activity A than B). I also have a static class called "values" that is tied to activity A. It also has the context of the current running activity.
At times values may call an item in the optionsmenu of the current running activity. My code is messy(see below) so I would like to organize it to a more readable form.
My goal is to set up values so that it can call just a function of the current running activity instead of an optionmenu item of that activity. Inside the activity an item of the optionsmenu would just call a function instead of code inside of it (for organization reasons).
Here a sample of values.class calling the item of an optionsmenu of the current running activity.
public void startExample() {
Runnable startRun = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the
// UI
#Override
public void run() {
((Activity) getCurrentContext()).openOptionsMenu();
((Activity) getCurrentContext()).closeOptionsMenu();
((Activity) getCurrentContext()).onOptionsItemSelected(theMenu.findItem(R.id.start));
}
});
}
};
new Thread(startRun).start();
}
As you can see values.startExample() calles the start item of the options menu of the current running activity. I would like to change this so that it calls a function based off of the current running activity instead. So I was thinking that I could do something like this instead.
In values.class
ActivityB b = new ActivityB
public void startExample() {
Runnable startRun = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the
// UI
#Override
public void run() {
if( ((Activity) getCurrentContext()).getClass().getName().equals("package.ActivityB") ) {
b.start();
}
}
});
}
};
new Thread(startRun).start();
}
And Activity B might look like.
public class ActivityB extends Activity {
//class code here
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case start:
this.start();
break;
}
}
public void start() {
//code here
}
}
Is this possible? I know that this question might sound confusing so please ask questions and I might be able to simplify it again.

No no no no no. This is like pulling a crowbar to Android and just smashing it to pieces. Activities are not ever meant to be instantiated via new.
If all you want is for there to be some shared behavior between the methods that the different Activities call in their onOptionsItemSelected(), then either create a super activity and call the super before you add whatever behavior you want in the particular subclasses or else create a function accessible to all activities that contains what doesn't change and then add what does change inside of the subclass. Either way, Activities are meant to be ran via providing a reference to the class in startActivity, etc. You can't treat an Activity like an ordinary java object (even though it ultimately is) because it has a lifecycle that is carefully managed by the system.

Related

ViewHolder from Adapter from Fragment starts Activity, how can the Activity talk back to the Fragment?

PlaylistFragment starts an adapter:
playlistsAdapter = new PlaylistRecyclerAdapter(playlistsListArray, addToPlaylist, mSong, getActivity(), this);
PlaylistRecyclerAdapter binds data to the PlaylistViewHolder, something like this:
((PlaylistViewHolder) viewHolder).bind(this, dataSet.get(position), addToPlaylist, mSong);
User clicks on an item in PlaylistViewHolder:
context.startActivity(PublicPlaylistActivity.createStartIntent(context, playlist));
Now here is the question, how can PublicPlaylistActivity talk back to the initial PlaylistFragment?
I suggest you'd better use Interface from fragment to adapter. So when user clicks anything in adapter, call function realization in fragment. If you need your activity to proceed some operation - ((YourActivity) getActivity()).someMethod() should be called from fragment.
Second trick is using broadcastreceiver to send events. A bit more complicated. You have to launch broadcast in view you need to recive message and send these messages from adapter. This approach is more complexible to debug and support if system is wide spread, so you'd better use interfaces.
There are several ways of doing that. The simplest way should be starting the PublicPlaylistActivity with startActivityForResult. In that way, then the activity finishes, you can set send some data to the caller fragment (which is PlaylistFragment in your case). Here is a nice tutorial about the implementation.
Another way of doing that is by using lifecycle methods. You might have a public static variable which can keep track of some status that you might observe in your onResume function of your PlaylistFragment when you are returning back from your PublicPlaylistActivity. You might consider a sample implementation as follows.
Define a public static variable in your PlaylistFragment. Then in your onResume function check the value of that variable and take actions accordingly.
public static boolean someIndicator = false; // Initialize with a default value
#Override
protected void onResume() {
super.onResume();
if(someIndicator == true) doSomething();
else doSomethingElse();
}
Now you can set the indicator variable from anywhere in your application actually which will have the effect on your PlaylistFragment. For example, from your PublicPlaylistActivity, you might consider doing something like this.
public void someFunctionInYourPublicPlaylistActivity() {
// ...
// Some code and then the following
PlaylistFragment.someIndicator = true;
}
Another way of achieving the same thing is by using a BroadcastReceiver. Here is a tutorial on how you can implement one.
It really depends on how you are structuring your whole activity-fragments communication. Hope that helps!
I would do a common "context" class (ComContext) with an interface. When you create your fragment, you also create this class. And from the activity you can check if it exists or not.
I assume that you already have a helper(AppHelper) class with static variables.
public class AppHelper {
public static ComContext comContext = null;
}
public class MainFragment {
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ConContext comContext = new ComContext();
comContext.listener = this;
AppHelper.comContext = comContext;
}
#Override
public void onDataChanged() {
}
#Override
public void onDestroyView() {
super.onDestroyView();
AppHelper.comContext = null;
}
}
public class MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppHelper.comContext != null) {
AppHelper.comContext.listener.onDataChanged();
}
}
}
public class ComContext {
public interface HelperListener {
void onDataChanged();
}
public HelperListener listener = null;
}

Listeners observer design pattern with Handler

I was dealing recently with a question I'm not sure how to answer.
I wrote a code example for some AsyncTask that I want to perform. I read somewhere on the net that someone has implemented the AsyncTask and the Handler as inner classes and I wanted to scale that a little bit and make less coupling so I made separated class for those so I can reuse them with more than one Activity.
Because I had to do some different UI things on each Activity I decided to make those activities implement an interface so I can react to each event with same methods.
What I don't understand is why do I need the handler object that will handle the messaging for event occurrence? can't I just use the listeners observer pattern? and then the question that I asked my self and can't understand the answers around the web is what is the difference between my listener observer implementation and the handler object we get from Android.
Here is my code example:
Activity 1:
public class SomeActivity extends Activity implements MyListener{
MyAsyncTask myTask;
MyHandler handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new MyHandler();
myTask = new MyAsyncTask(handler);
// initilize the activity views etc...
}
#Override
public void do1(){
// DO UI THINGS FOR ACTIVITY 1 IN A CALLBACK TO DO1 EVENT
}
#Override
public void do2(){
// DO UI THINGS FOR ACTIVITY 1 IN A CALLBACK TO DO2 EVENT
}
}
Activity 2:
public class OtherActivity extends Activity implements MyListener{
MyAsyncTask myTask;
MyHandler handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new MyHandler();
myTask = new MyAsyncTask(handler);
// initilize the activity views etc...
}
#Override
public void do1(){
// DO UI THINGS FOR ACTIVITY 2 IN A CALLBACK TO DO1 EVENT
}
#Override
public void do2(){
// DO UI THINGS FOR ACTIVITY 2 IN A CALLBACK TO DO2 EVENT
}
}
Listener interface:
public interface MyListener{
void do1();
void do2();
}
AsyncTask implementation:
public class MyAsyncTask extends AsyncTask<Void,Void,String>{
private MyModel m;
public MyAsyncTask(Handler h){
m = new MyModel();
m.setHandler(h);
}
protected String doInBackground(Void... params) {
// do something in background with MyModel m
return null;
}
}
Handler implementation:
public class MyHandler extends Handler {
Vector<MyListener> listeners = new Vector<>();
#Override
public void handleMessage(Message msg) {
switch(msg.what){
case 1:
// do something for case 1
fireMethod1();
break;
case 2:
// do something for case 2
fireMethod2();
break;
}
}
public void registerListener(MyListener l){
listeners.add(l);
}
public void unregisterListener(MyListener l){
listeners.remove(l);
}
private void fireMethod1(){
for(MyListener l : listeners){
l.do1();
}
}
private void fireMethod2(){
for(MyListener l : listeners){
l.do2();
}
}
}
Some demo model I created:
public class MyModel{
private Handel h;
public MyModel(){
// at some point send message 1 or message 2 ...
}
public void setHandler(Handler h){
this.h = h;
}
private void sendMessage1(){
h.obtainMessage(1, null);
}
private void sendMessage2(){
h.obtainMessage(2, null);
}
}
if it is too hard to read the code let me know, and if you don't want to read the code please help me to answer what is the difference between Handler and listening to events with the observer pattern? are they pretty much different solutions for same problem? thanks!
what is the difference between Handler and listening to events with the observer pattern?
The difference is that when you use a listener you call a method synchronously on the same thread. When you use a Handler you synchronously add a message to the MessageQueue but it is handled only after those messages that are already in the queue.
For example, if you are using a UI handler and you already called finish() on the activity and then added your message, it will be inserted after onStop() and onDestroy(). You can't achieve this with a listener.
The advantage of handlers is that you just add messages to queues and you don't care about threading. You can easily add a message to the UI handler from the background thread. If you use a listener from the background thread, it will be called on a background thread synchronously.
are they pretty much different solutions for same problem?
No, they are not. Handlers help you to decouple android components which is critical for Android, I think. If you use listeners you will be relying on strong references only which in some cases is not possible because you might leak a memory.
Handler is UI-threaded component. Usage of simple listener may cause CalledFromWrongThreadException if you want to touch some UI.
AsyncTask although have onPreExecute, onPostExecute and onProgressUpdate, which are just methods, which are running on UI thread. doInBackground runs on separate thread

Call method from another object-java

I'm doing some Android development and I have an object, which doing a specific task. When that task is done I need to inform my main method (Main Activity), which is constantly running, that the process has been finished and pass some information about it.
This may sound a bit unclear, so I'll give you an example:
Take a look at the setOnClickListener() method in Android:
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
//This method is called on click
#Override
public void onClick(View v) {
//The View is passed in an anonymous inner class
}
});
It waits for a button to be clicked and calls the onClick(View v) method. I am seeking to achieve the same code structure. How to do this?
You mentioned "process". If you are truly doing something in a different process, then you need to look at interprocess communications (IPC). Otherwise, you can use an interface:
Create a class called MyListener:
public interface MyListener {
void onComplete();
}
In your class that will notify your activity:
MyListener myListener;
public void setMyListener(MyListener myListener){
this.myListener = myListener;
}
Then, when you are ready to notify your main activity, call this line:
myListener.onComplete();
Last, in your MainActivity implement MyListener:
public class MyListener extends Activity implements MyListener {
///other stuff
#Override
public void onComplete(){
// here you are notified when onComplete it called
}
}
Hope this helps. Cheers.
This is exactly Listener pattern that you use with views in android. What you want to do is declare an interface in your class that's doing the job, and pass an instance of this interface. Raw example:
TaskDoer.java:
public class TaskDoer {
public interface OnTaskDoneListener {
void onDone(Data data);
}
public void doTask(OnTaskDoneListener listener) {
// do task...
listener.onDone(data);
}
}
Activity:
public void doTaskAndGetResult() {
new TaskDoer().doTask(new TaskDoer.OnTaskDoneListener() {
public void onDone(Data data) {
// do something
}
}
}

Adding nested views dynamically freezes the App

I am building the app, which generates and adds view dynamically. I don't know in advance what views I need to create, these can be nested layouts or simple labels, etc, depending what comes back from web services.
Everything has been well so far until I started building really complex nested layouts .I have one case where I need to add about 11 levels of Layouts dynamically. When activity starts I display ProgressDialog(ring), while views are being generated. My problem is that with this complex structure ProgressDialog freezes while views are added. This is the code, which creates the view:
private class ViewCreator implements Runnable {
public BackgroundTaskViewCreatedResponse delegate;
private View mCreatedView;
private ComponentDefinition mComponent;
private ViewCreator(ComponentDefinition component){
this.mComponent = component;
}
#Override
public void run() {
try {
if (mComponent != null){
mComponent.setLinkedData(model.getLinkedData());
mCreatedView = componentCreator.createComponent(mComponent);
}
} finally {
if (mCreatedView != null)
delegate.processFinishTask(mCreatedView);
}
}
}
Layout, which has other views in it implements BackgroundTaskViewCreatedResponse, so, when view is ready, it will be added:
#Override
public void processFinishTask(final View createdView) {
//((Activity)view.getContext()).runOnUiThread(new Runnable(){
mView.post(new Runnable(){
#Override
public void run() {
mView.addView(createdView);
}
});
}
As you can see above, I have tried to call runOnUiThread call, but this blocks the UI thread completely while view hierarchy is being generated. At the same time view.post doesn't get called out of the box, so I have made some changes to views as suggested in this SO answer. So, now my views are added, but my ProgressDialog is not running smoothly. It stops in a few occasions and then resumes. I've also tried using Android AsyncTask, but that gives the same effect as runOnUiThread
I am not very experienced with Threads, have been trying to fix this for a few days now. Please help.
You can use AsyncTask to do this/ Here is an example:
private class GenerateViews extends AsyncTask<Void,Void,Void>{
#Override
protected void onPreExecute() {
// SHOW THE SPINNER WHILE GENERATING VIEWS
spinner.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... params) {
//CALL YOUR VIEW GENERATING METHOD HERE
return null;
}
#Override
protected void onPostExecute(Void result){
spinner.setVisibility(View.INVISIBLE);
}
}
You can make this class inside your class, if you want to. And then, you just call
new GenerateCalls.execute();

Call a method when fragment is visible to user

I need execute a method when the fragment is visible (to the user).
Example:
I have 2 buttons (button 1 and button 2) ,
2 fragments(fragment 1 and fragment 2)
and the method loadImages() inside the class fragment 2.
when I press "button2" I want to replace fragment 1 by fragment 2
and then after the fragment 2 is visible (to the user) call loadImages().
I tried to use onResume() in the fragment class but it calls the method before the fragment is visible and it makes some delay to the transition.
I tried setUserVisibleHint() too and did not work.
A good example is the Instagram app. when you click on profile it loads the profile activity first and then import all the images.
I hope someone can help me. I will appreciate your help so much. Thank you.
Use the ViewTreeObserver callbacks:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
final View view = v;
// Add a callback to be invoked when the view is drawn
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Immediately detach the listener so it only is called once
view.getViewTreeObserver().removeOnDrawListener(this);
// You're visible! Do your stuff.
loadImages();
}
});
}
I'm a little confused by what you are trying to do. It sounds like the images are loading too fast for you... so does that mean that you have the images ready to display? And that is a bad thing?
My guess (and this is just a guess) is that Instagram does not have the profile pictures in memory, so they have to make an API call to retrieve them, which is why they show up on a delay. If the same is the case for you, consider starting an AsyncTask in the onResume method of the fragment. Do whatever loading you need to do for the images in the background, and then make the images appear in the onPostExecute callback on the main thread. Make sure you only start the task if the images are not already loaded.
However, if you already have the images loaded in memory, and you just want a delay before they appear to the user, then you can do a postDelayed method on Handler. Something like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
Edit
As kcoppock points out, the handler code is pretty bad. I meant it to be a quick example, but it is so wrong I should not have included it in the first place. A more complete answer would be:
private Handler handler;
public void onResume(){
super.onResume();
if(handler == null){
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
}
}
public void onDestroyView(){
super.onDestroyView();
handler.removeCallbacksAndMessages(null);
handler = null;
}
Use the onActivityCreated() callBck

Categories

Resources