I am writing an application that contains a service that handles Bluetooth device discovery. My discovered devices are received in the ScanCallback method. I want to make sure that everything that happens inside ScanCallback is handled in the background thread and not on the main thread. My problem is that with my implementation, each callback creates a separate thread. I was wondering if this is ok or not and if not, how can I reuse the same thread to handle all the callbacks. Here is my code.
#TargetApi(21)
private ScanCallback GetMScanCallbackForApi21AndAbove() {
return new ScanCallback() {
#Override
public void onScanResult(int callbackType,final ScanResult result) {
new Thread(new Runnable() {
#Override
public void run() {
sendBtDevice(result.getDevice());
}
}).start();
}
#Override
public void onScanFailed(final int errorCode) {
new Thread(new Runnable() {
#Override
public void run() {
//Do something else
}
}).start();
};
}
What you can do is use ExecutorService
And you can have different types of services, single thread/pooling or whatever fits your needs.
Related
I want to save data to DB in new Thread and after that show toast on the UI.
Method for saving:
public void addToBasket(String text) {
new Thread(() -> {
//emulate save
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//after that I need say ti UI thread - show Toast!
}).start();
}
I call this method:
BasketService.me().addToBasket(result.getContents());
I do now want use AsyncTask for this. Please tell me the best way to implement such tasks
batter to use:
runOnUiThread(new Runnable() {
public void run() {
//Do what ever you want do man
}
});
runOnUiThread() method to manipulate your UserInterface from background threads.
In case of callback from a nonUi thread to Ui thread you can use runOnUiThread()(As specified above) or Handler. Below is a example of using handler.
protected static final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
protected void onSuccessInMainThread(final R result, final Bundle bundle) {
mainThreadHandler.post(new Runnable() {
#Override
public void run() {
callback.onSuccess(result, bundle);
}
});
}
protected void onErrorInMainThread(final Exception error) {
mainThreadHandler.post(new Runnable() {
#Override
public void run() {
callback.onError(error);
}
});
}
Can I create onClickListener() for a button in more than 1 threads that are executing simultaneously?
Would that listener be called individually in every thread?
No, a button has only one onClickListener. Setting a second one overwrites any listener set previously. And that function will only be called on the UI thread. You can pass messages to multiple threads from that function though.
There are a lot of ways to communicate between threads. Since you are wondering how to pass something over to the thread, here is a simple example:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
...
//Start a thread you need to
anotherThread = new AnotherThread();
anotherThread.start();
}
protected void onResume() {
...
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
anotherThread.sendData(message);
}
});
}
}
public class AnotherThread extends Thread {
//Instantiate handler to associate it with the current thread.
//Handler enqueues all tasks in the MessageQueue using Looper
//and execute them upon coming out of queue;
private Handler handler;
#Override
public void run() {
try {
//Here we create a unique Looper for this thread.
//The main purpose of which to keep thread alive looping through
//MessageQueue and send task to corresponding handler.
Looper.prepare();
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
//manage incoming messages here
String value = msg.getData().getString("key");
}
};
Looper.loop();
} catch (Throwable e) {
e.printStackTrace();
}
}
public synchronized void sendData(Message message) {
handler.post(new Runnable() {
#Override
public void run() {
handler.sendMessage(message);
}
});
}
}
To get more about thread communication I'd recommend you the following:
1, 2, 3.
From my generic fragment, I have this method:
protected void loadDataListWithDelay() {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadDataList();
}
}, DELAY_START_LOADING);
}
This method allows to start a new loading data from server (with Volley networking).
In my loadDataList() method there are some views visibility setting:
protected void loadDataList(String url, ArrayList<BaseFilters> filters,
String query, boolean byPassSearchMode) {
...
mLoadingDataListView.setVisibility(View.INVISIBLE);
mListContainer.setVisibility(View.VISIBLE);
...
This code runs perfectly, but I have this crash this morning on mobile with Android 6.0.1.
Could you help me guys?
I will give you a good advice, if you want to do postDelayed, take a view in your fragment (or any view), and do postDelayed with it. i.e. textView.postDelayed(.. This way you can be sure you are on the ui thread.
Why not use runOnUiThread instead of creating a new handler ?
runOnUiThread(new Runnable() {
#Override
public void run() {
loadDataList();
}
});
If you need a delay, then you can use a handler inside the runOnUiThread
runOnUiThread(new Runnable() {
#Override
public void run() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadDataList();
}
}, DELAY);
}
});
If I want to update my UI from a background thread when a message is received from a web service call, would the below be considered a safe option? I'm worried about potential memory leaks having the handler in my Application class
public class MyApplication extends Application {
private static long uiThreadId;
private static Handler uiHandler;
#Override
public void onCreate() {
super.onCreate();
uiThreadId = Thread.currentThread().getId();
uiHandler = new Handler();
}
public static void customRunOnUiThread(Runnable action) {
if (Thread.currentThread().getId() != uiThreadId) {
uiHandler.post(action);
} else {
action.run();
}
} }
And then in the class that deals with the messages received in the separate threads:
new Thread(
new Runnable() {
#Override
public void run() {
// make web service call
MyApplication.customRunOnUiThread(new Runnable() {
#Override
public void run() {
httpResponseHandler.onSuccess();
}
});
}
}
}).start();
My alternative is to use parallel AsyncTasks doing the work in the doInBackground method and updating the UI in the onPostExecute method. Both of these options work, but I'm not sure which one is 'most' correct so to speak.
Better way is to register BroadcastReceiver and using LocalBroadcastManager send the broadcast on receiving any message
I have the following:-
public class resApp extends MapActivity implements Runnable {
public void run() {
searchImage.setVisibility(View.GONE);
}
}
I also have a background thread that runs before this but that seems to run ok.
When i run the app the run() never gets called.
Can you help?
This code did work about 6 months ago but the device was 2.1.
Thanks
Chris
edit
I had already implemented
private Handler handler;
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.toString().equalsIgnoreCase("1")) {
ad.dismiss();
} else {
pd.dismiss();
}
}
};
as an example and I already have an asynchronous task that runs in the back ground and in 2.1 I could have getters and setters in there. I have now had to pull these out and put them into the run() method as 2.2 doesn't like setting onclicklistener in an async task.
All I need to do is call the run() method on post execute but have tried everything:-
protected void onPostExecute(Object result) {
// Pass the result data back to the main activity
if (dialog != null) {
resApp.this.dialog.dismiss();
}
}
Could I just do:-
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
this.resApp.run();
}
};
You can call the run() method by using Handler.
Handler myHandler = new Handler();
resApp myObj;
And call it by using myHandler.post(myObj);