Original thread lost because of changed reference - java

In my android app I'm trying to extend the Thread class to easy pass my values between the original thread and a another one. So I can easily update my UI.
To do this I extended the Thread class:
public class ThreadUpdateUI extends Thread {
AppCombatActivityExtended activity;
Map<?,?> values;
public ThreadUpdateUI(AppCombatActivityExtended activity, Map<?,?> values){
this.activity = activity;
this.values = values;
}
public void UpdateUI(Map<?,?> values){
this.activity.UIThreadFinished(values);
}
public Map<?,?> GetValues()
{
return this.values;
}
}
Not only did I extend Threads, but I also extended the class for Activity so I have a main function I can call in every activity to update my UI:
public class AppCombatActivityExtended extends AppCompatActivity {
protected void UIThreadFinished(Map<?,?> values){}
}
In my activity I use the ThreadUpdateUI class to run a thread in which I can pass all my own values to use in the other thread:
public class MainActivity extends AppCombatActivityExtended {
static final String carlooking_key = "text_carlooking";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//init views
final TextView text_carlooking = (TextView) findViewById(R.id.main_text_carlooking);
//init parameters for thread
HashMap<String, String> UIThreadParameters = new HashMap<>();
UIThreadParameters.put(carlooking_key, text_carlooking.getText().toString().replace(".", ""));
//ThreadUpdateUI
ThreadUpdateUI TU_UI = new ThreadUpdateUI(this, UIThreadParameters) {
#Override
public void run() {
try {
Integer counter = 0;
while (true) {
sleep(1000);
Map values = GetValues();
String text = values.get(carlooking_key).toString();
text += "_test";
Map result = new HashMap<String,String>();
result.put(carlooking_key, text);
UpdateUI(result);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
TU_UI.start();
}
#Override
protected void UIThreadFinished(Map<?, ?> values) {
super.UIThreadFinished(values);
final TextView text_carlooking = (TextView) findViewById(R.id.main_text_carlooking);
text_carlooking.setText(values.get(carlooking_key).toString());
}
}
My code crashes at:
UpdateUI(result);
Saying: "Only the original thread that created a view hierarchy can touch its views."
In my trace I can see the following:
at MainActivity.UIThreadFinished(MainActivity.java:56)
at ThreadUpdateUI.UpdateUI(ThreadUpdateUI.java:21)
at MainActivity$1.run(MainActivity.java:42)
Which could indicate that the reference changed in my ThreadUpdateUI causing to call ThreadUpdateUIFinished in a different thread than the original.
Is it possible to make this code return to the original thread to update my UI in a loop?

Related

How can i make my request run in background while filling other EditTexts (Thread)?

I'm new to threading but I have an EditText view which whenever is getting out of focused it fills a RecyclerView with image logos using the user's input from EditText. But, whenever the user gets out of focuse and the method is called everything stops for a while(which mean im bad at threading). How can I improve this code so it can run smoothly?
My activity class:
public class addItem extends AppCompatActivity {
LoadingDialog loadingDialog;
RecyclerView imgList;
ArrayList<Bitmap> bitmapList = new ArrayList<>();
BitmapAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
/*
/ Code Unnecessary to the problem…
*/
et_title.setOnFocusChangeListener((v, hasFocus) -> {
if(!hasFocus){
getImageLogo(et_title.getText().toString());
}
});
}
#SuppressLint("NotifyDataSetChanged")
private void getImageLogo(String serviceName){
googleRequest googleList = new googleRequest(serviceName);
googleList.start();
try {
googleList.join();
} catch (InterruptedException e) {
Log.e("Interrupted Error","Thread Was Interrupted unexpectedly",e);
}
if(googleList.getImgRealList() != null) {
bitmapList.clear();
bitmapList.addAll(googleList.getImgRealList());
}else {
bitmapList.clear();
}
adapter.notifyDataSetChanged();
}
My googleRequest class:
public class googleRequest extends Thread {
private ArrayList<Bitmap> imgRealList;
private final String keyword;
public googleRequest(String keyword){
this.keyword = keyword;
}
public ArrayList<Bitmap> getImgRealList() {
return imgRealList;
}
#Override
public void run() {
String newKeyword = keyword.toLowerCase(Locale.ROOT);
newKeyword = newKeyword.replace(' ','+');
String url = "https://www.google.gr/search?bih=427&biw=1835&hl=el&gbv=1&tbm=isch&og=&ags=&q="+ newKeyword;
try {
Document document = Jsoup.connect(url).get();
imgRealList = new ArrayList<>();
Elements imgList = document.select("img");
for (int i=1;i<imgList.size();i++) {
if(i==8)
break;
String imgSrc = imgList.get(i).absUrl("src");
InputStream input = new java.net.URL(imgSrc).openStream();
Bitmap bitmap = BitmapFactory.decodeStream(input);
imgRealList.add(bitmap);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Here is an example of how it can be implemented using a callback as I have mentioned in comments. For this, we need to define a callback inerface I named it as following, you can change the names for your convenience.
RequestConsumer it is simple java interface.
/// Must be executed in the UI (main) thread.
#MainThread
public interface RequestConsumer {
void onRequestResult(List<Bitmap> bitmaps);
}
googleRequest thread class.
public class googleRequest extends Thread {
private ArrayList<Bitmap> imgRealList;
private final String keyword;
/*
We will use the request consumer callback in order to deliver the results
to the UI from background. Since we need to touch the UI by this callback
we ensure that it will execute within the UI thread's queue using the
uiHandler.
*/
private final RequestConsumer requestConsumer;
private final Handler uiHandler = new Handler(Looper.getMainLooper());
public googleRequest(#NonNull String keyword, #NonNull RequestConsumer requestConsumer){
this.keyword = keyword;
this.requestConsumer = requestConsumer;
}
#Override
public void run() {
String newKeyword = keyword.toLowerCase(Locale.ROOT);
newKeyword = newKeyword.replace(' ','+');
String url = "https://www.google.gr/search?bih=427&biw=1835&hl=el&gbv=1&tbm=isch&og=&ags=&q="+ newKeyword;
try {
Document document = Jsoup.connect(url).get();
imgRealList = new ArrayList<>();
Elements imgList = document.select("img");
for (int i=1;i<imgList.size();i++) {
if(i==8)
break;
String imgSrc = imgList.get(i).absUrl("src");
InputStream input = new java.net.URL(imgSrc).openStream();
Bitmap bitmap = BitmapFactory.decodeStream(input);
imgRealList.add(bitmap);
}
// I think according to your code; the data you've requested is ready
// to deliver from now on. But attention! we post it to execute it in the UI thread
uiHandler.post(() -> requestConsumer.onRequestResult(imgRealList));
} catch (IOException e) {
e.printStackTrace();
}
}
}
addItem activity class
public class addItem extends AppCompatActivity {
LoadingDialog loadingDialog;
RecyclerView imgList;
ArrayList<Bitmap> bitmapList = new ArrayList<>();
BitmapAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
/*
/ Code Unnecessary to the problem…
*/
et_title.setOnFocusChangeListener((v, hasFocus) -> {
if(!hasFocus){
getImageLogo(et_title.getText().toString());
}
});
}
#SuppressLint("NotifyDataSetChanged")
private void getImageLogo(String serviceName){
googleRequest googleList = new googleRequest(serviceName, images -> {
// Here we get the delivered results in this callback
if(images != null) {
bitmapList.clear();
bitmapList.addAll(images);
}else {
bitmapList.clear();
}
adapter.notifyDataSetChanged();
});
googleList.start();
}
}
Note I've written it in a text editor so the code needs some function test.

If I get a row from Database with Room and LiveData, how can I "synchronize" it with a variable in the UI thread?

The example:
selectedCode.something() gives error, because selectedCode is null, it has not initialized in the other thread, but I don't want the whole section to write in the changed method of Observable.
Maybe there is an other way to do this?
public class CodeDetailsActivity extends AppCompatActivity {
private Code selectedCode;
private CodeRepository mCodeRepository;
private void retrieve(final int id){
mCodeRepository.retrieveCode(id).observe(this, new Observer<Code>() {
#Override
public void onChanged(Code code) {
if (code != null){
selectedCode = code;
}
}
});
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_code_details);
retrieveCode(1);
selectedCode.something();

Update ListView asynchronously using Realm

My applications hang for a bit when I populate data from realm database to my listview.
So I planned to do it using Asynchronously so meanwhile data is collected I display a Loading dialogue here is the Code.
Already referred to this question by not able to implement in my case.
private class YourAsyncTask extends AsyncTask<String, String, RealmResults> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
// start loading animation maybe?
progressDialog = ProgressDialog.show(DictionarySscWords.this,
"ProgressDialog",
"Loading all words!");
}
#Override
protected RealmResults doInBackground(String... params) {
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);
realm = realm.getDefaultInstance();
RealmQuery<Word> query = realm.where(Word.class);
for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
} else {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
.or();
}
}
sscresult = query.findAll(); //error 1
return sscresult;
}
#Override
protected void onPostExecute(RealmResults r) {
progressDialog.dismiss();
list.setAdapter(new MyAdapter(sscresult)); //error 2
realm.close();
}
}
ok so there are two problems if anyone can be solved my application would be error-free
if I try to run list.setAdapter(new MyAdapter(sscresult)); in background process the error is:-
this can run only in UI thread
if try to run in postExecute error is :-
Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
I am not able to solve this issue please help
You can have your query evaluated on a background thread using asynchronous query API in Realm.
private OrderedRealmCollectionChangeListener<RealmResults<User> callback = new OrderedRealmCollectionChangeListener<>() {
#Override
public void onChange(RealmResults<User> results, OrderedCollectionChangeSet changeSet) {
if (changeSet == null) {
// The first time async returns with an null changeSet.
} else {
// Called on every future update.
}
}
};
private RealmResults<User> result;
public void onStart() {
result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}
But if you give the RealmResults to a RealmRecyclerViewAdapter, then this is all automatic.
P.S. not closing Realm instance in doInBackground() is like, S-class horrible mistake. Please close your Realm instance on non-looping background threads.
Specifically the following:
// private class YourAsyncTask extends AsyncTask<String, String, RealmResults> {
//
// ProgressDialog progressDialog;
// #Override
// protected void onPreExecute() {
// // start loading animation maybe?
// progressDialog = ProgressDialog.show(DictionarySscWords.this,
// "ProgressDialog",
// "Loading all words!");
// }
//
// #Override
// protected RealmResults doInBackground(String... params) {
// RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
// Realm.setDefaultConfiguration(realmConfig);
// realm = realm.getDefaultInstance();
// RealmQuery<Word> query = realm.where(Word.class);
//
// for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
// if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
//
// query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
// } else {
// query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
// .or();
//
// }
//
// }
// sscresult = query.findAll(); //error 1
// return sscresult;
//
// }
//
// #Override
// protected void onPostExecute(RealmResults r) {
// progressDialog.dismiss();
// list.setAdapter(new MyAdapter(sscresult)); //error 2
// realm.close();
// }
//}
and
public class MyActivity extends AppCompatActivity {
private RealmResults<Word> words;
private Realm realm;
private WordAdapter wordAdapter;
#BindView(R.id.recycler_view)
RecyclerView recyclerView;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.my_activity);
ButterKnife.bind(this);
realm = Realm.getDefaultInstance();
words_for_ssc = ...
RealmQuery<Word> query = realm.where(Word.class);
String[] array = words_for_ssc[Integer.parseInt(params[0])];
for (int i = 0; i < array.length; i++) {
query = query.equalTo("word", array[i]);
if (i != array.length - 1) {
query = query.or();
}
}
words = query.findAllSortedAsync("word");
wordAdapter = new WordAdapter(words);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(wordAdapter);
}
#Override
public void onDestroy() {
super.onDestroy();
realm.close();
realm = null;
}
}
public class WordAdapter extends RealmRecyclerViewAdapter<Word, WordViewHolder> {
public class WordAdapter(OrderedRealmCollection<Word> words) {
super(words, true);
}
#Override
public WordViewHolder onCreateViewHolder(...) {
...
}
#Override
public void onBindViewHolder(WordViewHolder holder, int position) {
holder.bind(getData().get(position));
}
public static class WordViewHolder extends RecyclerView.ViewHolder {
public WordViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
public void bind(Word word) {
...
}
}
}
I think a cleaner solution to your problem without changing much of the code can be written below. In this case, everything that realm does happen on the background thread inside doInBackground. The realm instance is also closed on the thread it was created.
Now what I did basically is that I extracted a deep copy of the list of Words from RealmResult from realm.copyFromRealm(sscresult) which is completely detached from realm and can be moved around and modified inside any thread. All these objects are now free from realm and can be used in onPostExecute without any worries. The only thing you need to modify is the MyAdapter constructor which doesn't take a RealmResult but instead a List of Words which is exactly what you need and can be iterated the same way as RealmResult was.
The only downside of this approach is that the list of Words will not get synced automatically since they're detached and their value won't change automatically if they get altered inside Realm from somewhere else. But I'm pretty sure though that it won't bother you.
I'm also going to attach an official reference for realm.copyFromRealm() which is here.
private class YourAsyncTask extends AsyncTask<String, String, List<Word>> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
// start loading animation maybe?
progressDialog = ProgressDialog.show(DictionarySscWords.this,
"ProgressDialog",
"Loading all words!");
}
#Override
protected List<Word> doInBackground(String... params) {
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);
try(realm = realm.getDefaultInstance()) {
RealmQuery<Word> query = realm.where(Word.class);
for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
} else {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
.or();
}
}
// Here's the sort. Use findAllSorted instead.
// You can change Sort.ASCENDING to Sort.DESCENDING to reverse
// the order.
sscresult = query.findAllSorted("word", Sort.ASCENDING);
// This is where the magic happens. realm.copyFromRealm() takes
// a RealmResult and essentially returns a deep copy of the
// list that it contains. The elements of this list is however
// completely detached from realm and is not monitored by realm
// for changes. Thus this list of values is free to move around
// inside any thread.
ArrayList<Word> safeWords = realm.copyFromRealm(sscresult);
realm.close();
return safeWords;
}
}
#Override
protected void onPostExecute(List<Word> words) {
progressDialog.dismiss();
// Please note here MyAdaptor constructor will now take the
// list of words directly and not RealmResults so you slightly
// modify the MyAdapter constructor.
list.setAdapter(new MyAdapter(words));
}
}
Hope it helps!

How to generate a TextView on AsyncTask and place it on a GridLayout?

This is my situation:
I have a GridLayout (wich has some textviews in some cells of it right at the begining of the activity, but i will change it to generate those textviews dynamically later) and I want to place some textViews in a few cells of it.
The problem: I don't know how many textViews I will need. It depends of the information of the database. Besides, I don't know how to add the textViews generated to the gridLayout from an AsyncTask.
So, i've been looking for some answers but I couldn't make it work. I tried something like this, but is not exactly what i'm looking for (i create a new TextView, but can't add it to the gridLayout from that thread).
This is the workflow of my app:
1º I start the activity with the gridLayout. It has some textViews:
This is the main Activity:
public class MostrarProyectos extends AppCompatActivity {
private final String TAG = "MostrarProyectos";
//Para obtener de un hilo no principal los proyectos:
public static ArrayList<Proyecto> listaDeProyectos = new ArrayList<>();
public GridLayout grid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mostrar_proyectos);
grid = (GridLayout) findViewById(R.id.grid);
EstrategiaObtenerObjetoDB e = FabricaEstrategiaObtenerDB.getInstance().construirEstrategia("proyecto", this); //This is a Fabric, wich obtains a Strategy
e.execute(this.getApplicationContext(), "proyecto", this, this.grid);//I sended a lot of things to test if something gives result
}
}
2º In that main class, I started a new Thread with AsyncTask, to get data from a SQLite DB.
This is the Fabric:
public class FabricaEstrategiaObtenerDB {
private static final FabricaEstrategiaObtenerDB ourInstance = new FabricaEstrategiaObtenerDB();
public static FabricaEstrategiaObtenerDB getInstance() {
return ourInstance;
}
private FabricaEstrategiaObtenerDB() {}
public EstrategiaObtenerObjetoDB construirEstrategia(String tabla, Activity acti){
switch(tabla){
//Some other cases
case "proyecto":
EstrategiaObtenerObjetoDBProyecto p = new EstrategiaObtenerObjetoDBProyecto(acti, new onTextViewCreatedListener() {
#Override
public void onTextViewCreated(TextView tv) {
//I don't know what to do here
}
}); //This code I tried from the other stackOverflow answer
return p;
default:
return null;
}
}
}
This an abstract class to obtain objects from the DB:
public abstract class EstrategiaObtenerObjetoDB extends AsyncTask<Object, Void, ArrayList<Object>> {
protected onTextViewCreatedListener onTextViewCreatedListener; //part of the code from the other StackOverflow user.
Activity miActividad;
public EstrategiaObtenerObjetoDB(Activity act, onTextViewCreatedListener onTextViewCreatedListener){
this.onTextViewCreatedListener = onTextViewCreatedListener; //This is too code from the other user.
this.miActividad = act;
}
#Override
protected ArrayList<Object> doInBackground(Object... params) {
AppDbHelper mDbHelper = new AppDbHelper(miActividad.getApplicationContext());
SQLiteDatabase db = mDbHelper.getReadableDatabase();
String[] projection = obtenerSelect();
Cursor c = armarQuery(db, (String)params[1], projection); //params[1] is the name of the table in the DB
ArrayList<Object> arrayDeObjetos = new ArrayList<>();
try{
arrayDeObjetos.add(miActividad.getApplicationContext());//add the context
arrayDeObjetos.add(miActividad.findViewById(R.id.grid));//add the grid
armarObjetos(c);
return arrayDeObjetos;
}catch (Exception e){
String b = e.getMessage();
return null;
}
}
#Override
protected abstract void onPostExecute(ArrayList<Object> objects);
protected abstract String[] obtenerSelect();
protected abstract Cursor armarQuery(SQLiteDatabase db, String tabla, String[] projection);
protected abstract void armarObjetos(Cursor c);
}
And this is the specific Strategy:
public class EstrategiaObtenerObjetoDBProyecto extends EstrategiaObtenerObjetoDB {
public EstrategiaObtenerObjetoDBProyecto(Activity act,onTextViewCreatedListener onTextViewCreatedListener) {
super(act,onTextViewCreatedListener);
}
#Override
protected String[] obtenerSelect() {
String[] projection = {
AppContract.Proyecto._ID,
AppContract.Proyecto.COLUMN_NOMBRE,
AppContract.Proyecto.COLUMN_DESCRIPCION,
AppContract.Proyecto.COLUMN_PRIORIDAD,
AppContract.Proyecto.COLUMN_ANIO,
AppContract.Proyecto.COLUMN_MES,
AppContract.Proyecto.COLUMN_SEMANA,
AppContract.Proyecto.COLUMN_DURACION,
};
return projection;
}
#Override
protected Cursor armarQuery(SQLiteDatabase db, String tabla, String[] projection) {
Cursor cursor = db.query(
tabla,
projection,
null,
null,
null,
null,
null
);
return cursor;
}
#Override
protected void armarObjetos(Cursor c) {
c.moveToFirst();
ArrayList<Object> proyectos = new ArrayList<>();
do {
try{
String nombre = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_NOMBRE));
String descripcion = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_DESCRIPCION));
String prioridad = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_PRIORIDAD));
String anio = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_ANIO));
String mes = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_MES));
String semana = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_SEMANA));
String duracion = c.getString(c.getColumnIndex(AppContract.Proyecto.COLUMN_DURACION));
Proyecto p = new Proyecto(nombre,descripcion, prioridad, anio, mes, semana, duracion);
MostrarProyectos.listaDeProyectos.add(p);
proyectos.add(p);
}catch(Exception e){
Log.d("EstrategiaObtenerPr",e.getMessage());
}
} while (c.moveToNext());
}
#Override
protected void onPostExecute(ArrayList<Object> objects) {
MostrarProyectos.listaDeProyectos.add((Proyecto)objects.get(i));
GridLayout grid = (GridLayout) miActividad.findViewById(R.id.grid);
if(!MostrarProyectos.listaDeProyectos.isEmpty()){
for(int numeroTemporal = 0; numeroTemporal<MostrarProyectos.listaDeProyectos.size();numeroTemporal++){
Proyecto j = MostrarProyectos.listaDeProyectos.get(numeroTemporal);
TextView text = new TextView(miActividad.getApplicationContext());
text.setText(j.getNombre());
int numFila = MostrarProyectos.contarFilas(j.getMes(), j.getSemana());
GridLayout.LayoutParams params3 = new GridLayout.LayoutParams();
params3.rowSpec = GridLayout.spec(numFila);//,Integer.parseInt(j.getDuracion())
Log.d("MostrarProyecto", numFila+","+Integer.parseInt(j.getDuracion()));
params3.columnSpec = GridLayout.spec(3);
text.setLayoutParams(params3);
try{
if(onTextViewCreatedListener!=null){
onTextViewCreatedListener.onTextViewCreated(text);//from the other user
}
}catch (Exception excepcion){
Log.d("MostrarProyecto", excepcion.getMessage());
}
}
}
else{
Toast.makeText(miActividad.getApplicationContext(),"No hay proyectos",Toast.LENGTH_LONG);
}
MostrarProyectos.terminoCargaDatos = true;
}
}
3º After I get the data, I want to generate as many TextViews as objects i've got from the DB, so I use a 'for' to see how many objects i have inside a temporal list i created. For heach object, I want to create a TextView, and add it to the gridLayout (that is on the main thread).
And finally, an interface from the other answer:
public interface onTextViewCreatedListener {
public void onTextViewCreated(TextView tv);
}
I hope you can help me. Thank you.
EDIT_1: I need to use other thread different from the UI thread because i need to search in the DB the data.
You have to split your logic, search data in the DB in AsyncTask or simple new Thread() and then create UI elements and attach them in UI thread.
mActivity
.runOnUiThread(
new Runnable() {
#Override
public void run() {
//create a TextView, and add it to the gridLayout
}
});

Modifying data from an Async task in an entirely different class

I would like to know just out of curiosity if there are any convenient ways of pulling data out of an async task created inside a class, and then modifying the data in another class (Without extending classes)
I have a way to do it, but it involves making methods static along with the Async task itself
for example, here I'm just making a string "text" in the Async task
public class Main extends Activity{
//Context ctx;
static class MyAsyncTask extends AsyncTask<Void,String,String>{
static String result;
private static Context context;
public MyAsyncTask(Context m)
{
this.context = m;
}
#Override
protected String doInBackground(Void... noArgs) {
result = "text";
return result;
}
protected void onPostExecute(String result)
{
super.onPostExecute(result);
}
public static String getStr()
{
return result;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText et = (EditText)findViewById(R.id.editText1);
Button btn = (Button)findViewById(R.id.button1);
MyAsyncTask task = new MyAsyncTask(this);
task.execute();
final Test t = new Test();
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
et.append(t.modifiedString());
}
});
}
}
and in a new class I make a simple String method to modify that data from the async task
public class Test{
public String modifiedString()
{
// Main main = null;
// MyAsyncTask task = new MyAsyncTask(main.ctx);
// task.execute();
String s = (String)Main.MyAsyncTask.getStr();
return "modified " + s + "\n";
}
}
I'm wondering, is there a way I can do this without having to make the async task static? Perhaps with sharing contexts or something?
by the way I'm not doing this to solve any particular problem, I'm only doing it out of curiosity
Just create a singleton
public class Main extends Activity{
public static Main instance;
public static String thestring;
public class MyAsyncTask extends AsyncTask<Void,String,String>{
static final String result = "text";
Context context;
public MyAsyncTask(Context m)
{
this.context = m;
}
#Override
protected String doInBackground(Void... noArgs) {
return result;
}
protected void onPostExecute(String result)
{
super.onPostExecute(result);
}
public String getStr()
{
return result;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText et = (EditText)findViewById(R.id.editText1);
Button btn = (Button)findViewById(R.id.button1);
MyAsyncTask task = new MyAsyncTask(this);
task.execute();
thestring = task.getStr();
instance = this;
final Test t = new Test();
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
et.append(t.modifiedString());
}
});
}
public String pulledFromAsyncTask()
{
return thestring;
}
public static Main getInstance(){
return instance;
}
}
and then in the another class
public class Test{
public String modifiedString()
{
Main main = Main.getInstance();
//so with main.something.. you can call the methods you want
//a good solution is to make a singleton class only for MyAsyncTask setting the
//functions get/set so you can take the values from other classes
return "modified " + main.pulledFromAsyncTask() + "\n";
}
}
Reference to a Context in a static way is generally bad idea, it can cause memory leaks
Why don't you simply pass MyAsyncTask object to Test and then do whatever modifications you want, i.e. non-static fashion?
When it comes to testable code static/ singleton is a tough choice.
Depending upon your requirement on the state of data you can however start with an Observer pattern or producer-consumer pattern.
Check out Event bus library for probably an out of the box solution for this use case

Categories