I am facing some problems trying to call main thread in my background thread.
Based on this post: Running code in main thread from another thread
The solution should be:
private void runOnMainThread() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Do something
ottoBus.post(new MyObjectEvent(mMyObject));
// End do something
mMyObject = null;
}
});
}
However my background thread is still being able to read Bluetooth socket data between "Do something" and "End do something"
What am I missing here? Is it possible to lock the background thread, while "Do something" is being executed?
My code for reading socket data is following:
InputStream stream = null;
InputStreamReader reader = null;
BufferedReader bufferedReader = null;
String data = "";
try {
stream = mSocket.getInputStream();
byte[] bytes = new byte[20];
int numRead = 0;
while ((numRead = stream.read(bytes)) >= 0) {
String s = new String(bytes, 0, numRead);
if (mMyObject != null) {
fillData(s); // Can cause NPE
} else {
mMyObject = new MyObject();
fillData(s);
}
// This should be synchronised call
runOnMainThread();
Thanks.
You will need to use a Java pattern called wait/notify. Simply put: it defines two threads,
a producer and a consumer, so that the consumer, after initiating the producer, stops and waits until the producer thread has completed.
It goes like this:
static final object uiActionMonitor = new Object();
transient boolean uiCompleted;
void network_thread_run() {
int numRead = 0;
while ((numRead = stream.read(bytes)) >= 0) {
String s = new String(bytes, 0, numRead);
// This should be synchronised call
uiCompleted = false;
runOnMainThread();
synchronized(uiActionMonitor) { //<---------- wait for UI to complete
while (!uiCompleted) {
uiActionMonitor.wait();
}
}
}
And the UI code:
private void runOnMainThread() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Do something
// End do something
uiCompleted = true;
synchronized(uiActionMonitor) { //<---------- release networking thread
uiActionMonitor.notifyAll();
}
}
});
}
Copy the synchronization logic exactly as is. This is where many developers get it wrong.
I must admit I fail to see why you need to block your networking thread while the UI thread is handling your message...
I find CountDownLatch to be the simplest way to accomplish this sort of thing. Here's a reusable method for running Runnables on the main thread and blocking on their completion:
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static void runOnMainThreadBlocking(Runnable runnable) throws InterruptedException {
CountDownLatch completionSignal = new CountDownLatch(1);
mainHandler.post(new Runnable() {
#Override public void run() {
runnable.run();
completionSignal.countDown();
}
});
completionSignal.await();
}
I think you need to use locks or synchronized blocs. You can take a look into the java concurency documentation and more specificaly this and this part.
This way you can guaranty that on portion of the code won't be executed muliple times in parallel.
Related
I have these next two methods, takescreenshots and saveScreenshot:
public static Bitmap takeScreenshot(View oView)
{
Bitmap oBitmap = null;
if(oView!=null) {
if (oView.getWidth()>0 & oView.getHeight()>0) {
oBitmap = Bitmap.createBitmap(oView.getWidth(),
oView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(oBitmap);
try{
oView.draw(canvas);
}catch (Exception ex){
//log or whatever
}
}
}
return oBitmap;
}
public static File saveScreenshot(Bitmap oBitmap)
{
String mPath = ApplicationContext.getActivity().getCacheDir().getAbsolutePath() + "/artandwordsshared_img.jpg";
File imageFile = new File(mPath);
try{
boolean operationOK = true;
if (imageFile.exists()){
operationOK = imageFile.delete();
}
if (operationOK) {
operationOK = imageFile.createNewFile();
}
if (operationOK) {
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 75;
oBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
}
}catch(java.io.IOException ex){
ex.printStackTrace();
}
return imageFile;
}
I use this "takeScreenshot" method to capture a view (I use it in some parts of my app) and the other one to save screenshot on device, and I'd like to know if I must/should (regarding good practices) call them through an AsyncTask so they run on background, or if it is not necessary at all. I'm currently using AsyncTask for both and all it's working just fine, but not sure if it is really a must, like it is, for example, for network operations.
Thank you in advance.
Edit 1: Adding the AsyncTask "TakeScreenshot" class.
class TakeScreenshot extends AsyncTask<String, Void, ImageProcessingResult>
{
private WeakReference<View> view2Capture;
private ImageListeners listener;
private String asyncTaskCaller;
TakeScreenshot(View view2Capture, ImageListeners listener, String asyncTaskCaller)
{
this.listener = listener;
this.view2Capture = new WeakReference<>(view2Capture);
this.asyncTaskCaller = asyncTaskCaller;
}
protected ImageProcessingResult doInBackground(String... urls)
{
Bitmap bitmap = null;
String result = Enum.Result.KO;
if(view2Capture.get()!=null)
{
bitmap = ImageHelper.takeScreenshot(view2Capture.get());
result = ImageHelper.saveBitmap(bitmap);
}
return new ImageProcessingResult(bitmap, result);
}
protected void onPostExecute(ImageProcessingResult ipr)
{
listener.onScreenCaptureFinished(ipr, asyncTaskCaller);
}
}
BTW, as for now takeScreenshot method called from AsyncTask is working just fine. Just trying to use good practices, and that's why my post.
I'm not sure you will be able to call first function takeScreenshot in the background Thread. Because you are performing operation with UI Draw. In any case, it makes no sense to put this small implementation to the background.
Next function saveScreenshot must be defined in the background Thread for sure. You need to eliminate the jank on UI which you would have because of using in in the foreground. Maybe you will not feel difference on new devices, but in some condition/platforms you will.
UPDATE
Seems like you new to the Android. Of course you can use AsyncTask, but people prefer other tools. AsyncTask is very old and nowadays there are bunch of alternatives. Just try to search for it.
On another hand. AsyncTask based on Java Executors(which includes ThreadPool, "Handler", MessageQueue, etc). For simple actions like yours, you can use just Thread. Clean and simple.
//Just make it once
private final Handler ui = new Handler(
Looper.getMainLooper());
//Whenever you need just call next call
new Thread("Tag").start(() -> {
//Background Work
//Whenever you need to submit something to UI
ui.post(() -> {
// Foreground Work
});
}
})
when you make a network connection in Android you are blocking the main thread , so you have to move "some" of this task to a new thread
I have 2 questions on this part
1- which of the following operation is blocking the main thread (A or B)
//A:
HttpURLConnection c = (HttpURLConnection) (new URL(url)).openConnection();
//B:
InputStream stream=c.getInputStream();
2- if "both" of the above (A & B) must run in a new thread , dose it have a bad effect to run each one in a new separate thread? take a look to the following code:
//I temporary removed try & catch to simplify the code
public class connect{
HttpURLConnection c; String url;
public connect(String url){
this.url=url;
new Thread(new Runnable{
#override public void run(){
c = (HttpURLConnection) (new URL(url)).openConnection();
}
});
}
public InputStream get(){
return c.getInputStream();
//or make this one in a new thread
}
public InputStream post(Sring params){
c.setRequestMethod("POST");
//.. make some code for posting data , and then call get()
//thats why i cannot perform c.getInputStram() at the same time with openConnection()
return get()
}
}
which of the following operation is blocking the main thread (A or B)?
It is pretty evident that both operations A and B will block the main thread. Just calling the following on the main thread will throw an exception(NetworkOnMainThreadException) right away:
HttpURLConnection c = (HttpURLConnection) (new URL(url)).openConnection();
Also when you are calling the following line on main thread:
InputStream stream=c.getInputStream();
You are simply trying to read a stream of bytes over a network. Now there are various factors that will determine the time taken by this operation to complete. For instance, network speed, overall number of bytes that you want to read, etc. The application should not really need to wait and stay idle till the reading process has been completed. All the UI related process should be able to run and consume the resources as a user reacts with your application which will not be possible because of the ongoing byte reading process which is actually blocking the main thread.
if both A and B must run in a new thread , dose it have
a bad effect to run each one in a new separate thread?
Technically, yes it is bad to run both in separate threads. Besides why would you want to do so? Before initiating stream reading process you need to make sure that the connection has been opened. Calling A and B in separate threads will raise concurrency issues. You must call B after A so if you even resolve concurrency issues, it will be of no use to make two separate threads.
EDIT:
So as you said in comments that you want to avoid using AsyncTask. An alternative for that is Java Threads. Check out the following sample usage of threads:
static public class MyThread extends Thread {
#Override
public void run() {
try {
// add your url and open connecttion here
HttpURLConnection c = (HttpURLConnection) (new URL("your url here")).openConnection();
// read stream or whatever data you want
InputStream stream = c.getInputStream();
} catch (Exception e) {
e.printStackTrace();
} finally {
//close your connection & wipe input stream here.
}
}
}
Now here is how we can call this thread:
private Thread downloadThread = new MyThread();
downloadThread.start();
At any time, you can also check if your thread is running or not by using the following code:
if (downloadThread != null && downloadThread.isAlive()) {
// do something when thread is alive here
}
This solution uses handler to connect main thread with background thread(the thread that does the HTTP connection stuff)
public class MainActivity extends AppCompatActivity {
Thread mThread;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startThread();
}
public void startThread(){
String url = "www.google.com";
String result = "";
mThread = new Thread(new Runnable() {
public void run() {
InputStream is = null;
HttpURLConnection conn;
try {
ConnectivityManager connMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = null;
if (connMgr != null) {
networkInfo = connMgr.getActiveNetworkInfo();
}
if (networkInfo != null && networkInfo.isConnected() && !mThread.isInterrupted()) {
conn = (HttpURLConnection) url.openConnection();
is = conn.getInputStream();
//Here you get the result from inputStream
}
threadMsg(result);
}catch (IOException e){
e.printStackTrace();
}
finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void threadMsg(String msg) {
if (msg != null && !msg.equals("") && !mThread.isInterrupted()) {
Message msgObj = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("message", msg);
msgObj.setData(b);
handler.sendMessage(msgObj);
}
}
private Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// What you want to do in UI thread
}
};
});
mThread.start();
}
The following code is taken from here
Can someone please explain in deep how this works?
What is this handler and what does it do?
How is the while condition implemented?.
How does the loop proceed?
I have very crude understanding how the code works , it would very much benefit me if you could help me out. Thank you.
{
final Handler handler = new Handler();
final byte delimiter = 10; //This is the ASCII code for a newline character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable()
{
public void run()
{
while(!Thread.currentThread().isInterrupted() && !stopWorker)
{
try
{
int bytesAvailable = mmInputStream.available();
if(bytesAvailable > 0)
{
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for(int i=0;i<bytesAvailable;i++)
{
byte b = packetBytes[i];
if(b == delimiter)
{
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable()
{
public void run()
{
myLabel.setText(data);
}
});
}
else
{
readBuffer[readBufferPosition++] = b;
}
}
}
}
catch (IOException ex)
{
stopWorker = true;
}
}
}
});
workerThread.start();
}
void sendData() throws IOException
{
String msg = myTextbox.getText().toString();
msg += "\n";
mmOutputStream.write(msg.getBytes());
myLabel.setText("Data Sent");
}
Be sure to know what a thread is, or I can clarify it.
The code you provide, as you should know, is defining your workerThread. All that is written in the run method will be run in another thread, once you call start() on that thread. All the the variables defined before the run method are define in the main thread. So is the Handler.
A Handler aims at linking 2 threads. You define it in one thread (here the main thread before the run method), and you use it in another thread (in the run method) to execute some action in thread where it was defined. SO in your code, you call
handler.post(new Runnable()
{
public void run()
{
myLabel.setText(data);
}
});
from the workerThread, so
myLabel.setText(data);
will be executed from the main thread (the thread where was instantiated your handler).
Why is it doing so? Because .setText() can't be called from another thread than the main thread as it is drawing something.
The while loop checks that the thread hasn't been interrupted (by Android or something else) and that your boolean stopWorker hasn't been modified, so that there was no Exception thrown while reading from your inputStream
try{}catch(){} is just a way to manage exceptions.
.available() method gives you the number of bytes you can read from the inpuStream by calling the .read() method. If there are some available bytes (so if you phone received something from the connected device), then it reads it.
To read method works this way: you pas a byte array as argument, and it will get the available bytes from the input stream and put them into the byte array.
Then it processes the bytes received...
But for me it's not an efficient solution, because the thread will loop very fast, whereas .read() is a blocking method, so all the part checking that checking that there are some bytes available is useless, and even inefficient. .read() would make the thread sleep until new bytes are available, and thus releasing resources. Maybe there is another reason why the code is doing so but I don't see it.
Hope that's clear.
This question is related to Android multi-threading, OpenCV and JNI. The JNI call made inside onCameraFrame is kind of an expensive image-processing operation, hence the camera-preview frame rate gets very slow (a lot of frame lag). When, the native method - 'FindSquare' is called in background from a new thread, the performance improves a little but not very much.
Please suggest the most efficient way to do what I intend to do below, so as to improve the frame-rate.
boolean isCallFinished = false;
public Mat onCameraFrame(Mat inputFrame) {
Size originalSize = inputFrame.size();
Imgproc.cvtColor(inputFrame, mDstImg, Imgproc.COLOR_RGBA2BGR);
Imgproc.resize(inputFrame, mDstImg, new Size(mScreenWidth,
mScreenHeight)); // 2048, 1536
new Thread(new Runnable() {
public void run() {
Message msg = new Message();
if(!isCallFinished) {
msg.arg1 = FindSquares(mDstImg.getNativeObjAddr()); // JNI call
isCallFinished = true;
messageHandler.sendMessage(msg);
}
}
}).start();
if (mDraw == 1) {
Imgproc.resize(mDstImg, inputFrame, originalSize);
}
return inputFrame;
}
A rapid optimization on a first glance would be to avoid creating a new thread for each frame.
Creating a new thread is very expensive.
Try the following approach :
Create a worker thread (keep reference to it)
Create a synchronized queue to communicate with the worker thread
Add objects to the worker thread queue and process them as a bunch
I'll try to write down the concept bellow , and adapt it to your needs (please note that the code wasn't tested in a proper environment).
public class MyWorkerThread extends Thread
{
ArrayList<FrameEvent> frameList;
boolean runWorkerThread;
public WorkerThread
{
super("FrameEventsWorkerThread");
runWorkerThread=true;
frameList= new ArrayList<FrameEvent>();
}
public synchronized AddFrameEvent(FrameEvent aEvent)
{
frameList.add(aEvent);
}
public void kill ()
{
runWorkerThread=true;
}
public void run()
{
while (true && runWorkerThread)
{
synchronized(this)
{
if (frameList.size()>0)
{
for (int i=0;i<frameList.size();i++)
{
Message msg = new Message();
if (!isCallFinished) { // don't quite understand what you want with this
FrameEvent evt = frameList.get(i);
msg.arg1 = FindSquares(evt.dstImg.getNativeObjAddr());
isCallFinished=true;
messageHandler.sendMessage(msg);
}
}
frameList.clear();
}
}
try {
Thread.sleep(10);
} catch (Exception e)
{
}
}
}
}
Your new function should look something like this :
boolean isCallFinished = false;
MyWorkerThread theThread;
public Mat onCameraFrame(Mat inputFrame) {
Size originalSize = inputFrame.size();
Imgproc.cvtColor(inputFrame, mDstImg, Imgproc.COLOR_RGBA2BGR);
Imgproc.resize(inputFrame, mDstImg, new Size(mScreenWidth,
mScreenHeight)); // 2048, 1536
FrameEvent newEvt = new FrameEvent();
newEvt.dstImg = mDstImg;
theThread.AddFrameEvent(newEvt); // avoid creating new threads
if (mDraw == 1) {
Imgproc.resize(mDstImg, inputFrame, originalSize);
}
return inputFrame;
}
I want process the UI element while lopping.
This code is recoding streaming audio.
But code have some issues.
Application stop in while instruction. So, "Record Button" don't change.
Also, Application can user actions.(button click, etc)
I want that button is clickable and writing data to file same time.
Following the code:
PlayerActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
btnStartStopRecord.setText(R.string.button_record_stop);
try{
conn = new("Streaming URL").openConnection();
is = conn.getInputStream();
outstream = new FileOutputStream(new File(dataDir, fileName).getAbsolutePath());
int c;
while((c = is.read()) != -1) {
outstream.write(c);
}
} catch(Exception e){
e.printStackTrace();
error(e.toString());
}
}
});
The application freezes because you're executing expensive operations on the UI thread.
You need to use background thread.
See:
http://developer.android.com/guide/components/processes-and-threads.html
http://androidresearch.wordpress.com/2012/03/17/understanding-asynctask-once-and-forever/
for reference.