Similar or the same Question has been answered here
I am creating an Android App, which sends a broadcast message to the network and prepares a list of devices responding back.
Now What I did:
I created an Activity Class DeviceManagerWindow.java which calls a thread Sender.java.
Sender.java is responsible for sending the broadcast message.
Then the DeviceManagerWindow.java calls another thread which is responsible for listening to the devices responding back. The devices responding back will be listed in the Activity as soon as the device responds back. For that I have a TableLayout named deviceList.
What code I have written:
DeviceManagerWindow.java This method is called when a button for search is pressed
public void searchDevice(View v) throws IOException, InterruptedException
{
//Log.v("TableLayout:",view.toString());
sendMulticastFlyport = new Thread(new FlyportSender(MAC));
sendMulticastFlyport.start();
new Thread()
{
public void run()
{
MulticastSocket socketComputer=null;
try
{
socketComputer = new MulticastSocket(WifiConstants.COMPUTER_RECV_PORT);
socketComputer.joinGroup(InetAddress.getByName(WifiConstants.COMPUTER_NETWORK_ADDR));
socketComputer.setSoTimeout(1*60*1000);
byte[] inBufComputer = new byte[1024];
DatagramPacket inPacketComputer = new DatagramPacket(inBufComputer, inBufComputer.length);
while(true)
{
System.out.println("Listening...");
socketComputer.receive(inPacketComputer);
System.out.println("Received");
String msg = new String(inBufComputer, 0, inPacketComputer.getLength());
DeviceInformation device = new DeviceInformation(1, msg, inPacketComputer.getAddress().toString());
addDevice(device, false, 1);
Log.v("Received:","Received Computer From :" + inPacketComputer.getAddress() + " Msg : " + msg);
//System.out.write(inPacket.getData(),0,inPacket.getLength());
System.out.println();
Thread.sleep(2000);
}
}
catch(Exception e)
{
Log.v("Exception:","During Receiving Computer: "+e.toString());
try
{
addDevice(null, true, 1);
}
catch (IOException e1)
{
Log.v("Exception:", "Computer End Error: " +e1);
}
}
finally
{
socketComputer.close();
}
}
}.start();
The following code creates a list:
public void addDevice(DeviceInformation device, boolean bool, int type) throws IOException
{
TableLayout tb = (TableLayout) findViewById(R.id.DeviceList);
Log.v("addDevice","Called");
if(bool)
{
LayoutParams layout = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
TableRow tr = new TableRow(getApplicationContext());
TextView tv = new TextView(getApplicationContext());
System.out.println(tb);
tv.setLayoutParams(layout);
tr.setLayoutParams(layout);
String message;
Log.v("addDevice","Device Timeout");
switch(type)
{
case 1:
computerEnd=true;
break;
case 2:
raspberryEnd=true;
break;
case 3:
flyportEnd=true;
break;
}
if(computerEnd && raspberryEnd && flyportEnd)
{
if(rowCounter>0)
{
message = "No More Devices";
}
else
{
message = "No Devices Found";
}
tv.setText(message);
tv.setTextColor(Color.WHITE);
if(rowCounter%2==0)
{
tr.setBackgroundColor(Color.DKGRAY);
}
else
{
tr.setBackgroundColor(Color.GRAY);
}
tv.setVisibility(1);
tr.addView(tv);
tb.addView(tr);
}
}
else
{
LayoutParams layout = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
TableRow tr = new TableRow(getApplicationContext());
TextView tv = new TextView(getApplicationContext());
tv.setLayoutParams(layout);
tr.setLayoutParams(layout);
Log.v("addDevice","Received");
String textToDisplay = device.getDeviceTypeString()+"\n"+device.getIPAddress(); //Write the text to display
tv.setText(textToDisplay);
tv.setTextColor(Color.WHITE);
Drawable img;
if(device.getDeviceType()==1)
{
img = getApplicationContext().getResources().getDrawable(R.drawable.pc);
}
else if(device.getDeviceType()==2)
{
img = getApplicationContext().getResources().getDrawable(R.drawable.raspberry);
}
else
{
img = getApplicationContext().getResources().getDrawable(R.drawable.flyport);
}
img.setBounds(0,0,70,45);
tv.setCompoundDrawables(null, null, img, null);
tv.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
}
});
if(rowCounter%2==0)
{
tr.setBackgroundColor(Color.DKGRAY);
}
else
{
tr.setBackgroundColor(Color.GRAY);
}
rowCounter++;
Log.v("Result","Device Added");
}
}
Now it is showing me an error in the logCat as:
05-11 22:01:10.165: E/AndroidRuntime(13873): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
What I have figured out from this is only the UIThread is allowed to access the Views that is created.
Previously I had tried:
new Thread()
{
public void run()
{
runOnUiThread(){
MulticastSocket socketComputer=null;
try
{
....
....
....
}
}
And that time I received an error:
Main thread cannot access Network
Before that I had tried to use synchronized methods which was called from the Receiving.java Thread File. but It also gave an error of not creating the list.
I have tried all possible ways.
Now what whould I do.?
You figured it out right. Now you can learn to either use a Handler to pass information to the UI thread (see http://www.techotopia.com/index.php/A_Basic_Overview_of_Android_Threads_and_Thread_handlers) or AsyncTask (see http://developer.android.com/reference/android/os/AsyncTask.html).
I personally prefer AsyncTask. You can paste the code which performs the search into the doInBackground() method (not need to use a separate thread, doInBackground() already does that for you) and paste the UI-related code (the list creation code) into the onPostExecute() method. Search for further examples of AsyncTask if it is not sufficiently clear how it works from the link.
EDIT: If you intend your device search code to run indefinitely, then you have to resort to Handler, as AsyncTask expects the doInBackground() method to finish before running onPostExecute(). See which option better suits your needs.
Related
I want to send value from string (distance to obstacle) to my TextView in main activity.
I tried to use Handler, but still not working (crash) or receive nothing.
A part code which receive data from HC-05 (screen where you see in debug value assignet to variable)
enter image description here
#Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while(true){
try {
bytes = inputStream.read(buffer);
final String comingMsg = new String(buffer,0,bytes);
Log.d(TAG,"InputStream: " + comingMsg);
/*mHandler2.post(new Runnable() {
#Override
public void run() {
Message message = new Message();
message.obj = comingMsg;
mHandler2.sendMessage(message);
}
});*/
}catch (IOException e){
Log.e(TAG,"Write: Error reading input." + e.getMessage());
active=false;
break;
}
}
}
Here It's parts of code from MainActivity where I tried put something to get values from service.
[I add, that for this moment i want to see something values from bluetooth in textView. Later I want to create parse string and send custom text to custom TextView - example: FL: (Front Left)- to one textView, FR: (Front Right) - to second textView]
There is method implementThreads(), because I wanted to do 6 Threads to 6 TextView which every time is refreshing value from string in Services (there I tried get value from Bluetooth Service)
Log.d(TAG,"Check intent - result");
if(getIntent().getIntExtra("result",0)==RESULT_OK){
mDevice = getIntent().getExtras().getParcelable("bonded device");
myBluetoothService = new MyBluetoothService(getApplicationContext());
startConnection(mDevice,MY_UUID);
Log.d(TAG,"Check is active service");
checkIfActive();
}
Log.d(TAG,"Check intent - connect_to_paired");
if(getIntent().getIntExtra("connect_to_paired",0)==RESULT_OK){
mDevice = getIntent().getExtras().getParcelable("bonded_paired_device");
myBluetoothService = new MyBluetoothService(getApplicationContext());
startConnection(mDevice,MY_UUID);
Log.d(TAG,"Check is active service");
checkIfActive();
}
}
#Override
public void onStart(){
super.onStart();
myBluetoothService = new MyBluetoothService(getApplicationContext());
}
public void checkIfActive(){
Log.d(TAG,"CheckIfActive: Started");
if(myBluetoothService.active){
Log.d(TAG,"CheckIfActive: Running method implementThreads()");
implementThreads();
}
}
public void implementThreads(){
Log.d(TAG,"ImplementThreads: Started");
Thread thread = new Thread(){
#Override
public void run() {
try{
sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
thread.start();
}
public void startConnection(BluetoothDevice device,UUID uuid){
Log.d(TAG,"StartConnection: Initializing connection");
myBluetoothService.startClient(device,uuid);
}
Thanks all for help, because It's very important for me !
Use this to interect with UI Thread for operations like updating textviews etc.
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//YOUR CODE HERE
Message message = new Message();
message.obj = comingMsg;
mHandler2.sendMessage(message);
}
});
Let me start by saying that if image shooting interval is anything more than 1 second it works. For example taking a picture every 2 seconds works perfectly fine. But taking a picture every second sometimes throws java.lang.RuntimeException: takePicture failed. What could be causing this kind of a behaviour?
Here is the code I use and it is in Service:
#Override
public void onCreate()
{
super.onCreate();
prefs = getSharedPreferences("general",Context.MODE_PRIVATE);
handler = new Handler();
shotInterval = prefs.getInt(getString(R.string.prefs_int_imageShootingFrequency),1);
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG).show();
} else {
cameraId = findBackFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.",Toast.LENGTH_LONG).show();
} else {
camera = Camera.open(cameraId);
}
}
cameraParameters = camera.getParameters();
cameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); //set camera to continuously auto-focus
camera.setParameters(cameraParameters);
pictureTaker.run(); // Start looping
}
Runnable pictureTaker = new Runnable() {
#Override
public void run() {
try {
takePicture();
} finally {
// 100% guarantee that this always happens, even if
// your update method throws an exception
handler.postDelayed(pictureTaker, shotInterval*1000);
}
}
};
private void takePicture(){
SurfaceView view = new SurfaceView(this);
try {
camera.setPreviewDisplay(view.getHolder());
camera.startPreview();
camera.takePicture(null, null,new PhotoHandler(getApplicationContext()));
} catch (IOException e) {
e.printStackTrace();
}
}
You should launch postDelayed() from the onPictureTaken() callback. You can check the system timer on call to takePicture() and reduce the delay respectively, to keep 1000ms repetition, but maybe once in a while, this delay will reach 0.
I cant get a progress dialog to show when I need it to. I have tried putting it in my asyncTask the ui class and the its own thread that runs on the ui and none have worked. Can anyone help me?
the method where the progressDialog method is called:
public void shareTest(View view){ //method called to jump to share activity if criteria matched
if(checkInputs()) { //call to check inputs
Share start = new Share();
boolean isConnected=start.connectToServer(); //connectToServer
Intent intent = new Intent(HomeScreen.this, Share.class); //create intent to move to share class from this activity
startProgressDialog();
if (isConnected) { //check to see if isconnected was succesful
if (Share.matchFound ){ //check to see if a match was found
progress.dismiss();
startActivity(intent); //if true jump to share activity
} else {
while (!Share.timedOut) { //While the time has not timedOut
if (Share.matchFound) { //if a share has been found
startActivity(intent); //start share activity
break; //if true then break
}
}
if (Share.timedOut) {
//send an notice that a match wasn't found
sendToast(getString(R.string.noShare)); //if not true then send Toast
}
}
}
else sendToast(getString(R.string.errServCon)); //if connection to server failed then send toast
}
}
this is the method:
void startProgressDialog() {
new Thread(new Runnable() {
#Override
public void run() { //creates a new runnable thread
// Issue command() on a separate thread
while (!Share.matchFound) { //while havent been asked to disconnect //if a new location has been recieved
activity.runOnUiThread(new Runnable() {
#Override
public void run() { //run on the ui thread act
progress.show(); //call the method that does the update
}
});
}
progress.dismiss();
}
}).start();
}
Declare a global variable like this:
ProgressDialog progress;
Wherever you want to show the progress, paste this code:
progress = ProgressDialog.show(this, "Please wait",
"Loading..", true);
When you are done, simply dismiss it:
progress.dismiss();
I am writing a piece of code for my Android Phone which will create a list of WifiDevices in the area using its personal scan.
I am getting java.lang.NullPointerException when I am creating a list.
My code goes follows:
public static synchronized void addDevice(DeviceInformation device, View v, Context con, boolean bool, int type) throws IOException {
Log.v("addDevice", "Called");
if (bool) {
TableLayout tb = (TableLayout) v.findViewById(R.id.DeviceList);
LayoutParams layout = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
TableRow tr = new TableRow(con);
TextView tv = new TextView(con);
System.out.println(v.toString());
tv.setLayoutParams(layout);
tr.setLayoutParams(layout);
String message;
Log.v("addDevice","Device Timeout");
switch(type) {
case 1:
computerEnd = true;
break;
case 2:
raspberryEnd = true;
break;
case 3:
flyportEnd = true;
break;
}
if (computerEnd && raspberryEnd && flyportEnd) {
if (rowCounter > 0) {
message = "No More Devices";
} else {
message = "No Devices Found";
}
tv.setText(message);
tv.setTextColor(Color.WHITE);
if (rowCounter % 2 == 0) {
tr.setBackgroundColor(Color.DKGRAY);
} else {
tr.setBackgroundColor(Color.GRAY);
}
tv.setVisibility(1);
tr.addView(tv);
tb.addView(tr); //This is line number 131
}
} else {
TableLayout tb = (TableLayout) v.findViewById(R.id.DeviceList);
LayoutParams layout = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
TableRow tr = new TableRow(con);
TextView tv = new TextView(con);
tv.setLayoutParams(layout);
tr.setLayoutParams(layout);
Log.v("addDevice", "Received");
String textToDisplay = device.getDeviceTypeString() + "\n" + device.getIPAddress(); //Write the text to display
tv.setText(textToDisplay);
tv.setTextColor(Color.WHITE);
Drawable img;
if (device.getDeviceType() == 1) {
img = con.getResources().getDrawable(R.drawable.pc);
} else if (device.getDeviceType() == 2) {
img = con.getResources().getDrawable(R.drawable.raspberry);
} else {
img = con.getResources().getDrawable(R.drawable.flyport);
}
img.setBounds(0,0,70,45);
tv.setCompoundDrawables(null, null, img, null);
tv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//empty
}
});
if (rowCounter % 2 == 0) {
tr.setBackgroundColor(Color.DKGRAY);
} else {
tr.setBackgroundColor(Color.GRAY);
}
rowCounter++;
Log.v("Result", "Device Added");
}
}
My Logcat error:
05-11 05:25:07.500: E/AndroidRuntime(30710): FATAL EXCEPTION: Thread-20873
05-11 05:25:07.500: E/AndroidRuntime(30710): java.lang.NullPointerException
05-11 05:25:07.500: E/AndroidRuntime(30710): at com.example.devicecontrolpanel.DeviceManagerWindow.addDevice(DeviceManagerWindow.java:131)
05-11 05:25:07.500: E/AndroidRuntime(30710): at com.connection.NetworkScanListenerRaspberry.run(NetworkScanListenerRaspberry.java:62)
05-11 05:25:07.500: E/AndroidRuntime(30710): at java.lang.Thread.run(Thread.java:864)
This is how this particular code is reached:
This code calls a Thread which in return calls this method.
public void searchDevice(View view) throws IOException, InterruptedException
{
try
{
Thread ComputerListener = new Thread(new NetworkScanListenerComputer(getApplicationContext(),view));
ComputerListener.start();
}
catch(Exception e)
{
Log.v("Exception:","Exception from SearchDevice Method"+e.toString());
}
}
This the code for the Thread:
package com.connection;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import android.content.Context;
import android.util.Log;
import android.view.View;
import com.example.devicecontrolpanel.DeviceManagerWindow;
public class NetworkScanListenerComputer implements Runnable
{
View thisView;
Context thisContext;
MulticastSocket socket = null;
DatagramPacket inPacket = null;
byte[] inBuf;
public NetworkScanListenerComputer(Context con, View v)
{
thisView = v;
thisContext = con;
try
{
socket = new MulticastSocket(WifiConstants.COMPUTER_RECV_PORT);
socket.joinGroup(InetAddress.getByName(WifiConstants.COMPUTER_NETWORK_ADDR));
inBuf = new byte[1024];
inPacket = new DatagramPacket(inBuf, inBuf.length);
socket.setSoTimeout(1*60*1000);
}
catch(Exception ioe)
{
Log.v("Exeception:","Computer Listener Exception"+ioe);
}
}
#Override
public void run()
{
try
{
while(true)
{
System.out.println("Listening...");
socket.receive(inPacket);
System.out.println("Received");
String msg = new String(inBuf, 0, inPacket.getLength());
DeviceInformation device = new DeviceInformation(1, msg, inPacket.getAddress().toString());
DeviceManagerWindow.addDevice(device, thisView, thisContext, false, 1);
Log.v("Received:","Received Computer From :" + inPacket.getAddress() + " Msg : " + msg);
//System.out.write(inPacket.getData(),0,inPacket.getLength());
System.out.println();
Thread.sleep(2000);
}
}
catch(Exception e)
{
Log.v("Exception:","During Receiving Computer: "+e.toString());
try
{
DeviceManagerWindow.addDevice(null, thisView, thisContext, true, 1);
}
catch (IOException e1)
{
Log.v("Exception:", "Computer End Error: " +e1);
}
}
finally
{
socket.close();
}
}
}
I am passing the view and the context of the application which is again redirected to the static method because static method cannot make use of them.
I figured out the problem... it is saying.. Only the original thread that created a view hierarchy can touch its views.
That is the thread (DeviceManagerWindow.java) which created the view can only access R.id.deviceList. So what should I do if I want to access it.?
The problem appears to be that you are trying to update the UI from a background Thread. You could use runOnUiThread() but even easier I think would be to make that Thread an AsyncTask. Then you can do all of the network stuff in the doInBacground() and update the UI in onPostExecute()
If this is a separate file and not an inner class then you can just pass context to the constructor so you can update the UI. Or you can return a value which tells the calling Activity to do something or not. I'm not sure but it looks like you might be calling this one at a time. If so, I would suggest calling a task to get all of the devices in the background while the UI is doing something and add them to a list from there. The other thing is it looks like you may have an infinite loop with while true in your Thread. If this is the case, I would change that to something that listens for a value to change. Hope this helps
Well, the first thing you should do is debugging, to get exactly where is your null pointer exception. But if you take a good look at your code, then there are two very probable possibilities for having a null pointer exception, the first one could be:
TableLayout tb = (TableLayout) v.findViewById(R.id.DeviceList);
because View v didn't found any view with id == "DeviceList"
or here:
if(device.getDeviceType()==1)
because device is getting passed in your method "null".
So, check out these two.
Good Luck
The project i am working on needs this type of behavior. The user will be presented with a UI that will give them the option to connect and disconnect to a server. I would also like this UI to show the status of the connection, 'connected or disconnected'. Whenever the user clicks connect, the application will start a thread that handles the connection to the server. The user will still be looking at the main UI. When they start that connection and while the connection remains, i would like the status of the connection to be 'connected'. If the connection is ever broken at any point i would like it to display disconnected. Listed below is what i have so far.
My question is... Am i doing the threading right? So that the phone will not be crushed by the server connection while it is connected?
Also, how do i get the main UI to reflect the connection status of the server and display when the connection is broken?
Thanks in advance! If you have any further questions, please let me know.
The server connection thread.
public class ConnectDevice implements Runnable {
private boolean connected;
private ObjectInputStream ois;
public void run() {
try {
InetAddress host = InetAddress.getByName("192.168.234.1");
Socket socket = new Socket(host.getHostName(), 7777);
connected = true;
while (connected) {
try {
ois = new ObjectInputStream(socket.getInputStream());
String message = (String) ois.readObject();
System.out.println("Message: " + message);
}
catch (Exception e) {
e.printStackTrace();
}
}
ois.close();
} catch (UnknownHostException e) {
e.printStackTrace();
connected = false;
} catch (IOException e) {
e.printStackTrace();
connected = false;
} /*catch (ClassNotFoundException e) {
e.printStackTrace();
connected = false;
}*/
}
}
The main UI and main class.
public class SmartApp extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.intro);
final Button firstTimeButton = (Button) findViewById(R.id.firstTimeButton);
firstTimeButton.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
// TODO Auto-generated method stub
Intent userCreationIntent = new Intent(v.getContext(), UserCreation.class);
startActivityForResult(userCreationIntent, 0);
}
});
final Button connectDeviceButton = (Button) findViewById(R.id.connectDeviceButton);
connectDeviceButton.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
//Intent connectDeviceIntent = new Intent(v.getContext(), ConnectDevice.class);
//startActivityForResult(connectDeviceIntent, 0);
Thread cThread = new Thread(new ConnectDevice());
cThread.start();
}
});
}
}
Android has a UI thread, which is the only thread that is allowed to update UI elements.
You need to have a background thread doing the work, and posting back to the UI thread when its done.
AsyncTask is an Android class designed to do just that.
Once your worker thread ends its work, and will update the UI element taken by findViewById, it will automatically change on the screen without you having to do anything else.
Check out the AsyncTask, it's tailor made for this sort of thing.