How to processing UI while looping? - android

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.

Related

which operation is exactly blocking the main thread in android

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();
}

Writing and reading between threads with Android Realm

I'm performing some investigation of Realm threading and encountered issue.
In this simple example I have 2 Thread objects, one for writing and second one for reading. The reader Thread gets count of written objects always as 0, but inside writer scope the size() for items in DB is correct. When I relaunch app, the reader gets the first count ok before any insertions.
Thread writer = new Thread() {
#Override
public void run() {
while (mRunning) {
try {
Realm r = Realm.getInstance(context, "test_db");
r.beginTransaction();
TestData data = r.createObject(TestData.class);
r.commitTransaction();
Logger.e("WRITER THREAD COUNT: " +
r.where(TestData.class).findAll().size());
sleep(LATENCY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
writer.setPriority(Thread.MAX_PRIORITY);
writer.start();
Thread reader = new Thread() {
#Override
public void run() {
while (mRunning) {
try {
Logger.e("READING THREAD COUNT: " + Realm.getInstance(context,
"test_db").where(TestData.class).findAll().size());
sleep(LATENCY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
reader.setPriority(Thread.MAX_PRIORITY);
reader.start();
Is there something needed to do for this to work?
Thanks.
Emanuele from Realm here.
What you are describing is expected behavior :) Since the reader thread doesn't have a Looper, it has no way to receive notifications from the reader thread and will never update unless you manually execute a refresh.
In out repo we have several examples (not to mention unit tests) using threads with and without Looper, illustrating the current best practices.

Running code in main thread in background thread while blocking background thread

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.

Android stop download

In my application I download and parse a html page. However, I want to be able to stop the download in its tracks (i.e. when the user hits cancel).
This is the code I use now, which is being called from doInBackground from ASyncTask.
How do I cancel this request from outside of the ASyncTask?
I currently use htmlcleaner
HtmlCleaner cleaner = new HtmlCleaner();
CleanerProperties props = cleaner.getProperties();
props.setAllowHtmlInsideAttributes(true);
props.setAllowMultiWordAttributes(true);
props.setRecognizeUnicodeChars(true);
props.setOmitComments(true);
try {
URL url = new URL(urlstring);
URLConnection conn = url.openConnection();
TagNode node = cleaner.clean(new InputStreamReader(conn.getInputStream()));
return node;
} catch (Exception e) {
failed = true;
return;
}
Can't you use AsyncTask.cancel()? You should be able to then use the onCancelled callback to return to the main activity..
Ok, I believe I've solved this.
In my Activity class I have a variable (boolean) failed. Also, I have a private Downloader class within the activity which extends ASyncTask. This way, the Downloader class has access to the failed boolean. When the Activity launches, it starts the Downloader task and a progress dialog pops up. When the task finishes, it closes the dialog and then goes on processing the downloaded content.
However, when the user cancels the progress dialog, failed is set to true, and the user is sent back to the previous activity by a call to finished. In the meantime, Downloader is still busy downloading. Because the results are now unneccessary, we want it to stop using resources asap. In order to accomplish this, I have broken up the doInBackground method in as much steps as possible. After each step I check if failed is still false, when it is set to true, it simply doesn't go to the next step. See it in action below. Furthemore, the BufferedReader reader is public, and in the onCancelled method I execute reader.close(). This will throw all sorts of exceptions, but these are properly caught.
public void DoInBackground(.........) {
try {
URL url = new URL(uri);
URLConnection conn = url.openConnection();
if (!failed) {
isr = new InputStreamReader(conn.getInputStream());
if (!failed) {
reader = new BufferedReader(isr);
publishProgress(1);
if (!failed) {
TagNode node = cleaner.clean(reader);
publishProgress(2);
return node;
}
}
}
} catch (Exception e) {
failed = true;
Log.v("error",""+e);
}
}
#Override
protected void onCancelled() {
failed = true;
if (reader != null)
try {
reader.close();
} catch (IOException e) {
failed = true;
}
if (isr != null)
try {
isr.close();
} catch (IOException e) {
}
}
I know that I could have broken up the downloading process in even tinier bits, but I am downloading very small files, so it's not that important.

Android image fetching

What is the simplest way to fetch an image from a url in an android program?
I would strongly recommend using an AsyncTask instead. I originally used URL.openStream, but it has issues.
class DownloadThread extends AsyncTask<URL,Integer,List<Bitmap>>{
protected List<Bitmap> doInBackground(URL... urls){
InputStream rawIn=null;
BufferedInputStream bufIn=null;
HttpURLConnection conn=null;
try{
List<Bitmap> out=new ArrayList<Bitmap>();
for(int i=0;i<urls.length;i++){
URL url=urls[i];
url = new URL("http://mysite/myimage.png");
conn=(HttpURLConnection) url.openConnection()
if(!String.valueOf(conn.getResponseCode()).startsWith('2'))
throw new IOException("Incorrect response code "+conn.getResponseCode()+" Message: " +getResponseMessage());
rawIn=conn.getInputStream();
bufIn=new BufferedInputStream();
Bitmap b=BitmapFactory.decodeStream(in);
out.add(b);
publishProgress(i);//Remove this line if you don't want to use AsyncTask
}
return out;
}catch(IOException e){
Log.w("networking","Downloading image failed");//Log is an Android specific class
return null;
}
finally{
try {
if(rawIn!=null)rawIn.close();
if(bufIn!=null)bufIn.close();
if(conn!=null)conn.disconnect();
}catch (IOException e) {
Log.w("networking","Closing stream failed");
}
}
}
}
Closing the stream/connection and exception handling is difficult in this case. According to Sun Documentation you should only need to close the outermost stream, however it appears to be more complicated. However, I am closing the inner most stream first to ensure it is closed if we can't close the BufferedInputStream.
We close in a finally so that an exception doesn't prevent them being closed. We account for the possibility of the streams will being null if an exception prevented them from being initialised. If we have an exception during closing, we simply log and ignore this. Even this might not work properly if a runtime error occurs .
You can use the AsyncTask class as follows. Start an animation in onPreExecute. Update the progress in onProgressUpdate. onPostExecute should handle the actual images. Use onCancel to allow the user to cancel the operation. Start it with AsyncTask.execute.
It is worth noting that the source code and the license allow us to use the class in non-Android projects.
You do it many ways but the simplist way I can think of would be something like this:
Bitmap IMG;
Thread t = new Thread(){
public void run(){
try {
/* Open a new URL and get the InputStream to load data from it. */
URL aURL = new URL("YOUR URL");
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
/* Buffered is always good for a performance plus. */
BufferedInputStream bis = new BufferedInputStream(is);
/* Decode url-data to a bitmap. */
IMG = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
// ...send message to handler to populate view.
mHandler.sendEmptyMessage(0);
} catch (Exception e) {
Log.e(DEB, "Remtoe Image Exception", e);
mHandler.sendEmptyMessage(1);
} finally {
}
}
};
t.start();
And then add a handler to your code:
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case 0:
(YOUR IMAGE VIEW).setImageBitmap(IMG);
break;
case 1:
onFail();
break;
}
}
};
By starting a thread and adding a handler you are able to load the images without locking up the UI during download.

Categories

Resources