I need to set the String value generated by from AsynTask's OnPostExecute() method to Another Activity's Textview for that I have used following two methods, but both methods fails.
Method 1: By using GetterSetter Class
I am fairly new to android. I am making an app in which i stuck to this particular problem. I am running asyncTask from BackgroundTask class and BackgroundTask is running from StringGenerator class. In this AsyncTask I am setting value of a string to the getterSetter class.
My String Generator class -->
public class StringGenerator {
...
new BackgroundTask(context).execute(sb.toString());
Intent intent = new Intent(context.getApplicationContext(),Answer.class);
context.startActivity(intent);
sb.setLength(0);
}
My BackgroundTask Class -->
class BackgroundTask extends AsyncTask<String, Void, String> {
Context context;
GetterSetter getterSetter;
...
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d("Answer",s);
getterSetter = new GetterSetter();
getterSetter.setString(s);
}
}
My GetterSetter Class, here log prints correct string.So, the string is set here, I have verified it. -->
class GetterSetter{
private String string;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
Log.d("S1",string);
Log.d("S2",this.string);
}
}
From this getterSetter class i want to access string and set it onto textview onto another Activity called Answer Activity.
My Answer Activity -->
public class Answer extends AppCompatActivity {
GetterSetter getterSetter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_answer);
getterSetter = new GetterSetter();
((TextView)findViewById(R.id.SetSteps)).setText(getterSetter.getString());
}
}
Problem with this method is -->
But the String value set on the textview is empty, so it prints
nothing. Also, i tried to log the string.
Method 2 : By using SharedPreferences,but it always gives me default value which is ""(Empty).
My StringGenerator Class -->
public class StringGenerator {
...
new BackgroundTask(context).execute(sb.toString());
Intent intent = new Intent(context.getApplicationContext(),Answer.class);
context.startActivity(intent);
sb.setLength(0);
}
My BackgroundTask Class -->
class BackgroundTask extends AsyncTask<String, Void, String> {
Context context;
SharedPreferences.Editor editor;
BackgroundTask(Context context) {
this.context = context;
editor = context.getSharedPreferences("SolutionString",context.MODE_PRIVATE).edit();
}
...
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d("Answer", s);
editor.putString("Solution",s);
editor.apply();
}
}
My Answer Class -->
public class Answer extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_answer);
SharedPreferences sharedPreferences = getSharedPreferences("SolutionString", MODE_PRIVATE);
((TextView)findViewById(R.id.SetSteps)).setText(sharedPreferences.getString("Solution",""));
}
}
Problem with this method is -->
This method will set the correct output string only for first time, after that second time and onwards this method will print default value which is ""(Empty).I tries to debug this method via printing logs and in logs the string values are not empty, i got correct values in logs. So, I think in this method the problem is updation of sharedpreferences value. I have also researched about this on stackoverflow and tried following solution, but nothing works.
SharedPreferences return only default value
getPreferences always returns default value
and many more.
Can anyone tell me how can i access the string inside OnCreate Activity for first method?? or for second method I need to get the updated values for SharedPreferences.
I don't want to create another intent because, i am already running intent from String Generator class.
first of all new GetterSetter() will creates a new object... now lets see how the memory works...
class BackgroundTask extends AsyncTask<String, Void, String> {
Context context;
GetterSetter getterSetter;
...
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d("Answer",s);
getterSetter = new GetterSetter(); // assume is stored at address 2000 in the memory
getterSetter.setString(s);
}
}
public class Answer extends AppCompatActivity {
GetterSetter getterSetter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_answer);
getterSetter = new GetterSetter();// assume stored at 2008 in the memory , hence the string private String string does not have any value...
((TextView)findViewById(R.id.SetSteps)).setText(getterSetter.getString());
}
}
Now here are the points :-
private String string; if you declare this as private String string = ""; the null pointer exception will not occur.
you should have single GetterSetter getterSetter; intialized getterSetter = new GetterSetter(); only once then the value will persist... if you have both the BackgroundTask and Answer in separate files Answer class wont be able to see BackgroundTask class GetterSetter...
Rather include BackgroundTask with the Activity class and call the textview.setText in onPostExecute...
public class Answer extends AppCompatActivity {
GetterSetter getterSetter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_answer);
getterSetter = new GetterSetter();
}
class BackgroundTask extends AsyncTask<String, Void, String> {
Context context;
...
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d("Answer",s);
getterSetter.setString(s);
((TextView)findViewById(R.id.SetSteps)).setText(getterSetter.getString());
}
}
}
If you insist on using different files then BackgroundTask should be called first and
class BackgroundTask extends AsyncTask {
Context context;
public static GetterSetter getterSetter; // this will store the object in memory and the value will persist between classes...
...
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d("Answer",s);
getterSetter = new GetterSetter();
getterSetter.setString(s);
// the activity Answer should start anywhere after this to get the correct value...
}
}
public class Answer extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_answer);
((TextView)findViewById(R.id.SetSteps)).setText(BackgroundTask.getterSetter.getString());
}
}
Hope it helps...
As you are creating a new object so the data in the existing object will be destroyed. So, the simplest way to do the above task is send the object with the intent. You can do this onPostExecute or on click of a button in your first Activity.
Intent intent = new Intent(MainActivity.this, Answer.class);
intent.putExtra("Object", getterSetter);
startActivity(intent);
and your object i.e. GetterSetter must implements Serializable as:
class GetterSetter implements Serializable
and in your another activity i.e. Answer.java you have to receive intent as:
Intent rcv = getIntent();
GetterSetter getterSetter = (GetterSetter) rcv.getSerializableExtra("Object");,
((TextView)findViewById(R.id.SetSteps)).setText(getterSetter.getString());
Related
I have Activity1 and Activity2. Both can call a class named "fetchData.java". Is there any way inside fetchData.java wherein I can get which activity called it?
Here is my fetchData.java:
public class fetchData extends AsyncTask<String,String,String> {
#Override
protected String doInBackground(String... voids) {
//do something
}
#Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
}
}
Activity1 and Activity2 code:
public class Activity1 extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Call fetchData.java
fetchData getValues = new fetchData();
getValues.execute();
}
The only way I can see for you to do this is by passing the Activity in the constructor:
public class FetchData extends AsyncTask<String, String, String> { //rename fetchData to FetchData to follow Java coding practices
private Activity activity;
public FetchData(Activity activity) {
this.activity = activity;
}
#Override
protected String doInBackground(String... strings) { //use the proper variable names
//...
}
#Override
protected void onPostExecute(String string) { //use the proper variable names
//use instanceof to check the identity of the activity instance
if (activity instanceof Activity1) {
//whatever
} else if (activity instanceof Activity2) {
//whatever else
}
//super call isn't needed
}
}
And to instantiate it:
FetchData getValues = new FetchData(this);
getValues.execute();
Take note of my comments. I made a few changes to your code to improve readability and conform better to Java's coding standards.
In other cases, you might be able to read the stacktrace and find the calling class, but since you're using an AsyncTask, which runs on another Thread, the stacktrace only goes back to when the Thread was created, so it wouldn't work here.
When I need a callback from Activity B back to Activity A I usually make the reference variable in Activity B 'static'. I realize that if the user rotates the device the Life Cycle methods will remove my reference.
Is this the only drawback and is there a better way to register without a static reference. Is it better to simply put all data in the Application class ? - Thank you.
public class MainActivity extends Activity implements InterfaceMainActivityTwo {
static Main2Activity main2Activity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
main2Activity = new Main2Activity();
main2Activity.setDataListener(this);
}
#Override
public void getDataMainActivityTwo(String string) {
tvTextData.setText(string);
}
}
public class Main2Activity extends Activity {
static InterfaceMainActivityTwo mGetDataInterface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
public void getDataSaveBtn(View v) {
if (mGetDataInterface != null)
mGetDataInterface.getDataMainActivityTwo(fullName);
else
Toast.makeText(this, "IS NULL.INTERFACE NOT INITIALIZED !!!!", Toast.LENGTH_SHORT).show();
}
/////////// interface setup
interface InterfaceMainActivityTwo {
void getDataMainActivityTwo(String string);
}
public void setDataListener(InterfaceMainActivityTwo listener) {
this.mGetDataInterface = listener;
}
}
You should never need a callback between two activities. You're doing something wrong if you do. If you need to pass data from A to B, pass it in the bundle. If you need to pass it back from B to A, use startActivityForResult and pass it in the result. If you need to share data between many activities, it should be held in some globally accessible data structure, either in memory or on disk.
How can I use findViewById() in a non activity class. Below is my code snippet. I get the error message: "can't resolve method findViewById" if used directly. And if i try to use the class constructor (Where the imageView is available) i get this error "cannot resolve symbol context"
public class MyBroadcastReceiver extends FirstBroadcastReceiver {
Activity activity;
public MyBroadcastReceiver(Context context, Activity activity){
this.context=context; // error here(cannot resolve symbol context)
this.activity=activity;
}
#Override
protected void (Context context) {
// content
}
#Override
public void onButton(Context context, boolean isClick) {
if(isClick) {
ImageView blueImage = (ImageView)activity.findViewById(R.id.imageView);
blueImage.setColorFilter(0xff000000);
}
}
.......
....
// and so on
And below is my MainActivity with MybroadcastReceiver class instance.Is it correct?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// and so on
}
}
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver(MainActivity.this,this);
#Override
public void onActivityResult() {
// some code
}
#Override
public void onInitialized(MyManager manager){
// some code
}
A BroacastReceiver runs entirely in the background, listening for Intents sent either by the OS or other apps. It is not responsible for any UI interactions, and cannot access any views. Therefore, findViewById cannot be used within a BroadcastReceiver.
See also - What is BroadcastReceiver and when we use it?
You have to pass View to the non activity class, before using findViewByid
and
try using
view.findViewByid(R.id.view_id);
Because context is null in Broadcast class. use Broadcast class constructor to pass parent_activity(Where the imageView is available) context in Broadcast to access the context:
public class Broadcast extends BroadCastReceiver {
Activity activity;
public Broadcast(Context context,Activity activity){
this.context=context;
this.activity=activity;
}
.......
....... //so on
and in parent_activity create Broadcast class instance by passing parent_activity context as:
Broadcast broadcast = new Broadcast(parent_activity.this,this);
Use activity instance as:
#Override
public void onButton(Context context, boolean isClick) {
if(isClick) {
ImageView blueImage = (ImageView) activity.findViewById(R.id.imageView); //<--- here
}
}
.........
......... //so on
public class MainActivity extends AppCompatActivity {
public ShareData SD = new ShareData();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SD.set_numb(5);
}
// when button clicked
public void noviEkran(View view){
Intent i = new Intent(this,klasaB.class);
startActivity(i);
}
}
public class ShareData {
private int number;
public ShareData(){
this.number=0;
}
public void set_numb(int num){
number = num;
}
public int get_numb(){
return number;
}
}
public class klasaB extends Activity{
ShareData sd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int i =sd.get_numb();
System.out.println("Saved numb:" + i);
}
}
My question is, if i declare object in 1st class, and set its parameter number to 5, how to acces this number from other class because now my apk crashes when reading " int i =sd.get_numb(); " in class "klasaB".
Any suggestion how to make this work?
ps: i dont want to use static variables, or putExtra with Intents.
If data is simple/primitive then use Intent to pass data from one activity to another. That is what Intent is for.
If it is not (some sort of complex data structure or object), I would extend Application, by making a custom sub class. Application class (as the name implies) is accessible to all Activities, even when app transitions from one to another. Below is a very simple example, just to show you the idea. You can modify/adjust that to your needs.
public class MyApplication extends Application {
private X x;
public static void setX(X x) { ... }
public static X getX() { ... }
}
public class ActivityA extends Activity {
...
MyApplication.setX(x);
}
public class ActivityB extends Activity {
...
X x = MyApplication.getX();
}
You may have mixed up Activity/MainActivity/AppCombatActivity inheritance... I suspect that the reason you are seeing the error -- by the way, please look into "how to ask" and include a bit more information next time -- is that sd in klasaB is never initialized.
MainActivity.SD will hold that 5 after its onCreate(), whereas klasaB.sd is never set to anything.
You never reference or instantiate SD in class B. To get the data to ClassB you will need to set the data as an extra in the intent. Most classes cannot be sent in the intent, so for your case you should pass the primitive types of the object, then create the object.
// when button clicked
public void noviEkran(View view){
Intent i = new Intent(this,klasaB.class);
i.putExtra("TAG", SD.get_num());
startActivity(i);
And then in Class B
ShareData SD = new ShareData();
SD.set_num(getIntent.getIntExtra("TAG", 0);
You can access your class object either using implements Serializable or Parcelable
1.Implement serializable into your ShareData class
public class ShareData implements Serializable{
private int number;
public ShareData(){
this.number=0;
}
public void set_numb(int num){
number = num;
}
public int get_numb(){
return number;
}
}
2.create object of SharedData and share with intent to classB
public class MainActivity extends AppCompatActivity {
public ShareData SD = new ShareData();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SD.set_numb(5);
}
// when button clicked
public void noviEkran(View view){
Intent i = new Intent(this,klasaB.class);
i.putExtras("key", SD)
startActivity(i);
}
}
3.Access in classB
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ShareData sd = (ShareData)getIntent().getSerializableExtra("key").
System.out.println("Saved numb:" + sd.get_num());
}
Use a singleton class
Declare an instance in ShareData class:
public class ShareData {
private static ShareData sdInstance = null;
...}
add this method in ShareData class:
public static ShareData getInstance(){
if(sdInstance == null){
sdInstance = new ShareData();
}
return sdInstance;
}
To get same object in other classes , use this
ShareData sd = ShareData.getInstance();
now you will receive same sd.get_numb()
I have an activity with multiple AsyncTask's, but when i press back button, the Activity is reloaded and the AsyncTask's are executed again. what should i do to Back to the previous activity and not reload the activity and asynctask ? please help.
public class LugarActivity extends SherlockActivity {
CargarDatos cargarDatos;
CargarComentarios cargarComentarios;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lugar);
cargarDatos = new CargarDatos();
cargarCometarios = new CargarComentarios();
loadData();
}
public void loadData(){
cargarDatos.execute();
}
public void loadOtherData(){
cargarComentarios.execute();
}
public class CargarDatos extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
#Override
protected void onPostExecute(String html) {
loadOtherData();
}
}
public class CargarComentarios extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
}
}
FIXED!
i fixed the problem with Singleton class:
public class DataManager {
private static DataManager instance = null;
protected static boolean isShowingTheView = false;
protected DataManager() { }
public static synchronized DataManager getInstance() {
if (instance == null) {
instance = new DataManager();
}
return instance;
}
}
in the activity i add this code:
DataManager dataManager = new DataManager();
if(!dataManager.isShowingTheView){
loadData();
dataManager.isShowingTheView = true;
}else{
finish();
}
and finally i override the onDestroy() method
#Override
public void onDestroy(){
dataManager.isShowingTheView = false;
super.onDestroy();
}
Remove loadData() from onCreate and call somewhere else.
Use Fragments
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
A fragment can stay in memory during a configuration change and therefore you can run your asynctask inside itself. You can then query the fragment for any state information you require from your tasks and update your Activity accordingly.
If your Activity is destroyed before the other activity starts, using the back button will call onCreate again, instead of onRestart or onResume.
See here for details.
As Kuffs already mentions, using Fragments is the way to go.
Uglier solution, you could also set a shared preference holding a boolean once your AsyncTask is launched (or on its onPostExecute) so that it won't launch again after checking for that preference on your Activity's onCreate.