I am building an app that requests the temperature of a server. When a button is pressed, I would like the app to:
1) Show a 'contacting server' message and show a spinning progress bar.
2) Contact the server on a new thread.
3) Display the result and hide the progress bar.
Here is my MainActivity:
public class MainActivity extends AppCompatActivity {
private Button mFetchTempButton;
private TextView mResultTextView;
private ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mResultTextView = (TextView) findViewById(R.id.result_textview);
mFetchTempButton = (Button) findViewById(R.id.fetch_temperature_button);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mFetchTempButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mResultTextView.setText("Contacting server... ");
mProgressBar.setVisibility(View.VISIBLE);
String[] args = {};
String temperature = RequestServerTemp.main(args);
mProgressBar.setVisibility(View.INVISIBLE);
mResultTextView.setText("Server temperature is " + temperature);
}
});
}
}
This calls the java class 'RequestServerTemp', which uses a Callable to make the server request on a new thread:
public class RequestServerTemp {
public static String main(String[] args) {
final ExecutorService service;
final Future<String> task;
String result = "";
service = Executors.newFixedThreadPool(1);
task = service.submit(new GetTemp());
try {
result = task.get();
}
catch(InterruptedException | ExecutionException ex) {
ex.getMessage();
}
service.shutdownNow();
return result;
}
}
class GetTemp implements Callable<String> {
public String call() {
// simulate a long networking operation
try {
Thread.sleep(3*1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
return "30 degrees C";
}
}
The error this causes is that the App only updates once the whole onClick has been completed. This prevents my required step 1) from occurring. I am new to Android, and this raises several questions for me:
1) Why does onClick execute at the end, unlike traditional scripting languages which execute line by line?
2) If I've launched RequestServerTemp in a new Thread, why does MainActivity wait for it to finish? I feel this is bad for an app, and delays like this are the whole reason we launch networking in new threads.
3) Several similar questions to this say that AsyncTask is the 'correct' way to handle networking, rather than Runnable or Thread. Is that true, and should I avoid using Runnable and Thread in an App?
I am mostly interested in question 3, as many stackoverflow answers point to using Runnable and Thread techniques to accomplish networking, and now I am here I am worried I've wasted a lot of time and effort. Thanks for reading, and any general hints and advice for a new app developer (or stackoverflow user!) are welcome.
result = task.get();
get() is a blocking method. It waits until T is available to be returned. That's why "MainActivity wait for it to finish".
Is that true, and should I avoid using Runnable and Thread in an App?
No it is not. When I am not allowed to use third party library to send requests to a webservice, I use an ExecutorService.
Why does onClick execute at the end, unlike traditional scripting
languages which execute line by line?
it does not execute at the end. You are providing a delegate to your mFetchTempButton, the code in the callback, onClick, is executed when the event on click takes place.
Related
I'm trying to connect to a PC server (using Hercules) through TCP from an android app (client) but I'm really lost and don't know where to go. None of the tutorials are fully working for me, most of them can allow me to send messages from client to server but not vice versa.
I read about connections not supposed to be run from the "main thread" but what does that mean?
Also any examples of a TCP connection from android would be great.
Thanks!
I suspect that the "main thread" in this context means that thread that is managing the user interface. If you do anything much in this thread, you run the risk of Android killing your app because it appears to have hung.
Personally, I don't think it's a huge problem to have the user interface thread block for a few milliseconds to do a TCP operation. You'd need to make sure that you code the operation to have sensible timeouts, so you don't end with a dead app because the remote server takes too long to respond.
The official way to handle situations like this is to define the network operations in services, or in separate threads, and have the user interface thread communicate with those services or threads using short-lived operations.
This process is documented with examples here:
https://developer.android.com/training/basics/network-ops/connecting
Main thread in android is responsible to create and display UI on screen
to perform task related to connection strictly need to use background thread otherwise UI become laggy.
There are two method available to perform background Task
Runnable:
new Thread(new Runnable() {
public void run() {
..... // code here
}
}).start();
Android AsyncTask: https://developer.android.com/reference/android/os/AsyncTask
Like #Kevin Boone said, Main thread means UI thread in Android.
You can't do networking operations in the Main thread, otherwise you will get NetworkOnMainThreadException. You can create a new thread and then pass result back to the Main thread using Handler. Code could look like this:
public class MyActivity extends AppCompatActivity implements FetchDataUseCase.Listener {
private FetchDataUseCase fetchDataUseCase;
private TextView textView;
private Button dataButton;
public void onCreate() {
textView = findViewById(R.id.textView);
dataButton = findViewById(R.id.dataButton);
dataButton.setOnClickListener(v -> getDataFromNetwork());
fetchDataUseCase = new FetchDataUseCase(this);
}
void getDataFromNetwork() {
fetchDataUseCase.fetchDataAndNotify();
// start async operation! and receive result in onDataFetched()
}
#Override
public void onDataFetched(String data) {
// now you are in Main thread
// do something with data
textView.setText(data);
textView.setVisibility(View.VISIBLE);
}
#Override
public void onError() {
textView.setText("ERROR!!!");
textView.setVisibility(View.VISIBLE);
}
}
public class FetchDataUseCase {
public interface Listener {
void onDataFetched(String data);
void onError();
}
private final Listener listener;
private final Handler handler = new Handler(Looper.getMainLooper());
public FetchDataUseCase(Listener listener) {
this.listener = listener;
}
public void fetchDataAndNotify() {
new Thread(() -> {
String myData = "";
try {
// your networking operation
// where you receive some data
} catch (Exception e) {
handler.post(() -> listener.onError();
} finally {
// close stream, file, ...
}
// pass it back to Listener in Ui Thread
handler.post(() -> listener.onDataFetched(myData);
}).start();
}
}
read ThreadPoster: Multi-Threading in Android
And don't use AsyncTask =)) Async task is deprecated
Also, you need to add permision to your AndroidManifest file.
<uses-permission android:name="android.permission.INTERNET"/>
Hope it will help you)) Good luck!
When I show an alertdialog, I start a thread that starts a 30 second countdown to update a determinate progressbar shown in this alertdialog.
I create the runnable as a static inner class so that I don't leak the context(activity), but then of course I can't access the flag to stop the thread, nor the views I want to update. How can I get around this?
public class MyDialogFragment implements DialogInterface.onShowListener, DialogInterface.onDismissListener {
private boolean stopThread = false;
private Progressbar countdownBar;
private TextView countdownRatio;
#Override public void onShow() {
Thread progressThread = new Thread(new myRunnable());
progressThread.start();
}
#Override public void onDismiss() {
stopThread = true;
this.dismiss();
}
private static class myRunnable implements Runnable {
int progressStatus = 0;
int numSeconds = 30;
#Override public void run() {
while (!threadStop && progressStatus < numSeconds) {
progressStatus++;
handler.post(new Runnable() {
public void run() {
countdownBar.setProgress(progressStatus);
countdownRatio.setText(progressStatus + "/" + numSeconds + " secs");
}
});
try {
// update the counter every sec
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
First - don't use Thread - you're asking for troubles, especially that you don't seem to be comfortable with multi-threaded programming. It's a tricky topic with tons of pitfalls. It's definitely not for noobs.
You may use AsyncTask for this - it has nice integration with UI event loop via AsyncTask.onProgressUpdate(). AsyncTask uses internal thread pool.
http://developer.android.com/guide/components/processes-and-threads.html#Threads
AsyncTask is ok for most trivial stuff. For more advanced uses try using Service with worker threads and message bus to communicate with fragments or activities. There is plenty of libraries for asynchronous programming. I can recommend this one:
https://github.com/stephanenicolas/robospice
It's main purpose if networking, but you can use it for other stuff as well.
Third solution is Loader API:
http://developer.android.com/reference/android/content/Loader.html
It's intended for asynchronous loading of data from database (SQLite is slow), but it's quite easy to use it for other stuff, such as data processing.
Remember: if you use Thread, there are 2 possibilities:
You are expert and you know what you're doing
You are green and you're doing it wrong
after some time of programming I decided to download the Android SDK and see if I should start focusing on mobile development to start making money.
At the moment I am just trying to get used to how the android works and up until now I have a basic understanding of the events and the way the xml files interact with the .java files.
To learn I'm just trying to do tests of basic stuff and now I have something like this :
TextView text = (TextView)findViewById(R.id.lol);
number mnumber = new number();
mnumber.change_in_number();
text.setText(mnumber.get_in_number() + "");
Let me explain;
number is a class I make which has an integer varibale inside, a function to get the value (get_in_number) and a function to change the integer variable to a random value (change_in_number).
All of this functions work es intended for they are very simple but when I run the code this only happens once (as expected).
My question now is...
Exactly how would I make this code repeat itself every X amount of seconds?
You know, to have the value changed multiple times without the need of any event while the application is running.
I know this question is really simple and probably also easy to answer but right now I really need help with getting started.
Thanks in advance for your help.
This might be a lot over your head but you need to create a separate thread with a while loop to periodically update your TextView. I didn't compile and run this but it should be pretty close to exactly what you want:
public class YourActivity extends Activity
{
private UpdaterThread updaterThread;
private Runnable changeNumberRunnable;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
changeNumberRunnable=new Runnable()
{
#Override
public void run()
{
YourActivity.this.updateNumber();
}
};
}
#Override
protected void onResume()
{
super.onResume();
updaterThread=new UpdaterThread();
updaterThread.start();
}
#Override
protected void onPause()
{
super.onPause();
updaterThread.kill();
}
private void updateNumber()
{
TextView text = (TextView)findViewById(R.id.lol);
number mnumber = new number();
mnumber.change_in_number();
text.setText(mnumber.get_in_number() + "");
}
private class UpdaterThread extends Thread
{
private boolean running;
public void kill()
{
running=false;
}
#Override
public void run()
{
running=true;
while(running)
{
//you can't change a view from a separate thread, so call the update on the main UI thread
YourActivity.this.runOnUiThread(changeNumberRunnable);
//sleep for 5 seconds, if we're interrupted then exit the loop
try { Thread.sleep(5000); }
catch(InterruptedException e) { running=false; }
}
}
}
}
Let's say you're designing the threading architecture for a an app -> the primary purpose is that your app will have a lot of tasks that need something done on the background thread, and sometimes a result task on UI thread, or something not (though more times, the result needs to be run on UI thread). For simplicity, let's say the tasks will be stuff like: download a file and display a pop-up, log a user in and go to a different page, process an image and store the result in a database (popular tasks that a lot of apps do)
I've researched a lot about the nuances, but would really like a deep-dive explanation/knowledge on what kind of architecture is better, and what are the considerations.
here are the three models in consideration:
AsyncTask model: each operation (like downloading a file and displaying a pop-up) is an AsyncTask, or some derivative of a parent class that abstracts out the common functionalities.
Thread/handler model: i always create a new Handler(Looper.getMainLooper()); and each time i need to do a task, i use a thread factory to spin off the task, with the handler on UI thread (or whatever custom handler).
Service/Thread model: i use a general Service class that is in charge of operations based on some operation code. there's a bunch of ServiceTask derivative objects that do certain things, but the Service class communicates with each ServiceTask when tasks are started/done.
I'm slightly leaning towards going the whole service/threading model, just because i've read some really awkward nuances with AsyncTask/Threads:
AsyncTask has a private static handler, and if the classloader calls it at the wrong time (such as including a library that uses it before your application does) then all of your onPostExecute will happen at the wrong time since your handler was not the main handler
it's easy to forget to check a bunch of things in the onPostExecute such as if there was a config change, or your activity was destroyed, or application was backgrounded/paused when the onPostExecute is called (leading to crashes)
AsyncTask changed its serial/parallel execution behavior on different APIs
If you went with the Thread/Handler model, on older devices, thread priority is actually incredibly low. i've heard something like there was a priority scale of 1-15 such that your threads automatically get a low priority and if the system was low on resources, your threads would stop running (whereas since services are running independently of your activity the thread priority there is higher?)
What is the best way to design a robust threading architecture that doesn't easily lead to crashes/unexpected behavior while also maintaining good performance ??
Please also let me know in the comments if this question is too vague and if you need actual code (i'm afraid to post code because it would super overbloat the question length more than it already is).
I don't think you will find a one-size fits all approach here.
Downloading a file? Use DownloadManager
Logging a user in and go to next screen? Probably an AsyncTask would be best.
Process an image and store it? A Service might be a good choice here since you don't want the action to be attached to any particular Activity.
Handlers are more tricky, if they are attached to a Looper running on a background thread you need to call quit() on the Looper when you are done with it. Handlers are good when you need to delay an action, postDelayed() is great for that. They are also good when you need to communicate back to the UI thread from a background thread.
But yes you are correct that each one has pitfalls as you mentioned. Android is a complex beast and it seems they could have a done a better job preventing developers from shooting themselves in the foot, especially in regards to AsyncTask being called after an Activity is destroyed!
I was using Java's old school approach by creating a class (I called it ThreadRunner) derived from Java's Thread. A constructor looked like:
public ThreadRunner (Object [] params, AbstractCallback callBack) {...}
AbstractCallback was a class that was implemnting a single 'onCall' method and was mostly used to notify a calling party about an event such as "execution of a task is completed".
I've used it to get content from Internet and run other time consuming operations. It didn't cause any problems and worked as expected.
However, I've heard many times that AsyncTask is an Android-ish way of doing things like that. I don't know why and do not have any intention to change, since I'm preaching "don't fix it if it's not broken" approach.
I've seen also comments that you'll need to write less code with AsyncTask, but in my approach with traditional Java's Threat the amount of coding was small as well, so I queses it's just a matter of your personal preferences and experience.
In regard of your 3-rd approach - I think you should use it when write a service that runs all the time, listens on requests and never stops. When you just need to execute a single task asynchronously Java Threads or AsyncTask should be used.
I think AsyncTask is a good tool for listed purposes. But it needs to wrap AsyncTask for an easy using. My variant of such wrapping (with a progress indicator) is a following:
Main class AsyncActivity for extending it in application activities:
public abstract class AsyncActivity extends Activity{
// Поле нужно обязательно объявить как статическое!
private static AsyncConnect asyncConnect = null;
protected void runBackgroundTask(String progressInscription, RequestTask task){
asyncConnect = new AsyncConnect(this, responseListener, progressInscription, task);
asyncConnect.execute();
}
protected abstract void onBackgroundTaskEnd(boolean result);
#Override
protected void onResume(){
super.onResume();
// Перерегистрируем текущий контекст этой формы
// для корректной работы слушателя ответа с сервера
responseListener.registerCurrentContext( this );
if (asyncConnect != null){
asyncConnect.onResume(this);
}
}
#Override
protected void onPause(){
super.onPause();
if (asyncConnect != null){
asyncConnect.onPause();
}
}
/**
* Чтобы диалоги не вызывались из устаревшего контекста
* и по этой причине не исчезали при повороте экрана,
* слушателя ответа с сервера необходимо сделать статическим полем класса,
* в котором должен быть зарегистрирован текущий контекст
*/
private static final OnServerResponseListener responseListener = new OnServerResponseListener(){
private AsyncActivity context = null;
#Override
public void registerCurrentContext(AsyncActivity context){this.context = context; }
#Override
public void onResponse(boolean result){
// Если никакой контекст не был зарегистрирован, ничего не делаем
if (context == null) return;
// Освождаем статическое поле для сборщика мусора (но делать это не обязательно!)
asyncConnect = null;
// Вызываем колбэк о завершении фоновой задачи
context.onBackgroundTaskEnd(result);
}
};
}
Additional class and a pair of interfaces:
public class AsyncConnect {
private final Activity context;
private final RequestTask task;
private final String progressInscription;
private final OnServerResponseListener responseListener;
private boolean isDone = false;
private ProgressDialog progressDialog;
public AsyncConnect(Activity context, OnServerResponseListener responseListener,
String progressInscription, RequestTask task){
this.context = context;
this.task = task;
this.progressInscription = progressInscription;
this.responseListener = responseListener;
progressDialog = null;
isDone = false;
}
public void execute(){
if (isDone) return;
new ConnectTask().execute();
}
public void onPause(){
if (isDone) return;
if (progressDialog != null){
if (progressDialog.isShowing()){
progressDialog.dismiss();
progressDialog = null;
}
}
}
public void onResume(Activity context){
if (isDone) return;
progressDialog = ProgressDialog.show( context, null, (CharSequence)progressInscription,
true, false);
}
private class ConnectTask extends AsyncTask<Object, Void, Boolean> {
#Override
protected void onPreExecute( ) {
super.onPreExecute();
progressDialog = ProgressDialog.show( context, null,
(CharSequence)progressInscription, true, false);
}
#Override
protected Boolean doInBackground(Object... messages) {
return task.call();
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (progressDialog != null){
if (progressDialog.isShowing()){
progressDialog.dismiss();
progressDialog = null;
}
}
// Делаем невозможным повторное использование этого объекта
isDone = true;
responseListener.onResponse(result);
}
}
}
public interface OnServerResponseListener {
public void registerCurrentContext(AsyncActivity context);
public void onResponse(boolean result);
}
public interface RequestTask {
public boolean call();
}
For using AsyncActivity we only need to call runBackgroundTask and implement onBackgroundTaskEnd in the target activity. It's possible to create different kinds of AsyncTask wrappings based on this idea.
You may also check out Needle; it's an open-source, simple but powerful multithreading library for Android. With it you can say things like:
Needle.onMainThread().execute(new Runnable() {
#Override
public void run() {
// e.g. change one of the views
}
});
or
Needle.onBackgroundThread().execute(new UiRelatedTask<Integer>() {
#Override
protected Integer doWork() {
int result = 1+2;
return result;
}
#Override
protected void thenDoUiRelatedWork(Integer result) {
mSomeTextView.setText("result: " + result);
}
});
very simple API
fixed thread pool size
customizable thread pool size
supports UI interaction ("do work and then use result on UI thread")
android 1.5+
behaves the same on all platform versions
Check it out on GitHub: https://github.com/ZsoltSafrany/needle
I am running a simple project where on clicking a button in android. A message is displayed returned from a webservice whose coding is done in java.
Here is my program in eclipse for Android:
#Override
public void onClick(View v) {
final String url = "http://10.0.2.2:8050/WebPractice/PracticeWebServices?wsdl";
final String namespace = "http://PracticeWebServices/";
final String methodName = "hello";
final String action = namespace + methodName;
if(v.getId() == submit_btn.getId()) {
SoapObject so = new SoapObject(namespace, methodName);
so.addProperty("name", "Arjun");
SoapSerializationEnvelope sse = new SoapSerializationEnvelope(SoapEnvelope.VER11);
sse.setOutputSoapObject(so);
HttpTransportSE hse = new HttpTransportSE(url);
try {
hse.call(action, sse);
SoapPrimitive primitive = (SoapPrimitive)sse.getResponse();
Toast.makeText(getApplicationContext(),primitive.toString() , Toast.LENGTH_SHORT);
} catch(IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
}
}
Following is my code in Java:
#WebService(serviceName = "PracticeWebServices")
public class PracticeWebServices {
#WebMethod(operationName = "hello")
public String hello(#WebParam(name = "name") String name) {
return "Hello " + name + " !";
}
}
I've included the internet permission in manifest file....
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Now, when I'm running the file in the emulator provided in Eclipse. On clicking the button it shows me a message Unfortunately the program has stopped
When i checked the logCat....Seems like i m hitting an exception at android.os.NetworkOnMainThreadException
You should put your long networking fetching operation in either a Thread or AsyncTask
I suggest you to use AsyncTask
you can do something like this by creating a class that extends AsyncTask
private class LongNetworkOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
SoapObject so = new SoapObject(namespace, methodName);
so.addProperty("name", "Arjun");
SoapSerializationEnvelope sse = new SoapSerializationEnvelope(SoapEnvelope.VER11);
sse.setOutputSoapObject(so);
HttpTransportSE hse = new HttpTransportSE(url);
try {
hse.call(action, sse);
SoapPrimitive primitive = (SoapPrimitive)sse.getResponse();
Toast.makeText(getApplicationContext(),primitive.toString() , Toast.LENGTH_SHORT);
} catch(IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
}
}
and you can call it on your onClick() like this
#Override
public void onClick(View v) {
if(v.getId()==submit_btn.getId()) {
new LongNetworkOperation.execute();
}
}
Also I suggest you to show a ProgressDialog on onPreExecute() and hide it in onPostExecute() so that the user will be aware of the long background process which is highly recommended for long operations
You should not call long time keeper process in the main thread like internet resources, data on disk, ... You should instead use a background task. Otherwise you will have ANR error or compilation error like you mention it.
The problem here is simply that you need to make your web service calls (or what-have-you) on a separate thread. So, quite simply, you’ll need to look into how to do threading with Android. Unfortunately this can be a bit of a pain because you need to make your service calls on a separate thread, but you need to update the UI on the main thread. Normally this would require passing data between the threads, which involves handlers or other complexities. Luckily the Android platform provides the Async Task to handle this, which alleviates some of this complexity and may help you avoid some clutter in your code.
Useful documentation to migrate your network calls to threads (or Android’s Async Task)
Painless Threading (from the Android Developer docs)
Async Task (from the Android Developer docs)
Android Threads, Handlers and AsyncTask – Tutorial
Designing for Responsiveness (from the Android Developer docs)
You should not run your network requests on the main thread, this is causing a NetworkOnMainThreadException. See this solution with a custom AsyncTask or use one of these libraries:
Basic HTTP client w/ Android AsyncTask wrapper
Android Asynchronous Http Client
or Android Query (the Async Network part)
The problem that you are having is network operation like what you are trying to do cannot and should not be performed on the main UI thread. Doing so will lead to an ANR (Android Not Responding), which is exactly the kind of error you are getting just now. What you want to do is move your code to a separate thread or to an AsyncTask that will perform this action in the background on a different thread using the doInBackground() method which does not have access to your views on the main UI thread. For example,
private class ExampleOperation extends AsyncTask<String, Void, String> {
public ExampleOperation() { //ctor }
#Override
protected void onPreExecute() {
// things that you want to initialize and maybe show dialog to user.
}
#Override
protected String doInBackground(String... params) {
// this is where you perform that network related operation.
}
#Override
protected void onPostExecute(String result) {
// this is where get the results from the network related task.
}
#Override
protected void onProgressUpdate(Void... values) {
// you can update your progressbar if any; otherwise omit method.
}
}
Then all you have to do is call the AsyncTask where ever you want to use it: new ExampleOperation().execute();