I am making an inhouse navigation application with google glass. I am sending orientation data from the google glass to the android phone true WIFI (SOCKETS). My code is running fine and ok, but after about 25 seconds the receiving thread (on the phone) stops for 5 seconds and then continues, this is very annoying.
I tested this by closing the connection (when i see the thread becoming unresponsive) and it takes 5 seconds for the debugger to hit the breakpoint that i set in the application (obviously this is the thread that is causing the problem)
The weird thing is that the UI thread is still responsive i can move every object in the activity, it is just the specific thread containing the connection (Socket) that is getting blocked. I will post the code (i suspect memory leak) so that maybe someone can give me a indication why this could be happening, any advice is welcome.
CHANGED CODE AFTER COMMENT:
public class GlassOrientationSocket implements Runnable {
private final static int PORT = 6604;
private static Socket mClientSocket;
private static String mResult;
private static Handler handler = null;
private static boolean threadIsRunning;
private static BufferedReader inputReader;
public GlassOrientationSocket(Handler handler) {
this.handler = handler;
this.threadIsRunning = true;
}
public void terminateThread() {this.threadIsRunning = false;}
#SuppressWarnings("deprecation")
#Override
public void run() {
//Replace this with ip-address of android server (aka Google glass) // put ip on NFC TAG for easier configuration
String ServerIP = SECRET;
//port should be same as ServerSocketActivity
try {
mClientSocket = new Socket(serverIP, PORT);
inputReader = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
int angleCutter;
int orientationCutter;
float[] results = new float[2];
while(threadIsRunning)
{
if (((mResult = inputReader.readLine()) != null)) {
angleCutter = mResult.indexOf("|", 0);
orientationCutter = mResult.indexOf("|", angleCutter + 1);
results[0] = Float.valueOf(mResult.substring(0, angleCutter));
results[1] = Float.valueOf(mResult.substring(angleCutter + 1, orientationCutter));
Message msg = new Message();
msg.arg1 = 1;
msg.obj = results;
handler.sendMessage(msg);
} else {
if (mClientSocket != null) mClientSocket.close();
Message msg = new Message();
msg.arg1 = 2;
handler.sendMessage(msg);
threadIsRunning = false;
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}}
////
public class CompassMonitor {
static protected ArrayList<CompassListener> listeners=new ArrayList<CompassListener>();
static protected GlassOrientationSocket monitor=null;
private static Handler msgHandler = new Handler();
private static CompassListener orientationListener;
private static float[] dataReturned = new float[2];
static public synchronized void registerListener(Context context,CompassListener listener){
if(listeners.size()==0){
orientationListener = listener;
monitor=new GlassOrientationSocket(msgHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.arg1) {
case 1:
dataReturned = (float[])msg.obj;
notifyListeners(dataReturned[0],dataReturned[1],"Useless Parameter");
break;
case 2:
unregisterListener(orientationListener);
monitor.terminateThread();
}
}
});
Thread t = new Thread(monitor);
t.start();
}
listeners.add(listener);
}
static synchronized public void unregisterListener(CompassListener listener){
if (listeners != null && listener != null)
listeners.remove(listener);
}
static synchronized protected void notifyListeners(float azimuth,float angle, String direction){
for(CompassListener l:listeners){
try{
l.onCompassChanged(azimuth,angle,direction);
}catch(Exception ex){}
}
}}
Related
I try to build an app, that vibrates with a certain pattern depending on the value of a variable that comes constantly with a stream from a video game.
I managed to display telemetry data on the TextView continiously. What I want is the phone to vibrate for a certain lenght in ms or pattern, depending on the value of gforce in Thread2. What I would like to achieve is, the following sequence. Read the stream, display ias, gforce and vertical on the TextView, check gforce value, if value>=2, vibrate(choose length or pattern by gforce value), pause 500ms, repeat.
I know about the lack of connection security in my code, but that is something for later.
I do have the problem of thread management here, that I cant solve. The stream comes from a Java server socket and has an output of around one line per ms.
This the code I try to make that work with.
public class MainActivity extends AppCompatActivity {
Thread Thread1 = null;
EditText etIP, etPort;
TextView tvMessages;
String SERVER_IP;
int SERVER_PORT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etIP = findViewById(R.id.etIP);
etPort = findViewById(R.id.etPort);
etIP.setText("192.168.178.61");
etPort.setText("31091");
tvMessages = findViewById(R.id.tvMessages);
Button btnConnect = findViewById(R.id.btnConnect);
btnConnect.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onClick(View v) {
tvMessages.setText("");
SERVER_IP = etIP.getText().toString().trim();
SERVER_PORT = Integer.parseInt(etPort.getText().toString().trim());
Thread1 = new Thread(new Thread1());
Thread1.start();
}
});
}
PrintStream output;
BufferedReader input;
Socket socket;
String message;
class Thread1 implements Runnable {
#Override
public void run() {
try {
socket = new Socket(SERVER_IP, SERVER_PORT);
socket.setTcpNoDelay(true);
output = new PrintStream(socket.getOutputStream(), true);
input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
runOnUiThread(new Runnable() {
#Override
public void run() {
tvMessages.setText("Connected\n");
}
});
new Thread(new Thread2()).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Thread2 implements Runnable {
private double ias;
private double gforce;
private double vertical;
#Override
public void run() {
while (!(socket.isClosed())) {
try {
message = input.readLine();
if (message != null && message.contains("IAS")) {
ias = Double.parseDouble(message.substring(5, (message.indexOf(" ", 5))));
gforce = Double.parseDouble(message.substring(message.indexOf("Gy:") + 4, message.indexOf("Gz:") - 1));
vertical = Double.parseDouble(message.substring(message.indexOf("vertical:") + 10, message.indexOf("Gx:") - 1));
}
if (message != null && !(message.equals("exit"))) {
runOnUiThread(new Runnable() {
#Override
public void run() {
System.out.println(message);
tvMessages.setText("IAS: " + ias + "\n" +
"G: " + gforce + "\n" +
"Vertical: " + vertical + "\n");
}
});
//do trigger Vibration handler and make the phone vibrate in a pattern relative to gforce
new VibrationHandler((int)gforce).start();
//at this point sleep for n ms
} else {
Thread1 = new Thread(new Thread1());
Thread1.start();
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class VibrationHandler extends Thread{
int pattern;
public VibrationHandler(int pattern){
this.pattern=pattern;
}
public void run(){
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
// Vibrate for 500 milliseconds
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(pattern*50, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
//deprecated in API 26
v.vibrate(pattern*50);
}
}
}
}
Thank you a lot for your help.
I would like to ask a question about my code in Wifi client communication. I am communicating with a Raspberry Pi as server.
The architecture of my code is:
Main Activity: I have the Handler class and I launch in the OnCreat the first Thread (Thread1) that takes care of establishing the wifi connection.
public class MainActivity extends AppCompatActivity {
public int serverPort = 40000;
public String serverIP = "10.177.86.212";
public WiFiConnector wifiConnection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editTextWE = (EditText) findViewById(R.id.editText_WE);
wifiConnection = new WiFiConnector(serverIP, serverPort);
Handler mHandler = new MyHandler();
WiFiConnector.Thread1 = new Thread(new WiFiConnector.Thread1(mHandler,true));
WiFiConnector.Thread1.start();
}
private class MyHandler extends Handler {
private byte[] bytes = null;
#Override
public void handleMessage(Message msg) {
bytes = msg.getData().getByteArray("KEY");
if(bytes!= null){
for (int i = 0; i < bytes.length; i++){
Log.d("Data received", "value " + (0xFF & bytes[i]) );
}
for (int i=0; i<bytes.length; i++) {
editTextWE.setText(editTextWE.getText()+ "Server says: " + bytes.length + " "+ (0xFF & bytes[i]) + "\n");
}
}
}
}
}
WifiConnector class: Thread1 and Thread2 are sharing the handler coming from the Main Activity. Thread1 send a command to Raspberry Pi to let it start sending data. Thread2 is dedicated to read data received from the server.
public class WiFiConnector {
static String serverIP;
static int serverPort;
public static Thread Thread1 = null;
//Constructor
public WiFiConnector(String IP, int port) {
serverIP = IP;
serverPort = port;
}
public static class Thread1 extends Thread implements Runnable {
private Handler handler1;
boolean firsttime = false;
OutputStream out ;
public Thread1(Handler handler_1, boolean firsttime) {
this.handler1 = handler_1;
this.firsttime = firsttime;
}
public void run() {
Socket socket = null;
try {
//Writing to a Socket
InetAddress serverAddress = InetAddress.getByName(serverIP);
socket = new Socket(serverAddress, serverPort);
out = new DataOutputStream(socket.getOutputStream());
if(firsttime){
//I send "B" to Raspberry to let him start sending data
out.write("B".getBytes());
this.fisrttime = false;
}
Thread2 comThread = new Thread2(socket, handler1);
new Thread(comThread).start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static class Thread2 implements Runnable {
public Socket clientSocket;
private Handler handler_2;
public DataInputStream in;
public byte[] bytes = new byte[13];
public Message msg;
public Thread2(Socket clientSocket, Handler handler2) {
this.clientSocket = clientSocket;
this.handler_2 = handler2;
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
if (Looper.myLooper() == null) {
Looper.prepare();
}
this.in = new DataInputStream(clientSocket.getInputStream());
in.readFully(bytes);
if (in != null) {
for (int i = 0; i < bytes.length; i++){
Log.d("Data received", "valuewifi " + (0xFF & bytes[i]) );
}
msg = new Message();
Bundle b = new Bundle();
b.putByteArray("KEY", bytes);
msg.setData(b);
handler_2.sendMessage(msg);
} else {
Thread1 = new Thread(new Thread1(handler_2,false));
Thread1.start();
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
Looper.loop();
}
}
}
NOW THE PROBLEM IS:
I am receiving correctly my data (package of 13 bytes each) from Raspberry Pi, indeed:
Log.d("Data received", "valuewifi " + (0xFF & bytes[i]) );
prints correctly my values. Then I create the message to be sent to the handler in MainActivity. the Bundle contains (I have verified) the same values of the input stream received, but the message printed in the Handler of the MainActivity:
Log.d("Data received", "value " + (0xFF & bytes[i]) );
substitutes the first byte value of each message (I am trying to get 2 package each communications with the RPi) with 66 that actually is the ASCII code of "B" that I sent to start the data sending from Raspberry Pi.
PLEASE DO YOU HAVE ANY IDEA ON WHY THIS IS HAPPENING?
Many thanks for your help in advance!:)
Well, I have found that in Thread2 if I put
public byte[] bytes = new byte[13];
inside the run{..} before
in.readFully(bytes);
The exchange of message happens perfectly. Otherwise I only get in MainActivity the last package of byte received from the server.
Any suggestion on why does it happen?
Thanks!
First, I extended IntentService to build my DonwloadService class. It was OK, but a download queue had been created and 2nd download had to wait for the 1st to finished etc.
So I decided to extend Service class (following the docs). I've just added download function and function for publishing results (listened by receiver in activity):
public class DownloadService extends Service
{
private static final String TAG = DownloadService.class.getSimpleName();
public static final String EXTRA_DOWNLOAD = "EXTRA_DOWNLOAD";
public static final String EXTRA_POSITION = "EXTRA_POSITION";
public static final String INTENT_NOTIFICATION = "INTENT_NOTIFICATION";
public static final String EXTRA_RESULT = "EXTRA_RESULT";
public static final String EXTRA_PROGRESS = "EXTRA_PROGRESS";
public static final String EXTRA_PATH = "EXTRA_PATH";
public static final String EXTRA_INDETERMINABLE = "EXTRA_INDETERMINABLE";
public static final int RESULT_PROGRESS = 123;
private int mResult = Activity.RESULT_CANCELED;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler
{
public ServiceHandler(Looper looper)
{
super(looper);
}
#Override
public void handleMessage(Message message)
{
// Download file
download(message.getData());
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(message.arg1);
}
}
#Override
public void onCreate()
{
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread handlerThread = new HandlerThread("DownloadServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = handlerThread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message message = mServiceHandler.obtainMessage();
message.setData(intent.getExtras());
message.arg1 = startId;
mServiceHandler.sendMessage(message);
return START_REDELIVER_INTENT;
}
#Nullable
#Override
public IBinder onBind(Intent intent)
{
// No binding provided
return null;
}
#Override
public void onDestroy()
{
Log.d(TAG, "Service done");
}
/**
* Handles file download
*
* #param bundle
*/
private void download(Bundle bundle)
{
if (bundle != null) {
return;
}
Download download = bundle.getParcelable(EXTRA_DOWNLOAD);
int position = bundle.getInt(EXTRA_POSITION, -1);
File downloadedFile = new File(Environment.getExternalStorageDirectory(), position + ".jpg");
if (downloadedFile.exists()) {
downloadedFile.delete();
}
FileOutputStream fileOutputStream = null;
int filesize = -1;
try {
fileOutputStream = new FileOutputStream(downloadedFile.getPath());
URL url = new URL(download.getUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
filesize = connection.getContentLength();
InputStream reader = new BufferedInputStream(connection.getInputStream());
byte data[] = new byte[1024];
int next = -1;
int total = 0;
while ((next = reader.read(data)) != -1) {
mResult = RESULT_PROGRESS;
total += next;
publishResult(downloadedFile.getAbsolutePath(),
(filesize > 0) ? Math.round(total * 100 / filesize) : Math.round(total / 1024),
position, filesize <= 0);
fileOutputStream.write(data, 0, next);
}
mResult = Activity.RESULT_OK;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
publishResult(downloadedFile.getAbsolutePath(), 100, position, filesize <= 0);
}
private void publishResult(String downloadPath, int progress, int positionInList,
boolean indeterminable)
{
Intent intent = new Intent(INTENT_NOTIFICATION);
intent.putExtra(EXTRA_PATH, downloadPath);
intent.putExtra(EXTRA_PROGRESS, progress);
intent.putExtra(EXTRA_POSITION, positionInList);
intent.putExtra(EXTRA_INDETERMINABLE, indeterminable);
intent.putExtra(EXTRA_RESULT, mResult);
sendBroadcast(intent);
}
}
But still there is a download queue, no parallel downloads.
If you want to run a task repeatedly on different sets of data, but you only need one execution running at a time, an IntentService suits your needs. To automatically run tasks as resources become available, or to allow multiple tasks to run at the same time (or both), you need to provide a managed collection of threads. To do this, use an instance of ThreadPoolExecutor, which runs a task from a queue when a thread in its pool becomes free. To run a task, all you have to do is add it to the queue.
Reference:
Creating a Manager for Multiple Threads
I refactoring my threads in order to avoid memory leaks, and I got 2 errors regarding handler and startActivityForResult being called from within the thread ( dealing with GoogleDrive)
I have in my DownloadActivity :
public class DownloadActivity extends Activity {
....
private void getFolderId(){
getFolderIdThread = new GetFolderIdThread();
getFolderIdThread.start();
}
private static class GetFolderIdThread extends Thread {
private Boolean mRunning = false;
#Override
public void run() {
mRunning = true;
fResultList = new ArrayList<File>();
Files f1 = mService.files();
Files.List request = null;
aFolderId = null;
do {
try {
request = f1.list();
String aQuery = "'root' in parents and mimeType='application/vnd.google-apps.folder' and title='"+ aFolderName + "'";
request.setQ(aQuery);
FileList fileList = request.execute();
fResultList.addAll(fileList.getItems());
request.setPageToken(fileList.getNextPageToken());
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION); <= THIS RAISES THE ERROR
} catch (IOException e) {
e.printStackTrace();
if (request != null){
request.setPageToken(null);
}
}
} while (request.getPageToken() !=null && request.getPageToken().length() > 0);
if (fResultList.size() == 0) {
Log.d(TAG, "cannot find the training folder at root level");
Message msg = handler.obtainMessage(); <= THIS RAISES THE ERROR
Bundle bundle = new Bundle();
bundle.putInt("msgKey", DownloadActivity.NO_TRAININGS_FOLDER);
msg.setData(bundle);
handler.sendMessage(msg); <= THIS RAISES THE ERROR
} else {
File folder = fResultList.get(0);
aFolderId = folder.getId();
getFolderContents(); <= THIS RAISES THE ERROR
}
}
public void close() {
mRunning = false;
}
}
I have the handler defined in my activity
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
...
}
}
and the onActivityResult
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_ACCOUNT_PICKER:
....
break;
}
}
what are my options to bypass this error ?
Your GetFolderIdThread class is static and a static nested class cannot reference non-static methods and fields in the instance of the outer class that created it. Such a nested class can only access static methods and fields in your Activity. Remove static from the class definition and I think your problem will resolve.
You also need to post your call to startActivityForResult on the UI thread. Of course you should be able to use your handler for that, something like:
handler.post(new Runnable()
{
public void run()
{
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
}
});
Make sure your thread can gracefully complete as well when you do that because it will continue to run.
I am doing an Android service that gives content to other apps that can register as callback.
I am not 100% sure about how the Android Handler class works, so can someone confirm me that this code is thread safe?
public class MyService extends Service {
private static final String MESSAGE = "message";
private final RemoteCallbackList<IMyCallback> readerCallbacks = new RemoteCallbackList<IMyCallback>();
private static final int REPORT_MSG = 1;
private Thread readerThread;
#Override
public void onCreate() {
readerThread = new Thread(readerRunnable);
readerThread.setDaemon(true);
readerThread.start();
}
private Runnable readerRunnable = new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
// Blocking call
byte[] message = JniCommunicator.readMessage();
if (message == null || message.length == 0) {
continue;
}
Bundle b = new Bundle();
b.putByteArray(MESSAGE, message);
Message m = readHandler.obtainMessage(REPORT_MSG);
m.setData(b);
readHandler.sendMessage(m);
}
}
};
private final Handler readHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MSG:
byte[] message = msg.getData().getByteArray(MESSAGE);
// Broadcast the new message to all clients
final int N = readerCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
readerCallbacks.getBroadcastItem(i).newMessage(message);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
readerCallbacks.finishBroadcast();
break;
}
}
};
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IService.Stub mBinder = new IService.Stub() {
public void registerCallback(IMyCallback cb) {
if (cb != null)
readerCallbacks.register(cb);
}
public void unregisterCallback(IMyCallback cb) {
if (cb != null)
readerCallbacks.unregister(cb);
}
};
}
In particular, if someone calls unregisterCallback() while the Handler is in the for loop, will it crash?
From my understanding, the Handler run in the same thread, so it is thread safe, but I am not sure.
Thanks
Handlers are thread safe, that is their entire purpose.
I'll agree that the documentation on the thread safety of handlers isn't the best but it would be very ironic if a class designed to communicate between thread weren't thread safe.
About the remote callbacks, they are also designed to be thread safe, you should read the documentation on this, it states clearly:
Performs locking of the underlying list of interfaces to deal with multithreaded incoming calls, and a thread-safe way to iterate over a snapshot of the list without holding its lock
All you have to make sure is that all variables multiple thread access are thread safe (which they are in your case) and that they aren't being changed (yours are final so no worries there either)