Below is my test code. I let the thread sleep on purpose. I tried to simulate the heavy IO task.
I tried to use static inner class. And also used the WeakReference. I think I must miss some very important part. I test the code is still leaking after the activity finished. Can anyone provide some hints? Thanks.
public class Main2Activity extends AppCompatActivity {
TextView textView;
MyAsyn myAsyn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
textView=findViewById(R.id.test_view);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
myAsyn=new MyAsyn(textView);
myAsyn.execute();
}
#Override
protected void onDestroy() {
super.onDestroy();
if (myAsyn!=null){
myAsyn.cancel(true);
}
}
static class MyAsyn extends AsyncTask {
WeakReference<TextView> textViewWeakReference;
public MyAsyn(TextView textView) {
this.textViewWeakReference = new WeakReference<>(textView);
}
#Override
protected Object doInBackground(Object[] objects) {
t(textViewWeakReference);
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
TextView textView= textViewWeakReference.get();
if (textView!=null) {
textView.setText("DONE");
}
}
}
public static void t( WeakReference<TextView> textViewWeakReference){
final TextView textView=textViewWeakReference.get();
new Thread(){
#Override
public void run() {
super.run();
try {
Thread.sleep(500000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (textView!=null) {
String a = textView.getText().toString();
String b = a + a;
}
}
}.run();
}
}
Related
I am working on a game project. I want to associate each view of my game to its respective thread and then update the view according to the logic running in that thread.
To simplify, I am posting a sample:
This is Main Activity class, which will implement the UI:
public class Main extends Activity{
private View root;
private boolean ready = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
private void init() {
setContentView(R.layout.s_main);
root = findViewById(R.id.root);
ViewTreeObserver vto = root.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
root.getViewTreeObserver().removeOnPreDrawListener(this);
ready = true;
return true;
}
});
}
public void start(View view) {
try {
if (ready && !Threads.run) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
new AsyncTasks(this, R.id.txv1).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new AsyncTasks(this, R.id.txv2).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
new AsyncTasks(this, R.id.txv1).execute();
new AsyncTasks(this, R.id.txv2).execute();
}
} else {
Threads.run = false;
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
This is AsyncTask extended class to update View:
public class AsyncTasks extends AsyncTask<Void, Void, String> {
private TextView view;
private boolean breakMove;
private String updateError;
public AsyncTasks(Activity activity, int viewId) {
breakMove = false;
updateError = null;
view = activity.findViewById(viewId);
}
#Override
protected String doInBackground(Void... voids) {
String message;
Threads.run = true;
try {
while (!breakMove) {
publishProgress();
Thread.sleep(100);
}
message = updateError != null ? updateError : "Thread Ends";
} catch (Exception ex) {
ex.printStackTrace();
message = ex.getMessage();
}
return message;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
try {
breakMove = !Threads.run;
if (view != null)
view.setText(String.valueOf(Math.random() * 100));
} catch (Exception ex) {
breakMove = true;
ex.printStackTrace();
updateError = ex.getMessage();
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Threads.run = false;
}
}
This works good. But there are limitations:
AsyncTask is recommended for short duration threads, not for Game or Long Running Thread projects.
In latest android frameworks, only 5 AsyncTask threads can run simultaneously and rest will be in waiting queue. So it will not work if my project requires more than 5 views to update simultaneously.
What I have tried:
Rest of other Thread implementations like Runnable, Handler, Service etc. don't allow to update views. Please keep in mind that my threads are coded in separate external files or classes.
runOnUiThread is not recommended since it runs on UI thread so it will make Main thread busy all time and also it's output is noticeable after the thread which called it, ends.
I am looking for a simple clean solution like I have coded above to implement Updation of Multiple Views through Multiple Threads.
Thanks in advance
I found a solution. Simple and clean:
public class Main extends Activity{
private View root;
private Runs runs;
private boolean ready = false;
private final Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
private void init() {
setContentView(R.layout.s_main);
runs = new Runs(this);
root = findViewById(R.id.root);
//
ViewTreeObserver vto = root.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
root.getViewTreeObserver().removeOnPreDrawListener(this);
ready = true;
return true;
}
});
}
private void startRuns() {
try {
runs.run();
Threads.run = true;
} catch (Exception ex) {
Alerts.alert(context, ex.getMessage());
}
}
public void start(View view) {
try {
if (ready && !Threads.run) {
startRuns();
} else {
Threads.pause = !Threads.pause;
}
} catch (Exception ex) {
ex.printStackTrace();
Alerts.alert(context, ex.getMessage());
}
}
}
public class Runs implements Runnable {
private int count;
private Handler handler;
private TextView view1, view2;
public Runs(Activity activity) {
count = 0;
handler = new Handler();
view1 = activity.findViewById(R.id.txv1);
view2 = activity.findViewById(R.id.txv2);
}
#Override
public void run() {
if (!Threads.pause) {
update();
}
handler.postDelayed(this, Threads.sleep);
}
private void update() {
view1.setText(String.valueOf(count++));
view2.setText(String.valueOf(Math.random() * 100));
}
}
I working with this app - https://github.com/googlesamples/android-AppUsageStatistics . I'm new in android and I'm trying to refresh the view in every second. Now the view is refreshing in onCreate() or onResume().
I'm trying to use this:
public class MainActivity extends AppCompatActivity implements UsageContract.View {
private ProgressBar progressBar;
private TextView permissionMessage;
private UsageContract.Presenter presenter;
private UsageStatAdapter adapter;
public static Context myContext;
private long getStartTime() {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
permissionMessage = (TextView) findViewById(R.id.grant_permission_message);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new UsageStatAdapter();
recyclerView.setAdapter(adapter);
permissionMessage.setOnClickListener(v -> openSettings());
presenter = new UsagePresenter(this, this);
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
presenter.retrieveUsageStats();
throw new RuntimeException(e);
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
}
private void openSettings() {
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
}
#Override
protected void onResume() {
super.onResume();
showProgressBar(true);
presenter.retrieveUsageStats();
}
#Override
public void onUsageStatsRetrieved(List<UsageStatsWrapper> list) {
showProgressBar(false);
permissionMessage.setVisibility(GONE);
adapter.setList(list);
}
#Override
public void onUserHasNoPermission() {
showProgressBar(false);
permissionMessage.setVisibility(VISIBLE);
}
private void showProgressBar(boolean show) {
if (show) {
progressBar.setVisibility(VISIBLE);
} else {
progressBar.setVisibility(GONE);
}
} }
but is not working. What am I doing wrong? Why the view in not refreshing? Does anybody have an idea?
Ideally your view wouldn't be polling the presenter for updates, but to continue this way try using a timer to run your recurring task, eg:
private Timer updateTimer;
#Override
protected void onStart() {
super.onStart();
updateTimer = new Timer();
updateTimer.schedule(new TimerTask() {
#Override
public void run() {
presenter.retrieveUsageStats();
}
}, 0, 1000);
}
#Override
protected void onStop() {
super.onStop();
if(updateTimer != null) {
updateTimer.cancel();
updateTimer = null;
}
}
I want to update my text view on the receivers side as soon as I receive the message. I have the following code which creates the instance of main activity and uses it in the Broadcast receiver to update UI. But the text view isn't getting updated??
public class Mainactivity extends activity{
private static MainActivity ins;
public static MainActivity getInst()
{
return ins;
}
protected void onCreate(Bundle savedInstanceState) {
ins=this;}
public void updateUI(final String s)
{
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
TextView textView=(TextView) findViewById(R.id.textView);
textView.setText(s);
}
});
}
In the smsreceiver class
public class smsreceiver extends BroadcastReceiver
{
try{
if (MainActivity.getInst()!=null)
MainActivity.getInst().updateUI(str);
}
catch(Exception e){
e.printStackTrace();
}
}
Please help me out!!
What if you try like below:
public class Mainactivity extends activity{
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.your_layout);
//register your receiver
}
protected void onDestroy() {
//unregister your receiver
super.onDestroy();
}
public void updateUI(final String s)
{
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
TextView textView=(TextView) findViewById(R.id.textView);
textView.setText(s);
}
});
}
private class smsreceiver extends BroadcastReceiver
{
try{
updateUI(str)
}
catch(Exception e){
e.printStackTrace();
}
}
}
this is my code base you code it can work
public class MainActivity extends Activity
{
private static MainActivity ins;
private Button mButton;
public static MainActivity getInst()
{
return ins;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ins = this;
mButton = (Button) findViewById(R.id.but_send);
mButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent inten = new Intent(MainActivity.this, SmsReceiver.class);
inten.setAction("com.example.demo.action.info");
sendBroadcast(inten);
}
});
}
public void updateUI(final String s)
{
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
TextView textView=(TextView) findViewById(R.id.tv_info);
textView.setText(s);
}
});
}
}
public class SmsReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
try {
if (MainActivity.getInst() != null)
MainActivity.getInst().updateUI("Hello World");
} catch (Exception e) {
e.printStackTrace();
}
}
}
<receiver android:name="com.example.demoexample.SmsReceiver" >
<intent-filter>
<action android:name="com.example.demo.action.info" />
</intent-filter>
</receiver>
I'm facing a problem: I created two Activities.
One is the main Activity, which has a Button.
When I click this Button, the second Activity starts.
The second Activity uses an Asynctask in which a number is incremented from 1 to 10 and displays this number in a Textview
What I'm facing is that when I click the back Button while the Asynctask has not completed and then again go to the second Activity the Asynctask is not run from start immediately.
I know because in background when it completed the old task then it again starts a new task. Is there a way to fix this when destroying the Activity it also destroy the Asynctask?
Here is video sample for my problem.
Code for Main Activity:
public class MainActivity extends AppCompatActivity {
Button bt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt = (Button) findViewById(R.id.bt);
bt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this,SecondAcitivity.class);
startActivity(i);
}
});
}
}
Code of Second Activity:
public class SecondAcitivity extends AppCompatActivity {
TextView t1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second_acitivity);
t1 = (TextView) findViewById(R.id.t1);
OurWork obj = new OurWork();
obj.execute();
}
class OurWork extends AsyncTask<Void, Integer, String> {
#Override
protected String doInBackground(Void... params) {
int i = 0;
while (i < 11) {
try {
Thread.sleep(700);
publishProgress(i);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Successfully Completed";
}
#Override
protected void onProgressUpdate(Integer... values) {
t1.setText(values[0] + "%");
}
#Override
protected void onPostExecute(String result) {
t1.setText(result);
}
}
}
you need to cancel the task on back pressed, and you need to monitor if the task is canceled while executing the doInbackground().
1- override onbackpressed:
#Override
public void onBackPressed() {
obj.cancel(true); // where obj is the asyncTask refernce object name
super.onBackPressed();
}
2- monitor isCanceled()
#Override
protected String doInBackground(Void... params) {
int i = 0;
while (i < 11 && !isCancelled()) { // added !isCancelled()
try {
Thread.sleep(700);
publishProgress(i);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Successfully Completed";
}
on next iteration of the while loop, after cancel(true); is called,the loop will quit, and doInBackground() will return.
When you press back button , onBackPressed callback is called. so you can basically try this :
#Override
public void onBackPressed() {
if (asyncFetch.getStatus() == AsyncTask.Status.RUNNING) {
asyncFetch.cancel(true);
}
finish();
}
Try to use :
private OurWork task;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second_acitivity);
t1 = (TextView) findViewById(R.id.t1);
task = new OurWork();
task.execute();
}
#Override
public void onBackPressed() {
task.cancel(true);
super.onBackPressed();
}
AsyncTask runs in background of the activity where it was hosted. If OnPause or OnDestroy is called, AsyncTask is destroyed, so to solve this issue, Override OnResume and execute AsyncTask again.
To cancel the asyncTask even when it is running when back is pressed, add this to onBackPressed:
public class SecondAcitivity extends AppCompatActivity {
TextView t1;
static OurWork obj;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second_acitivity);
t1 = (TextView) findViewById(R.id.t1);
obj = new OurWork();
obj.execute();
}
class OurWork extends AsyncTask<Void, Integer, String> {
#Override
protected String doInBackground(Void... params) {
int i = 0;
while (i < 11) {
try {
Thread.sleep(700);
publishProgress(i);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Successfully Completed";
}
#Override
protected void onProgressUpdate(Integer... values) {
t1.setText(values[0] + "%");
}
#Override
protected void onPostExecute(String result) {
t1.setText(result);
}
}
//override onBackPressed and do this
#Override
public void onBackPressed() {
if (obj!=null && (obj.getStatus()== AsyncTask.Status.RUNNING ||
obj.getStatus()== AsyncTask.Status.PENDING ))
obj.cancel(true);
super.onBackPressed();
}
}
I know there are already quite a number of discussions about this, but none of what I found could clear my confusion.
I'm using the Android SDK for the first time and my Java Skills are rather average.
I have the following Problem:
From my MainActivity - OnCreate() fct. I start a thread (Receiver), receiving data from a SocketStream. This thread shall refresh a TextView-Element on the GUI when new data was read from the stream.
What is a simple but proper way to do so? I read something about ASyncTask, but did not understand how to implement it.
public class MainActivity extends Activity
{
ExecutorService myExecutor;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("this is a test"); System.out.flush();
try
{
myExecutor = Executors.newCachedThreadPool();
myExecutor.execute(Receiver.getInstance());
}
catch (IOException e)
{
e.printStackTrace();
}
}
...
public class Receiver implements Runnable
{
[...]
public void run()
{
while (true)
{
//blocking system-io-call to read data from socket..
//extract information
// *** update textView *** ??
}
}
}
You can implement handler in GUI thread to change GUI (in MainActivity in your case):
public Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
//refresh textview
}
};
and than call it from another threads
activity.handler.sendEmptyMessage(what);
You can write your own constructor for Receiver:
public class Receiver implements Runnable
{
[...]
MainActivity activity;
public Receiver(MainActivity activity){
this.activity = activity;
}
public void run()
{
while (true)
{
//blocking system-io-call to read data from socket..
//extract information
// *** update textView *** ??
activity.handler.sendEmptyMessage(0);
}
}
}
You can use runOnUiThread
public class Receiver implements Runnable
{
[...]
public void run()
{
while (true)
{
//blocking system-io-call to read data from socket..
//extract information
runOnUiThread(new Runnable() {
public void run() {
// *** update textView *** ??
}
});
}
}
}
this is a example:
create Counter class :
public class Counter implements Runnable
{
private ICounterEvents listener;
public static Thread OBJ_THREAD = null;
public Counter()
{
OBJ_THREAD = new Thread(this);
}
public void setCountListener(ICounterEvents listener)
{
this.listener = listener;
}
public void start()
{
OBJ_THREAD.start();
}
#Override
public void run()
{
for(int i = 0; i < 100; i++)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Message msg = Message.obtain();
msg.obj = i;
this.handler.sendMessage(msg);
}
}
private Handler handler =
new Handler()
{
#Override
public void handleMessage(Message msg)
{
if(Counter.this.listener != null)
{
int value = (Integer)msg.obj;
Counter.this.listener.countChanged(value);
}
}
};
}
and create a interface class:
public interface ICounterEvents
{
public void countChanged(int value);
}
and than in your main layout create a textview and a button,
and use this code in onCreate method in MainActivity:
public class MainActivity extends Activity implements ICounterEvents, OnClickListener
{
private TextView txtCounter;
private Button btnStart;
private Counter counter;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setupViews();
}
private void setupViews()
{
this.counter = new Counter();
this.counter.setCountListener(this);
this.txtCounter = (TextView)findViewById(R.id.txtCount);
this.btnStart = (Button)findViewById(R.id.btnStart);
this.btnStart.setOnClickListener(this);
}
#Override
public void onClick(View v)
{
this.counter.start();
}
public void countChanged(int value)
{
try
{
this.txtCounter.setText(value + "");
}
catch (Exception e)
{
}
}
}