Android Threads and Messaging - java

I'm working on an Android app that uses uses a background worker thread. I need to be able to send messages to the thread from the activity, but I can't quite figure it out.
I have one activity, and one thread to do work in the background. I want to start the thread and be able to send messages (arguments, objects, etc.) to it when needed. I've mastered sending messages from the thread to the activity (by passing the activity's handler to the thread, and using that to send messages), but whenever I attempt to send messages from activity to thread, the app crashes.
I've tried following a good 10-12 tutorials that I've found online, all of which seemed to have a different way of doing things, but I still haven't gotten this to work correctly. Could someone please point me in the right direction?
An example simple activity:
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savesInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private void doWork() {
Worker worker = new Worker();
worker.start();
worker.handler.sendEmptyMessage(0);
}
}
An example simple thread:
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
public class Worker extends Thread {
public Handler handler;
public void run() {
Looper.prepare();
handler = new Handler() {
public void handleMessage(Message msg) {
Log.d("Worker.run()", "Got message saying " + msg.what);
}
};
Looper.loop();
}

You have to read how to use and that for is Looper first and don't forget to stop looper at the end Goodluck
public class Worker extends Thread {
public Handler handler;
#Override
public run() {
Looper.prepare();
//initialization will take a little time you if you want to send message check if handler != null
handler = new Handler() {
public void handleMessage(Message msg) {
Log.d("Worker.run()", "Got message saying " + msg.what);
}
};
Looper.loop();
}
public void sendMessage(Message m)
{
while(handler == null);
handler.sendMessages(m);
}

Related

Android: onCreate boolean not changing

In an android application that I am developing Im using a thread, and to make sure I dont get the "java.lang.IllegalStateException: System services not available to Activities before onCreate()" I use a boolean called donecreate. Problem is that Android studio says I have a "java.lang.NullPointerException at picLoop.run(picLoop.java:24)"
Code main class:
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
public class main extends Activity {
public Boolean donecreate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new eyeCanvas(this));
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
docreate();
}
public void docreate(){
donecreate = true;
}
public void checkHead(){
AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
if(am.isWiredHeadsetOn()){
Toast.makeText(getApplicationContext(), "HEADPHONES", Toast.LENGTH_LONG).show();
}
}
}
Code: pic loop
import android.graphics.Canvas;
//**Threading
public class picLoop extends Thread {
private eyeCanvas eye;
private main main = new main();
public picLoop(eyeCanvas eye) {
this.eye = eye;
}
#Override
public void run(){
Canvas c = null;
while(true) {
if(main.donecreate){ //<-- where error is
main.checkHead();
}
try {
// head.onCreate(Bundle savedInstanceState);
c = eye.getHolder().lockCanvas();
synchronized (eye.getHolder()) {
eye.onDraw(c);
}
} finally {
if (c != null) {
eye.getHolder().unlockCanvasAndPost(c);
}
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Also if you guys could give me feedback on how I submitted, It would help :)
You can't create activities like you're trying to do. You can NEVER EVER do 'new Activity()', as the activity needs to be launched by the system to get set up properly and go through its lifecycle as intended.
So remove the line private main main = new main();.
To do what you're trying, make the boolean a static variable.
Change
public Boolean donecreate;
to
public static Boolean donecreate;
Then you can access it like you're trying to do, without creating an instance of main Activity.
There are a large number of things wrong with the assumptions you're making. Firstly, if your Thread requires your Activity to be created, don't start it until your Activity is created. Manage the lifecycle of this object within the Activity itself, i.e.:
#Override
public void onStart() {
super.onStart();
// Start your work here
}
#Override
public void onStop() {
super.onStop();
// Stop your work here
}
Secondly, please don't use the static access approach being recommended -- that makes the assumption that there is only one Activity instance (which is wrong immediately on a configuration change, and/or if you start another instance of that Activity in the task). And even if that assumption were true, you would need to set it back to false in onDestroy() (still, don't do that).
try setting donecreate to false initially
public class main extends Activity {
public Boolean donecreate = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
You can request system service on application context, look at this answer.
So create a static variable in application class, initialize it like instance = this; in onCreate of Application class and then you'll be able to get app context whenever you want.

handler, runnable and services: I put everything together and it doesn't really work

I wanted to create a service, that runs a method after 10 seconds over and over again, until I stop it. It doesn't work. Can somebody help me?
package com.example.tauben;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
public class Reminder extends Service {
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
TimerTask task = new TimerTask() {
public void run(){
f();
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, 0, 10000);
}
public void f(){
Toast t = Toast.makeText(this, "Service is still running", Toast.LENGTH_SHORT);
t.show();
}
}
And this is how I start the service.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(Log_TAG,"_____________RESTART__________");
Intent i= new Intent(this, Reminder.class);
startService(i);
}
Well I can think of 2 alternatives to what you are trying to achieve here.
1- Use TimerTask in set a repeating task that will call call the required method every 10 seconds.
2- Use setRepeating method of AlarmManager.
Both these alternatives are way better. You can google search the examples of both to get a better understanding.
Happy Coding :)
Edit:- I seem to got your original code working using Handler's postDelayed()
package com.example.tauben;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.widget.Toast;
public class Reminder extends Service {
Handler mHandler;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
mHandler = new Handler();
Runnable r = new Runnable() {
#override
public void run() {
f();
mHandler.postDelayed(this, 10000);
}
};
mHandler.postDelayed(r, 10000);
}
public void f(){
Toast t = Toast.makeText(this, "Service is still running", Toast.LENGTH_SHORT);
t.show();
}
}
What you have to do is bind to service and declare a method where service clients can stop the service. Inside that method you have to call handler.removeCallbacksAndMessages(null) to cancel all runable tasks.

Continuously updating GUI in java Android

I am a new java programmer in Eclipse for Android platform.
I am working on an android app which will receive multicast data through wifi interface and display the same in a TextView. The data will be updated every 1 sec.
I have the code as below. But i am facing problem in updating the GUI.in this code the network receive and gui are done in same thread i.e. main thread that's why my application in hanging.
I had tried using AsynchTask but could not succeed because i dont know how exactly to use it.
package com.example.cdttiming;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.view.Menu;
import android.widget.EditText;
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText Seconds;
Seconds =(EditText)findViewById(R.id.Seconds);
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("mydebuginfo");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
InetAddress ia = null;
byte[] buffer = new byte[65535];
MulticastSocket ms = null;
int port = 4321;
try
{
ia = InetAddress.getByName("226.1.1.1");
DatagramPacket dp = new DatagramPacket(buffer, buffer.length,ia,port);
ms = new MulticastSocket(port);
ms.setReuseAddress(true);
ms.joinGroup(ia);
while (true)
{
ms.receive(dp);
String s = new String(dp.getData(),0,dp.getLength());
Seconds.setText(s);
}
}
catch (UnknownHostException e)
{
Seconds.setText(e.getMessage());
}
catch (IOException e)
{
Seconds.setText(e.getMessage());
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Can anyone tell me how to use above code so that I may receive the data every second and display that in the TextView?
Thank u all in advance
AsyncTask does not really for your use case. If the background task needs to run continuously, you'd better just start a new thread. In the thread use a handler to communicate values to the UI. In the callback method of the handler, you can update the gui.
More info on handlers:
http://mobileorchard.com/android-app-developmentthreading-part-1-handlers/
http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html
http://developer.android.com/reference/android/os/Handler.html
Or google for good examples to get this going.
Edit:
Ok, what you need to do is start a new thread and in the run method put the part that reads from the socket.
You class create the thread as in inner class, something like:
class SocketThread extends Thread {
private final Handler mHandler;
private final WifiManager mWifiManager;
SocketThread(Handler handler, WifiManager wifiManager) {
mHandler = handler;
}
#override public void run() {
// socket code goes here
// whenever you receive a part that should update the ui,
// do something like:
final String s = new String(dp.getData(),0,dp.getLength());
mHandler.post(new Runnable() {
#override
public void run() {
update(s);
}
});
}
}
Then in your onCreate() create a handler and an instance of this thread and start it.
...
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText Seconds;
Seconds =(EditText)findViewById(R.id.Seconds);
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
Handler handler = new Handler();
SocketThread thread = new SocketThread(handler, wm);
thread.start();
...
Also define a method update in you activity. Here you will receive the text you want to set. This method will look like this:
public void update(String s) {
Seconds.setText(s);
}
I advice you to save the seconds variable (the edittext) as a global class variable. This way you can write to it from the update method without having to findById it first.
I edited your code a bit, it may have to do with the wifi code. I am not familiar with this part of the framework. But I can imagine it has something to do with the WifiManager and connecting to it. If this doesn't work, set a breakpoint and step through it, you'll see where it stops doing anything.
package com.example.timing;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
EditText Seconds;
String s;
Button button;
byte[] buffer = new byte[65535];
InetAddress ia = null;
int port = 4321;
DatagramPacket dp = new DatagramPacket(buffer, buffer.length,ia,port);
MulticastSocket ms = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Seconds = (EditText) findViewById(R.id.et_time);
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
Handler handler = new Handler();
SocketThread thread = new SocketThread(handler, wm);
thread.start();
}
class SocketThread extends Thread {
private final Handler mHandler;
SocketThread(Handler handler, WifiManager wifiManager) {
mHandler = handler;
}
#Override
public void run() {
// socket code goes here
try {
wm.setWifiEnabled(true);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
ia = InetAddress.getByName("226.1.1.1");
ms.setReuseAddress(true);
ms.joinGroup(ia);
while(true) {
ms.receive(dp);
s = new String(dp.getData(),0,dp.getLength());
update(s);
}
} catch (UnknownHostException e) {
update(e.getMessage());
} catch (IOException e) {
update(e.getMessage());
}
}
}
public void update(String s)
{
Seconds.setText(s);
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
You should not use AsyncTask for longer lasting activities - use a simple Thread and a Handler instead. In the thread, you do the WI-FI stuff (basically your while(true) loop) and whenever you want to update the UI, you pass a message to the Handler and handle the message within the UI thread.
For further information, please read the famous Painless Threading post from the Android Developers Blog.
Have a look through Handlers and Services
Do you only need to collect the data while your app is visible? The way you approach this challenge is very dependant on the needs and lifecycle of the data and communication.
AsyncTask is normally used to do one off jobs on a separate thread and then notify main thread when done.
Possibly look into a service as it is intended for long running jobs.

Background changing every second

I am trying to change background every second and this is my code:
package com.example.splasher;
import java.lang.reflect.Field;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Typeface;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
public class Views extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.view);
final ScrollTextView scrolltext=(ScrollTextView) findViewById(R.id.scrolltext);
if(MainActivity.bold.isChecked())
{
scrolltext.setTypeface(null, Typeface.BOLD);
};
if(MainActivity.italic.isChecked())
{
scrolltext.setTypeface(null, Typeface.ITALIC);
};
if((MainActivity.italic.isChecked())&&(MainActivity.bold.isChecked()))
{
scrolltext.setTypeface(null, Typeface.BOLD_ITALIC);
};
scrolltext.setTextSize(Integer.parseInt(MainActivity.tSize[MainActivity.Size.getSelectedItemPosition()]));
scrolltext.setText(MainActivity.text);
scrolltext.setTextColor(Integer.parseInt(MainActivity.colour[MainActivity.TextColour.getSelectedItemPosition()]));
scrolltext.startScroll();
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[MainActivity.BackgroundColour.getSelectedItemPosition()]));
Thread thread = new Thread()
{
public void run()
{
if(MainActivity.random.isChecked())
{
int delay = 0; // delay for 5 sec.
int period = 1000; // repeat every sec.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
int n=1;
public void run() {if (n==9)n=1;
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[n]));
}
}, delay, period);
/*int n=1;
boolean constant=true;
while (constant==true){
if (n==10) n=1;
// int randInt = new Random().nextInt(2);
// scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[randInt]));
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[n]));
n=n+1;
try
{
Thread.sleep(2000); // 1 second
} catch (Exception e)
{
e.printStackTrace();
}
}*/
}
}
};
thread.start();
// TextView textview=(TextView) findViewById (R.id.textView1);
// textview.setText(MainActivity.text);
// textview.setTextColor(Integer.parseInt(MainActivity.colour[MainActivity.TextColour.getSelectedItemPosition()]));
// textview.setBackgroundColor(Integer.parseInt(MainActivity.colour[MainActivity.BackgroundColour.getSelectedItemPosition()]));
// textview.setTextSize(Integer.parseInt(MainActivity.tSize[MainActivity.Size.getSelectedItemPosition()]));
// textview.setSelected(true);
}
}
But it force closes. Logcat shows: FATAL EXCEPTION: Timer-0; android.view.ViewRootŲcalledfromwrongThreadException: Only original threat that created a view hierarchy can touch its view.
What is wrong with this code?
You can not operate on the UI elements (like call scrolltext.setBackgroundColor()) in a thread separate from the so-called UI thread (which is basically the same thread onCreate() runs in.
To solve your issue, use a Handler. Create a class field:
Handler handler = new Handler();
And in your thread put all UI operations inside a Handler call:
handler.post(new Runnable() {
#Override
public void run () {
// All UI operations, like scrolltext.setBackgroundColor()
}
});
Your problem is
scrolltext.setBackgroundColor(Integer.parseInt(MainActivity.colour[n]));
You are updating UI from worker Thread(it not runs on UI Thread) and this is not allowed. Only Main(UI) Thread can manipulate with UI.
So you need to change it to runOnUiThread() or Handler.
Examples:
runOnUiThread(new Runnable(){
public void run() {
// do your stuff
}
});
Handler example.
Just trying to contribute here, but I have zero experience in android, so be cautions ;p
The exception message seems to suggest that the view can only be altered on the main thread. As you are running code which alters the view on a new thread, it doesn't like it.
Are you aware of a main Event-Dispatcher thread used in Android? Similar to how SWINGs GUI is updated on the EDT. If so, look for a way to tell the main drawing thread to do the final updates.
In swing, it would look something like:
EventQueue.invokeLater(new Runnable(){
public void run(){
}
}
Hope this has some meaning.

Thread remains in NEW state if started from AsyncTask by runOnUiThread

Here is an example application I wrote to reproduce the memory leak related issue i have met :
package a.b.mapleak;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MapLeak extends Activity
{
TextView callVoidText, getNewDataText, getDataStringText;
Button callVoidButton, getNewDataButton, getDataStringButton;
Handler callbackHandler;
MemoryLeak memleak;
private LoadAccountsTask mLoadAccountsTask = null;
private class LoadAccountsTask extends AsyncTask<Void, Void, Object[]> {
private int mCursor1;
private int mCursor2;
private Context context;
private UITask t = new UITask();
#Override
protected Object[] doInBackground(Void... params) {
runOnUiThread(t);
// Create the summaries cursor
mCursor1 = 1;
mCursor2 = 2;
return new Object[] { mCursor1, mCursor2};
};
private class UITask extends Thread {
#Override
public void run() {
int i = 2;
while (i>0) {
i--;
}
Thread.State state=t.getState();
Log.e(ALARM_SERVICE, "Thread state in run is " + state.toString());
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getDataStringText = (TextView) findViewById(R.id.getDataString_text);
getDataStringButton = (Button) findViewById(R.id.getDataString_button);
getDataStringButton.setOnClickListener(new Button.OnClickListener() {
long i = 0;
public void onClick(View v) {
if (mLoadAccountsTask != null) mLoadAccountsTask.cancel(true);
mLoadAccountsTask = (LoadAccountsTask) new LoadAccountsTask(this).execute();
}
});
}
}
I found that the thread started in runOnUiThread has never been exited. For some unknown to me reason the task was running, but the output from
Log.e(ALARM_SERVICE, "Thread state in run is " + state.toString());
every time was "Thread state in run is NEW" e.g. ''The thread has been created, but has never been started.''. So the thread was running continuously preventing "LoadAccountsTask" class from being garbage collected. Thus, in this example, every time the button is pressed - the new "LoadAccountsTask" appeared in memory.
But, if to replace
private class UITask extends Thread
with
private class UITask implements Run
there was no memory leak, the thread exited successfully. Of course state=t.getState(); will not work in this case and should be commented.
Do anybody have an explanation why it is so?
Focusing on your immediate concern, runOnUiThread() takes a Runnable. While Thread does implement Runnable, runOnUiThread() will not treat it as a Thread. Please just pass a Runnable to runOnUiThread().
Second, never call runOnUiThread() from doInBackground(). Just perform the work from that Runnable in onPreExecute() or onPostExecute(), which are executed on the main application thread.
Third, if you ever do write a real Thread, don't busy-loop in it.

Categories