Android - Change the Activity State using another Thread is Not Working - java

MyThread class is used to change the value of the myValue attribute in ActivityTwo Class.
public class MyThread implements Runnable {
private ActivityTwo activity;
public MyThread(ActivityTwo activity) {
this.activity = activity;
}
#Override
public void run() {
while(true){
activity.setValue(2);
}
}
}
ActivityTwo is an AppCompatActivity activity which runs as the main Thread.
public class ActivityTwo extends AppCompatActivity {
private MyThread myThread;
private Button startLogBtn;
private int myValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
startLogBtn = findViewById(R.id.startLogBtn);
myThread = new MyThread(this);
startLogBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new Thread(myThread).start();
}
});
}
public void setValue(int value){
this.myValue= value;
}
}
When I Click the startLogButton it goes to a white screen and then restart the app. What should I do? I have no idea what has gone wrong.
Thanks in advance.

The issue was I have violated the rule "Do not access the Android UI toolkit from outside the UI thread".
ref : https://developer.android.com/guide/components/processes-and-threads
As a solution I found that I can use thread communication using message passing.
ref : Communication between UI thread and other threads using handler

Related

Accessing data of destroyed activity means I have a memory leak?

I've created an interface which holds a reference to an interfaces instantiated from an activity.
This is the interface:
public interface Calback {
void fun();
}
This is the activity which instantiates the calback and binds it to asincktask.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView txt = findViewById(R.id.helloTxtv);
txt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Calback call = new Calback() {
#Override
public void fun() {
Log.d("tag","text of destroyed activity: "+((TextView)findViewById(R.id.helloTxtv)).getText());
}
};
Worker worker = new Worker(call);
worker.execute();
}
});
}
}
What's strange is that using that calback I can access textview even if the activity was destroyed.
This is the code from asyncktask:
public class Worker extends AsyncTask<Void, Void, Void> {
private final Calback call;
public Worker(Calback call) {
this.call = call;
}
#Override
protected Void doInBackground(Void... voids) {
try {
sleep(5000);
Log.d("tag","done");
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
call.fun();
}
}
To ensure that the activity it's destroyed I've just rotated the screen.(But I've got the same result after starting another activity and finish the current one)
And here is the log result.
PS: I've used Android Studio 3.0
If you are able to access the text of the TextView after the parent Activity has been destroyed, then you have a memory leak.
However, I'm not convinced that is what is going on here. I think it is more likely that either the activity has not been destroyed, or the activity's state was persistent and you are now looking at the state in the new (reincarnated) activity.
Why? Because, it seems that the callback is being called via an onClick listener for the text view. And that can only occur if the specific text view is still visible. It can't be visible if it is a component of a destroyed activity.

Sending data from broadcastreceiver to a custom thread in android not working?

I have 3 threads : UI Thread (MainActity thread), Thread1 and Thread2.
UI Thread starts Thread1 and Thread1 starts Thread2
There is a MainActity class variable that I want to instantiate. What I did was using MainActivity.this.runOnUiThread(new Runnable() {...} to instantiate a TextView from a broadcastReceiver. But I thought that Thread2 would have the new value of that text view. It does not. It still has the initial value (i.e before the private boradcastreceiver alters it. How can I get the new value in Thread2 ?
Below is how my code looks like :
public class MainActivity extends AppCompatActivity {
TextView mytxtView; //Use to bind a TextView from UI layout
#Override
protected void onCreate(Bundle savedInstanceState) {
mytxtView = (TextView) findViewById(R.id.mytxtView);
mytxtView.setText("Value 1");
Thread thread1 = new Thread(new Thread1());
thread1.start();
}
public void onResume() {
registerReceiver(myReceiver, new IntentFilter("android.provider.Telephony.SOME_TELEPHONY_INTENT_ACTION"))
}
private class Thread1 extends Thread{
#Override
public void run() {
Thread thread2 = new Thread(new Thread2());
thread2.start();
}
}
private class Thread2 extends Thread{
#Override
public void run() {
Thread.sleep(45000) ; //just to make sur myReceiver does its job
Toast.makeText(getApplicationContext(),myTxtView.getText(),Toast.LENGTH_LONG).show();
// This shows "Value 1" instead of "Value 2"
}
}
BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().
equals("android.provider.Telephony.SOME_TELEPHONY_INTENT_ACTION")){
//do some work
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
myTxtView.setText("Value 2");
}
}}
}
Issue : your are just registering your broadcast but your are never triggering it so you need to trigger the broadcast to to execute onReceive method using sendBroadcast(yourBroadcastIntent).
Try
public void onResume() {
registerReceiver(myReceiver, new IntentFilter("android.provider.Telephony.SOME_TELEPHONY_INTENT_ACTION"));
sendBroadcast(new Intent(""android.provider.Telephony.SOME_TELEPHONY_INTENT_ACTION""));
}
And no need of runOnUiThread in receiver , as #greenapps has already mentioned it as well
And it setText to set the text as
myTxtView.setText("Value 2");
though seems like a typo info.myTxtView("Value 2");
Update : Working Project files repo along with screenshot to demonstrate the working state with provided screenshot.

Constructor wont run it's code

I want to run a gameloop code inside my GameLoop class constructor in my activity, but it seems that it got no respond.
I've tried to put the code inside the OnCreate method instead of a new class and that worked.
My Activiy Class:
public class GameActivity extends Activity {
private Button btnHertz;
private TextView textView1;
private GameLoop gameloop;
private int hertz = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
gameloop = new GameLoop();
btnHertz = (Button) findViewById(R.id.btnHertz);
textView1 = (TextView) findViewById(R.id.testTextView1);
}
public void testUpdate(){
hertz++;
textView1.setText(Integer.toString(hertz));
}
GameLoop Class:
public class GameLoop {
private GameActivity gui;
public GameLoop() {
gui = new GameActivity();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(new Runnable() {
public void run() {
gui.testUpdate();
}
}, 0, 10, TimeUnit.MILLISECONDS);
}
gui = new GameActivity();
The activity object you're passing updates to is different from the one that displays your UI.
Never instantiate activities yourself with new. Their lifecycle methods won't be invoked and they won't be good for anything. In this case you'd get an NPE at textView1.setText() since onCreate() has not been run.
Instead, pass a GameActivity reference as an argument to GameLoop, e.g.
... new GameLoop(this)
...
public GameLoop(GameActivity gui) {

Changing fragment layout from a thread that works in activity

I have an activity that contains fragments that are added at runtime. Also activity is connected to the tcp server. When I swipe the fragment server sends a message in a different thread. Then I want to change the button colors inside the fragment in this thread. I have written the code in fragment below:
public class changeBtn implements Runnable
{
#Override
public void run() {
button = (Button) fragmentView.findViewWithTag("Button1");
}
}
public void startProg(String[] array)
{
new Thread(new changeBtn()).start();
}
This is the class that works async in activity for tcp
public class connectTask extends AsyncTask<String,String,TCPClient> {
#Override
protected TCPClient doInBackground(String... message) {
//we create a TCPClient object and
mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
srvrMessage = message;
if(srvrMessage!=null&&message!=null) {
acikMasalar = srvrMessage.split("\\*");
mesajGeldi = true;
FragmentMasaDesing fragment=null;
if(mesajGeldi) {
int a =mViewPager.getCurrentItem();
fragment = (FragmentMasaDesing) getSupportFragmentManager()
.getFragments().get(mViewPager.getCurrentItem());
}
if(fragment!=null) {
fragment.startProg();
}
}
}
});
mTcpClient.run();
return null;
}
}
I am running this code inside the thread that works in activity. It throws me this error:"Only the original thread that created a view hierarchy can touch its views."
How can I change the button color from another thread in activity?

AsyncTask is restarting when i press back button

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.

Categories