I've 2 activities(LoginActivity and VitalListActivity) and a class(PostLoginData) in another package. From LoginActivity I'm calling PostLoginData which extends AsynchTask. I'm trying to create intent in the onPostExecute() of the AsyncTask.
Here's the code:
protected void onPostExecute(String result) {
Log.d("ON POST EXE", "Success");
Intent intent = new Intent(LoginActivity.context, VitalListActivity.class);
Log.d("INTENT STARTED","SUCCESS");
try {
JSONObject json = new JSONObject(result);
String status = json.getString("status");
intent.putExtra(LoginActivity.EXTRA_MESSAGE, status);
Log.d("PUT MESSAGE", "Success");
LoginActivity.context.startActivity(intent);
} catch (JSONException e) {
e.printStackTrace();
}
}
It has no error, but when I run the app stops working. It has problem with the intent creation. So, how can I create an Intent from Asynch task of another class.
you need to inject the Context into the AsyncTask something like:
class MyTask extends AsyncTask<...> {
Context context;
MyTask(Context context){
this.context = context;
}
}
Then you can just use the context to start the Service.
You should consider using ApplicationContext and not Activity in that case
By the way i have no idea how LoginActivity.context does compile for you. the Activity class does not have a static Context unless you defined one by yourself which is not a very good idea
You have to create one constructor in PostLoginData class like below and passed LoginActivity Context on it.
public class PostLoginData extends AsyncTask<...>
{
Context m_context;
public PostLoginData(LoginActivity activity)
{
m_context = activity;
}
}
Replace below line in onPostExecute() method
Intent intent = new Intent(LoginActivity.context, VitalListActivity.class);
To
Intent intent = new Intent(m_context, VitalListActivity.class);
Related
I need to download elements from internet and add them to an arraylist in the background. (The download may take a few minutes.)
There is a loop in which part of overall elements are downloaded each iteration and added to the list. I need different activities be able to have access to that arraylist whenever needed, no matter if the download (the loop) is in progress or finished.
It seems a service can do this, but i don't have any idea on how. Considering the code below, how can i achieve this?
class A extends Service {
void foo(){
//uses a loop to get elements from internet
//then adds the elements to myArraylist in each loop
}
}
class B extends Activity {
//needs to have access to myArraylist asynchronously
}
class C extends Activity {
//needs to have access to myArraylist asynchronously
}
Note that i need the download process stay active when user switches between activities.
You can do it by Broadcast receiver.For send the data on other activity you can use:
intent = new Intent(ApplicationSetting.NEW_MESSAGE_ACTION);
intent.putExtra(IMMessage.IMMESSAGE_KEY, msg);
sendBroadcast(intent);
For receive this message for other any activity you can use this code:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
/*
* For before commit
*/
if (ApplicationSetting.NEW_MESSAGE_ACTION.equals(action)) {
IMMessage message = intent
.getParcelableExtra(IMMessage.IMMESSAGE_KEY);
Log.w("message", "are" + message);
}
}
};
So the problem you face with what you are asking is that your download loop may be adding to or changing the list while the active activity may also be accessing the same list. This can cause a ConcurrentModificationException. To avoid this what you need to do is synchronise all activity with the list. In order to make it available to all activities and have it accessible to your service I would suggest that the list itself is stored in your application (a class extending Application)
public class MyApplication extends Application {
private List<MyElement> mElems;
#Override
public void onCreate() {
super.onCreate();
mElems = Collections.synchronizedList(new ArrayList<MyElement>());
//this line will start your download service, available accross the whole app
startService(new Intent(getApplicationContext(), A.class));
}
//You can use accessor methods and keep the list private to ensure
//synchronisation doesn't get missed anywhere
public void synchronisedAddElement(MyElement elem) {
mElems.add(elem); //already synchronous in this case
}
//I havent tested this method, you method below may be safer
public Iterator getElementsIteratorSynchronised() {
synchronized(mElems) {
return list.iterator();
}
}
public Iterator iterateElementsSynchronised(OnElementListener lis) {
synchronized(mElems) {
Iterator<MyElement> i = list.iterator();
if (lis != null) {
while (l.hasNext()) {
lis.onElement(l.next());
}
}
}
}
public static class OnElementListener {
public void onElement(MyElement el);
}
}
You would write to it as follows
class A extends Service {
void foo(){
MyApplication app = (MyApplication) getApplication();
... //do your network call loop here, adding to local list
app.synchronisedAddElement( myNewElement );
}
}
And Read
class B extends Activity {
//the async task just because your comment said async access
new AsynTask<MyApplication, Void, Void>() {
public Void doInBackground(MyApplication app) {
app.iterateElementsSynchronised(new OnElementListener() {
public void onElement(MyElement el) {
Log.d(TAG, "Did somethign appropriate with " + el);
}
})
}
}.execute( (MyApplication) getApplication() );
}
Please just treat this as pseudo code, I've written it on the train home so the method signatures may vary, but this should get you where you need to be
Using the structure recommended by Nick Cardoso but with many changes to meet my case, i managed to solve the problem. here it is:
class A extends Service {
ArrayList arrayList = new ArrayList();
MyApplication app;
void foo(){
new Thread (new Runnable (){
#Override
public void run() {
app = (MyApplication)getApplication();
While(true){
//get elements from network and put them in arrayList
app.synchronisedAddCollection(arrayList);
LocalBroadcastManager.getInstance(this).sendBroadcast(mediaIntent);
}
}
}).start();
}
}
And here is my Application class:
public class MyApplication extends Application {
List<HashMap<String, String>> myArrayList = new ArrayList<HashMap<String, String>>();
#Override
public void onCreate() {
super.onCreate();
}
public void synchronisedAddCollection(ArrayList<HashMap<String, String>> arrayList) {
myArrayList.addAll(arrayList);
}
public ArrayList<HashMap<String, String>> getArrayList(){
return (ArrayList<HashMap<String, String>>) myArrayList;
}
}
Here is the activity which needs to access the shared arraylist
class B extends Activity {
MyApplication app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(new Intent(getApplicationContext(), MyService.class);
LocalBroadcastManager.getInstance(this).registerReceiver(lbr,
new IntentFilter("mediaIntent"));
}
private BroadcastReceiver lbr = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
app = (MyApplication)getApplication();
//now i have access to the app arrayList
System.out.println(app.myArrayList.size());
}
}
};
}
Do not forget to register MyApplication and MyService in manifest.
I have got a Fragment Activity that contains a textview and another class that extends AsyncTask. Now I would like to use the onPostExecute(String result) method to set the result text in my textview that is in my fragment activity.
How can I do that? I already created a custom constructor for the AsyncTask class that takes in a context object. How can I use that??
This is how I create a task object in my Fragment activity:
String query = "someText";
Task task = new Task(this.getActivity());
task.execute(query);
This is a snippet from my task class:
public class Task extends AsyncTask<String, Void, String> {
private Context context;
public Task (Context context) {
this.context = context;
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
// ??? What comes here ???
}
}
TextView txt = (TextView)((Activity)context).findViewById(R.id.watheveryouwant);
txt.setText("blabla");
But you should pass an Activity and not a Context, will be easier ;-)
Or
public Task (Context context, TextView t) {
this.context = context;
this.t = t;
}
super.onPostExecute(result);
t.setText("BlahBlah")
}
Should do the trick
You can pass to the AsynkTast the instance of the TextView as a parameter, and call setText in onPostExecute.
in your case, simply following will come here:
((TextView)findViewById(R.id.xyz)).setText("abc");
I picked the solution from
How do I return a boolean from AsyncTask?
new Task(getActivity()).execute(query);
In the Constructor of AsyncTask
TheInterface listener;
public Task(Context context)
{
listener = (TheInterface) context;
}
Interface
public interface TheInterface {
public void theMethod(String result); // your result type
}
Then
In your doInbackground return the result.
In your onPostExecute
if (listener != null)
{
listener.theMethod(result); // result is the String
// result returned in doInbackground
// result of doInbackground computation is a parameter to onPostExecute
}
In your activity class or fragment implement the interface
public class ActivityName implements Task.TheInterface
Then
#Override
public void theMethodString result) {
tv.setText(result);
// set the text to textview here with the result of background computation
// remember to declare textview as a class member.
}
Edit:
You are also missing #Override annotation for your onPostExecute
I am trying to pass the Context from an activity to a AsyncTask class. The problem is that the context is null.
In my AsyncTask class, I have the following
public class LoginService extends AsyncTask<String, Void, String> {
....
public Context context;
public LoginService(){
}
public LoginService(String username, String password){
this.username=username;
this.password=password;
}
#Override
protected String doInBackground(String... params) {
String userID = login(username, password);
return userID;
}
protected void onPostExecute(String result){
loginSuccess = result;
if (loginSuccess!=fail){
Intent casesActivity = new Intent(context, CasesActivity.class);
casesActivity.putExtra("username", result);
context.startActivity(casesActivity);
}
public void setContext(Context newContext){
context = newContext;
}
And in my activity, when I click a button, I have the following code:
public void onClick(View view) {
if ((editTextPassword.getText().toString() != null & editTextUsername.getText().toString() != null)){
new LoginService().setContext(getApplicationContext());
new LoginService(editTextUsername.getText().toString(), editTextPassword.getText().toString()).execute();
}
else{
//Display Toaster for error
Toast.makeText(getApplicationContext(),"Please enter your details", Toast.LENGTH_LONG).show();
}
}
The intent is never created and the application crashes abruptly because the context is null. I cannot seem to find the solution for this issue.
For a minimal code change, keep a reference to your LoginTask instead of creating two separate instances:
LoginService l = new LoginService(editTextUsername.getText().toString(), editTextPassword.getText().toString());
l.setContext (getApplicationContext());
l.execute();
Your code creates a new LoginTask every time, and since you have two separate constructors, context is always null (the second constructor doesn't hold the context, since you created a separate Object!)
However, if you want a Context passed off along with the user creds, take out the no argument constructor and change the remaining one so it looks like:
public LoginService(Context context,String username, String password){
this.context = context;
this.username=username;
this.password=password;
}
Having empty constructors that do nothing is usually pointless, so adjust your useful constructor so it is even more useful.
I'm logging to bank account and getting account balance.
I calling this function from onUpdate in widget and running in AsyncTask
package com.example.oobe.widget.widgetexample;
public class ExampleAppWidgetProvider extends AppWidgetProvider
{
(...)
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
(...)
if (notFromAsyncTask)
new MyAsyncTask().execute(context);
(...)
}
(...)
}
In method onPostExecute I want to call onUpdate widget and putExtra strings.
How can I do this?
package com.example.oobe.widget.widgetexample;
public class MyAsyncTask extends AsyncTask<Object, Void, BGZ>
{
Context context;
#Override
protected BGZ doInBackground(Object... params)
{
this.context = (Context)params[0];
return GetSomething();
}
protected void onPostExecute(BGZ page)
{
Intent intent = new Intent(context, ExampleAppWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra("result", result);
intent.putExtra("webpage", webPage);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
(...)
}
If I doing like above I'm getting error:
.. Unable to find explicit activity class..
Can I do this from AsyncTask?
Can I call widget_update (onUpdate) with params to recognize that is from my AsyncTask?
Please give me little sample code (what to add to manifest if it must be broadcastreceiver and so on).
I have updated widget in onPostExecute but I think better method is to do that in ExampleAppWidgetProvider class?
If you are getting this error
Unable to find explicit activity class..
It may be due to this line
Intent intent = new Intent(context, ExampleAppWidgetProvider.class);
make sure ExampleAppWidgetProvider.class exist and
Context context = Activity.this;
or
Intent intent = new Intent(Activity.this, ExampleAppWidgetProvider.class);
UPDATE
public class ExampleAppWidgetProvider extends AppWidgetProvider
{
//your code .......
//asynctask as an inner class
public class MyAsyncTask extends AsyncTask<Object, Void, BGZ>
{
doInBackground(){}
onPreExecute(){}
onPostExecute(){
//save result
// or call your methods you need to run after your async call
}
}
}
How to call an activity from another class to a non-Activity class?
My code is as follows (Activity Class)
public void onCreate(Bundle savedInstanceState){super.onCreateSavedInstanceState);
this.mp();
}
public MediaPlayer mp(){//insert method here// }
Then in my non activity class i call
Intent i = new Intent();
i.setClassName(".......process", ".....ActualRenderingMedia");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
however if I try to use context.startActivity it will give an error asking me to create the activity method. I can't use getApplicationContext.startActivity either.
Is your non-Activity class instantiable? If so, you can add a constructor to the class that accepts a Context Object, and instantiate it from your main Activity.
For example, in you non-Activity class:
public class MyClass {
Context context;
public MyClass(Context context) {
this.context = context;
}
public void someOtherMethod() {
Intent i = new Intent(...);
context.startActivity(i);
}
}
And in your main Activity:
MyClass myclass = new MyClass(this);
...
myclass.someOtherMethod();