In my Activity1, I have an AsyncTask that uploads to the server. Once this task is started, I want to start Activity, without waiting for the completion of AsyncTask. When the AsyncTask from Activity1 is completed, I want to update something in Activity2. After doing some searching, I've found multiple references/examples of using interfaces. But I ran into the following problem:
OnUploadCompleted Interface
public interface OnUploadCompleted {
void on UploadCompleted();
}
Activity2
public class Activity2 extends Activity implements OnUploadCompleted {
// all the usual activity code
#Override
public void onUploadCompleted() {
Toast.makeText(this, "Upload Done", ....
}
}
Activity1
public class Activity1 extends Activity {
// all the usual activity code
private class Upload extends AsyncTask<...> {
OnUploadCompleted listener;
public Upload(OnUploadCompleted listener) {
this.listener = listener;
}
// skipping doInBackground task
#Override
protected void onPostExecute(...) {
super.onPostExecute();
listener.onUploadCompleted();
}
}
void foo (...) {
OnUploadCompleted listener = new Activity2();
Upload upload = new Upload(listener);
upload.execute();
finish();
}
}
The problem I have is in the foo() function. the listener is a new instance of Activity2 class, but Activity2 hasn't been created yet. It will be created by the parent activity of Activity1, after the finish(). So, when the listener is actually called, the activity that it's "connected" to is null. In the onUploadCompleted(), when Toast is called, the "this" is null.
try sending Broadcasts to ACtivity2 from Activity1 when Activity1's AsyncTask completed...
public class MainActivity extends Activity {
public static final String ACTION_TASK_COMPLETED = "com.sample.project.action.ACTION_TASK_COMPLETED";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class DoTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// please wait. I am doing work
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// yay... work completed...
Intent intent = new Intent(ACTION_TASK_COMPLETED);
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);
}
}
}
public class SecondActivity extends Activity {
private TaskReceiver taskReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter intentFilter = new IntentFilter(MainActivity.ACTION_TASK_COMPLETED);
taskReceiver = new TaskReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(taskReceiver, intentFilter);
}
#Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(taskReceiver);
}
private void onUploadImage() {
// uploading completed...
}
private class TaskReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
onUploadImage();
}
}
}
Related
I have troubles on calling the method update from MainActivity class in a the MSG0100 non-activity class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void update(boolean msg100Preselection){
if(msg100Preselection){
mExpandableListViewAdapter.setSelectedChild(-1);
mExpandableListViewAdapter.notifyDataSetChanged();
}
}
}
And this is my class where i want to call the update method of Mainactivity.
public class MSG0100{
boolean msg100Preselection=false;
pulic void onUpdate(){
msg100Preselection=true;
// Want to call my update method here
MainActivity activity= new MainActivity();
activity.update(msg100Preselection); //<-------- Using mainactiviy object crashes my app.
}
}
What you want is impossible as you dont have a pointer to your main activity.
The following statement is invalid.
MainActivity activity= new MainActivity();
You are not allowed to use the new operator to create an activity. That should be done using an intent.
There are several things you could do:
Move your update method in another class
OR
declare your update method as static and use it like this:
MainActivity.update(msg100Preselection);
Try using a callbackListener :-
In your MSG0100 class
public class MSG0100 {
boolean msg100Preselection = false;
private static OnUpdateListener mListener;
public static setListener(OnUpdateListener mListener) {
this.mListener = mListener;
}
public void onUpdate() {
msg100Preselection = true;
if (mListener != null)
mListener.onUpdate(msg100Preselection);
}
public interface OnUpdateListener()
{
void onUpdate ( boolean msg100Preselection);
}
}
In your MainActivity-
public class MainActivity extends AppCompatActivity, OnUpdateListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MSG0100.setListener(this)
}
#Override
public void onUpdate(boolean msg100Preselection) {
if (msg100Preselection) {
mExpandableListViewAdapter.setSelectedChild(-1);
mExpandableListViewAdapter.notifyDataSetChanged();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
MSG0100.setListener(null)
}
}
This way you won't have any memory leaks or crashes due to Activity being killed.
I am working on Firebase Push Notification and i want to close MainActivity. Application should finish when onMessageReceived() is called. I am also passing the Context but its not working. In this case, I'll send notification when application is opend. My code:
MainActivity.java
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new FirebaseMessagingService(MainActivity.this);
}
}
FirebaseMessagingService.java
public class FirebaseMessagingService extends
com.google.firebase.messaging.FirebaseMessagingService {
Context context;
public FirebaseMessagingService(Context ctx) {
this.context = ctx;
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
context.finish();
}
}
You could define a BroadcastReceiver in MainActivity, that calls finish() when triggered:
private final BroadcastReceiver finishReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
Register/unregister it when appropriate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ...
LocalBroadcastManager.getInstance(getApplicationContext())
.registerReceiver(finishReceiver,
new IntentFilter(FirebaseMessagingService.ACTION_FINISH));
}
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(getApplicationContext())
.unregisterReceiver(finishReceiver);
super.onDestroy();
}
And then you just simply have to send a local broadcast from onMessageReceived():
public static final String ACTION_FINISH = "yourpackagename.ACTION_FINISH";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(new Intent(ACTION_FINISH));
}
(FirebaseMessagingService is a Context subclass, there is no need to pass another Context instance to it)
I'm trying to do the simple act of hiding/showing ProgressBar according to AsyncTask state ,
I have two classes one extends FragmentActivity and second AsyncTask.
MainActivity.java
public class MainActivity extends FragmentActivity {
public static ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
}
#Override
protected void onStart() {
super.onStart();
// What will happen to the progress bar here?
}
#Override
protected void onStop() {
super.onStop();
// What will happen to the progress bar here?
}
#Override
protected void onResume() {
super.onResume();
// What will happen to the progress bar here?
}
}
MyAsyncTask.java
public class MyAsyncTask extends AsyncTask<Void,Void, Void> {
#Override
protected Void doInBackground() {
// start download some images from cloud
// Here the progress bar should start to appear in MainActivity
// mProgressBar.setVisibility(View.VISIBLE);
}
#Override
protected void onPostExecute(Void result) {
Log.d(TAG, "Finished book downloading images the cloud");
// Here the progress bar should start to disappear in MainActivity
// mProgressBar.setVisibility(View.GONE);
}
}
main_activity.xml
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
Hope you understand me, thank to everyone who can help.
The progress bar should appear in onPreExecute() method (still in UI thread). Then you dismiss it when you get back to UI thread in onPostExecute method.
public class MyAsyncTask extends AsyncTask<Void,Void, Void> {
#Override
protected void onPreExecute(Void result) {
Log.d(TAG, "Finished book downloading images the cloud");
// Here the progress bar should start to disappear in MainActivity
mProgressBar.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground() {
// start download some images from cloud
// there is a time cost operation
}
#Override
protected void onPostExecute(Void result) {
Log.d(TAG, "Finished book downloading images the cloud");
// Here the progress bar should start to disappear in MainActivity
// mProgressBar.setVisibility(View.GONE);
}
}
You should use onPreExecute() method to show the dialog.
Here is MainActivity
#Override
protected void onResume() {
super.onResume();
new MyAsyncTask().execute();
}
I think you should read APIs first.
Use preExecute and postExecute methods as they run on UI thread.
public class MyAsyncTask extends AsyncTask<Void,Void, Void> {
ProgressBar pBar;
#Override
protected void onPreExecute(Void result) {
pBar=new ProgressBar(getContext());
pBar.show();
}
#Override
protected void onPostExecute(Void result) {
if(pBar !=null and pBar.isShowing()){
pBar.dismiss();
}
}
}
Use interface in Asyntask class for communicate with Activity class
public class MyAsyncTask extends AsyncTask<Void,Void,Void> {
//use context for activity reference
private Context context_;
public MyAsyncTask(Context context) {
this.context_=context;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(mCallBack!=null ){
mCallBack.onSuccess("Success");
}else {
mCallBack.onError("Error");
}
}
MyAsyncCallBack mCallBack=null;
public MyAsyncCallBack getmCallBack() {
return mCallBack;
}
public void setmCallBack(MyAsyncCallBack mCallBack) {
this.mCallBack = mCallBack;
}
public interface MyAsyncCallBack{
public void onSuccess(String successMessage);
public void onError(String successMessage);
}
}
Call AsynckTask Class from activity class.Before calling asyntask start progress. and after completing work in asyntask return activity via interface and hide progress.
startProgress();
MyAsyncTask mTask=new MyAsyncTask(YourActivity.this);
mTask.setmCallBack(new MyAsyncTask.MyAsyncCallBack() {
#Override
public void onSuccess(String successMessage) {
//do success work and hide progress
hideProgress();
}
#Override
public void onError(String successMessage) {
//do error work and hide progress
hideProgress();
}
});
mTask.execute();
I have service, which gets data from API and sends this data to BroadcastReceiver class. Also, I create interface OnReceiveListener, which used in Activity. Look at the code here:
Activity:
public class StartActivity extends AppCompatActivity
implements MyBroadcastReceiver.OnReceiveListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
receiver.setOnReceiveListener(this);
LocalBroadcastManager.getInstance(this).registerReceiver(receiver,
new IntentFilter(MyBroadcastReceiver.START));
...
}
#Override
public void onReceive(Intent intent) {
// Do smth here
}
}
MyBroadcastReceiver:
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String START = "com.example.myapp.START";
public static final String GET_LINKS = "com.example.myapp.GET_LINKS";
private OnReceiveListener onReceiveListener = null;
public interface OnReceiveListener {
void onReceive(Intent intent);
}
public void setOnReceiveListener(Context context) {
this.onReceiveListener = (OnReceiveListener) context;
}
#Override
public void onReceive(Context context, Intent intent) {
if(onReceiveListener != null) {
onReceiveListener.onReceive(intent);
}
}
}
Service isn't important on this question.
---- Question ----
So, what's problem: I want to use this receiver in fragment, but when it sets context - I get exception "enable to cast". What I should to do on this case?
Here is my code in fragment:
public class MainFragment extends Fragment
implements MyBroadcastReceiver.OnReceiveListener {
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
myBroadcastReceiver.setOnReceiveListener(getContext());
LocalBroadcastManager.getInstance(getContext()).registerReceiver(myBroadcastReceiver,
new IntentFilter(MyBroadcastReceiver.GET_LINKS));
}
#Override
public void onReceive(Intent intent) {
// Do smth here
}
}
Your MainFragment class implements your OnReceiveListener interface, not its Context as returned by getContext(). Instead of passing a Context object into setOnReceiveListener(), try directly passing an OnReceiveListener instance. Then your fragment and activity can both call setOnReceiveListener(this).
you don't need to dynamically register the receiver. i believe you must have registered it in manifest using <receiver> tag.
this is not required:
LocalBroadcastManager.getInstance(getContext()).registerReceiver(myBroadcastReceiver,
new IntentFilter(MyBroadcastReceiver.GET_LINKS));
and about callback registering listener, instead of using getContext() use MainFragment.this like this:
myBroadcastReceiver.setOnReceiveListener(MainFragment.this);
After searching for hours for the appropriate way to implement such a solution to this problem, I've found a way finally. It is based on RussHWolf's answer. The complete solution with code is below:
In this way, a setListener() method is exposed so that Fragment or Activity can set the listener by sending an instance of IStatusChangeListener.
public class StatusChangeReceiver extends BroadcastReceiver {
private IStatusChangeListener listener;
public void setListener(IStatusChangeListener listener) {
this.listener = listener;
}
#Override
public void onReceive(Context context, Intent intent) {
if (NetworkUtil.isNetworkConnected()) {
listener.onConnected();
} else {
listener.onDisconnected();
}
}
}
This is the interface:
public interface IStatusChangeListener {
void onConnected(String status);
void onDisonnected(String status);
}
Now, it is required to have an instance of IStatusChangeListener interface instead of implementing the IStatusChangeListener interface. And then, pass this instance of IStatusChangeListener to setListener() method.
public class MainFragment extends Fragment { //Not implementing the interface
private IStatusChangeListener listener = new IStatusChangeListener() {
#Override
void onConnected(String status) {
//some log here
}
#Override
void onDisonnected(String status) {
//some log here
}
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
StatusChangeReceiver r = new StatusChangeReceiver();
r.setListener(listener); // pass the IStatusChangeListener instance
LocalBroadcastManager.getInstance(getContext()).registerReceiver(r, new IntentFilter("connectionStatus"));
}
}
Note: Always use LocalBroadcastManager if you register BroadcastReceiver from Fragment.
I have an AsyncTask which shows a ProgressDialog. The AsyncTask is started when the activity is started:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_layout);
new MyTask().execute();
}
// ... other code
private class MyTask extends AsyncTask<Void, Void, Void>{
private ProgressDialog dialog = new ProgressDialog(MyActivity.this);
#Override
protected void onPreExecute() {
dialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
// get data from a server
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
// call to a method in MyActivity which updates the UI.
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
}
This code works perfectly, untill I rotate my screen. Which makes sense, because the context that was used to create the dialog doesn't exist anymore (because the activity is re-created when rotating), and a window leak is caused.
The only solution I could think of isn't a really nice one: create a static instance of the task and dialog, and simply dismiss the dialog when the activity is destroyed, and recreate the dialog in the oncreate method if the task is still running.
So how would I solve something like this without losing functionality (so the dialog must always be shown when the task is running, and rotating the device should be allowed)?
As Raghunandan suggested in his comment, I looked into Fragments and solved my problem.
I created a Fragment which starts my AsyncTask, as explained in the blogpost that Raghunandan provided.
And to make sure that my Dialog didn't get leaked, I created a DialogFragment, as described here (Basic Dialog).
Here's my working code:
My Activity:
public class MyActivity extends Activity implements MyTaskFragment.TaskCallbacks {
private MyTaskFragment task;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_layout);
FragmentManager fm = getFragmentManager();
task = (MyTaskFragment) fm.findFragmentByTag("myTask");
if (task == null) {
task = new MyTaskFragment();
fm.beginTransaction().add(task, "myTask").commit();
}
}
#Override
public void onPreExecute() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("myDialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
StringProgressDialogFragment dialog = StringProgressDialogFragment.newInstance("My message");
dialog.show(ft, "myDialog");
}
#Override
public void onPostExecute() {
StringProgressDialogFragment dialog = (StringProgressDialogFragment) getFragmentManager().findFragmentByTag("myDialog");
if (dialog!=null) {
dialog.dismiss();
}
// update UI
}
// ... other code
}
My Task fragment:
public class MyTaskFragment extends Fragment {
private TaskCallbacks mCallbacks;
private Task mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
// Create and execute the background task.
mTask = new Task();
mTask.execute();
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class Task extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
mCallbacks.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
// do stuff
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
mCallbacks.onPostExecute();
}
}
public static interface TaskCallbacks {
void onPreExecute();
void onPostExecute();
}
}
My Dialog fragment:
public class StringProgressDialogFragment extends DialogFragment {
private String message;
public static StringProgressDialogFragment newInstance(String message) {
StringProgressDialogFragment dialog = new StringProgressDialogFragment();
Bundle args = new Bundle();
args.putString("message", message);
dialog.setArguments(args);
return dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog dialog = new ProgressDialog(getActivity());
message = getArguments().getString("message");
dialog.setMessage(message);
return dialog;
}
}
New Loaders API can help you (available via support package) - man. They will solve problem with rotation, but not a mem. leak. To solve mem. leaks write your own "AsyncTask" (with a "clearContext" routine) and clear it's context in activity's onDestroy (or onPause, depends on your architecture). It may looks like a bicycle, but the task takes max 1 day, and you will have a full control on all the resources you background worker use.
By the way: consider using dialogs through fragments, because it solves dialog kill on screen rotation.
try with sample. it will work. basically just restrict the oncreate call by handling the config change. this solution may help you.
public class MainActivity extends Activity {
LoadProgrssdata task = new LoadProgrssdata();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, "oncreate called", Toast.LENGTH_SHORT).show();
task.execute();
}
public class LoadProgrssdata extends AsyncTask<Void, Void, Void> {
ProgressDialog progressDialog;
//declare other objects as per your need
#Override
protected void onPreExecute()
{
progressDialog= ProgressDialog.show(MainActivity.this, "Progress Dialog Title Text","Process Description Text", true);
//do initialization of required objects objects here
};
#Override
protected Void doInBackground(Void... params)
{
//do loading operation here
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
progressDialog.dismiss();
};
}
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.e("orientation ", "landscape");
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Log.e("orientation ", "portrait");
}
}
}
and in android manifest file:
<activity
android:name="com.example.MainActivity"
android:label="#string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize" />
I managed to fix this problem by trying to catch any crash that, may occurs, in doInBackground.