I am having a slight problem in Android Async Task. In my MainActivity, I am calling GetEventAsyncTask which will execute the method inside called retrieveEventJSON:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = this;
public void onSingleTap(float x, float y) {
final Point point = mMapView.toMapPoint(x, y);
eventModel.setEventX(String.valueOf(point.getX()));
eventModel.setEventY(String.valueOf(point.getY()));
new MyAsyncTask(new MyAsyncTask.OnRoutineFinished() {
public void onFinish() {
CreateEvent.createEventDialog(context, point.getX(),
point.getY(), eventAddress); //this will be called after the task finishes
}
}).execute(eventModel);
}
});
new GetEventAsyncTask().execute();
}
In my GetEventAsyncTask, basically I am just retrieving the data returned from JSON and save them into an array:
public class GetEventAsyncTask extends AsyncTask<Event, Integer, Double> {
EventController eventCtrl = new EventController();
String eventAddress;
Event eventModel = new Event();
public interface OnRoutineFinished{ //interface
void onFinish();
}
private OnRoutineFinished mCallbacks;
public GetEventAsyncTask(OnRoutineFinished callback){ //constructor with interface
mCallbacks = callback;
}
public GetEventAsyncTask(){} //empty constructor to maintain compatibility
#Override
protected Double doInBackground(Event... params) {
try {
eventAddress = eventCtrl.getStreetAddressFromGeometry(eventModel.getEventX(), eventModel.getEventY());
eventCtrl.retrieveEventJSON();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Double result) {
if(mCallbacks !=null)
mCallbacks.onFinish(); //call interface on finish
}
protected void onProgressUpdate(Integer... progress) {
}
}
Then when the navigation drawer item onselected, I am calling the plotting marker on map method which takes in the array I saved just now:
case 0:
eventCtrl.plotEventOnMap(context);
break;
I tried to print out the data retrieved in retrieveJSON and it did printed out. But somehow, when I tried to plot onto the map, it does not shows anything. I wonder which part that I overlapped or reinitialize some Object?
The strange thing is if I put getEventAsyncTask under MainActivity, it did run and retrieved the data. But however, if I shifted the getEventAsyncTask out as an individual class, it stopped working. I wonder why is it so?
Thanks in advance.
Related
I'm currently working on my first Android application.
The application accesses a database to get some informations that I want to print on the screen. To send requests and get answers on the network, I need to use a new thread (I'll name it "N thread"), different from the UI Thread. This part is ok.
Now, I want to modify the variable eventList to get the values stored in a collection, in the N thread.
public class MainActivity extends AppCompatActivity {
public List<Event> eventList = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* I fill the list in an other thread */
new Thread(new Runnable() {
public void run(){
eventList = new WebService().getEvents(); //returns a list
}
// if I check here, eventList contains elements
}).start();
/* I check the result */
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
...
}
The problem is : eventList is not modified. How can modify this variable and print it from the UI thread ?
Thank you for your help.
You can use runOnUiThread function or Handler to update UI from other thread. I suggest you reading the below tutorial first: AndroidBackgroundProcessing
Try this
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params)
{
eventList = new WebService().getEvents();
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
}
});
}
}.execute();
private class EventsDownloader extends AsyncTask<Void, Void, Void> {
protected Long doInBackground(Void... params) {
eventList = new WebService().getEvents()
}
protected void onPostExecute(Void result) {
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
}
}
This AsyncTask does what you want, the doInBackground runs on a thread and the 'onPostExecute' runs on the UI thread, and it's only called after the doInBackground finishes. This class is "managed" by the OS. To run it you just need to instantiate it and call 'execute'. I recommend doing something like this
The thing with your code is that the thread runs at the same time as the rest of your code (the calls to the setText), this means when it runs the setText the Thread is still getting the events.
In my android application I am trying to create a situation similar to ios delegate function.
(in ios->)where a class that perform the checking is called and after finish checking it will be redirected back using delegate to viewcontroller and perform next function.
Here is my Class
public class Checking{
private boolean flag;
public boolean getFlag(){
return flag;
}
public void checkFunction(){
//..... check database
if(need to do call webservice){
Thread thread = new Thread(){
#Override
public void run(){
// Perform webservice calling
}
};
thread.start();
}
else{
//end
}
}
}
Here is my Activity
public class ActivityA extends Activity{
#Override
public void onResume(){
doChecking();
}
public void doChecking(){
Checking check = new Checking();
check.checkFunction();
// should finish preform checking in Checking class before proceed
if(check.getFlag()){
// perform next function
}
else{
// show alert
}
}
}
Problem with this is that right after calling the Checking class it straight away perform the if else below the function call. Which in some situation the check in Checking class have not finished and an empty alert is shown. The thread might or might not start depending on the database checking.
Can someone provide me a solution to overcome this?
I know something is missing after calling the Checking class but I am not quite sure what to put it there in order to achieve the result.
Thanks in advance.
What you basically want to do is Hit a web service and then wait till you get the response of web service.
First thing, you don't need to create your own Thread t hit web service. Instead you can use AsyncTask
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
Below is code of AsyncTask
class MyAsync extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
//Hit your web service here
}
#Override
protected void onPostExecute(final Void unused) {
//Process the result here
}
}
If you want to restrict user from accessing the app till web service is hit. You can show dialog from another method, like below:
class MyAsync extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
loadingDialog.setMessage("Please wait...");
loadingDialog.setCancelable(false);
loadingDialog.show();
}
#Override
protected Void doInBackground(Void... arg0) {
//Hit your web service here
}
#Override
protected void onPostExecute(final Void unused) {
if (loadingDialog.isShowing()) {
loadingDialog.dismiss();
}
//Process the result here
}
}
It can be implemented in the following way, by passing the activity in the Checking class's construtor and using it to call showAlert() function in the ActivityA class.
public class Checking{
Activity activity_;
public Checking(Activity activity){
this.activity_ = activity;
}
private boolean flag;
public boolean getFlag(){
return flag;
}
public void checkFunction(){
//..... check database
if(need to do call webservice){
Thread thread = new Thread(){
#Override
public void run(){
// Perform webservice calling
//after all the process
Handler handler = new Handler();
handler.post(new Runnable{
#Override
public void run(){
(MyActivity) activity_.showAlert();
}});
}
};
thread.start();
}
else{
//end
}
}
}
public class ActivityA extends Activity{
#Override
public void onResume(){
doChecking();
}
public void doChecking(){
Checking check = new Checking(ActivityA.this);
check.checkFunction();
}
public void showAlert(){
// should finish preform checking in Checking class before proceed
if(check.getFlag()){
// perform next function
}else{
// show alert
}
}
}
I currently have a fragment containing two spinners and I want to send the information from both spinners to MainActivity. Is this possible? While my code works when I send the information from just one spinner, as soon as I try and send the information from both spinners (per below), none of the information appears to be transmitted:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
onNumberInPartyListener = (onNumberInPartyListener) activity;
onMethodOfSplitListener = (onMethodOfSplitListener) activity;
}
catch (Exception ex){}
}
Do I need to create two onAttach methods, two fragments or is there another way?
Thanks
Update:
So I ended up doing away with the above and instead used an 'Interface' java class to send the information from Fragment 1 to Main Activity, however now I'm having issues sending the information from Main Activity to Fragment 2.
In my Main Activity, I'm sending the information to Fragment 2 with the following code (where 'evenSplit_CalculationFragment2' is Fragment 2 and 'tellMeWhatEachPersonOwesES is the method I've implemented in Fragment 2):
//Send data to Even Split Fragment 2
evenSplit_CalculationFragment2.tellMeWhatEachPersonOwesES(eachPersonOwesESString);
And in Fragment 2 I've implemented this as follows:
//What Each Person Owes (from Main Activity)
public void tellMeWhatEachPersonOwesES (String eachPersonOwesThisESString) {
amountEachPersonOwesES.setText(eachPersonOwesThisESString);
}
However, I'm coming up with a Null Pointer exception for both of these. I've tried testing this by substituting 'eachPersonOwesThisESString' with an actual string (e.g. "test") but most baffling of all I still get a Null Pointer exception. Any help appreciated.
You should use Handler
h = new Handler() {
public void handleMessage(android.os.Message msg) {
// Getting data from Handler
tvInfo.setText("Data from Spinner1: " + msg.what);
if (msg.what == 10)
// do what you need
};
};
You can use one handler with different msg.what codes to distinguish them. Initialise it in activity and send to fragment, it will fire up when you write h.sendMessage
If I wanted to solve this problem, what I'd do is use an event bus (although if you really want, you can technically use LocalBroadcastManager with Parcelables).
With Otto event bus, it'd look like this
public enum SingletonBus {
INSTANCE;
private Bus bus;
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
}
public class YourFragment extends android.support.v4.Fragment {
public static class SpinnersSelectedEvent {
public String firstSpinnerData;
public String secondSpinnerData;
public SpinnersSelectedEvent(String firstSpinnerData, String secondSpinnerData) {
this.firstSpinnerData = firstSpinnerData;
this.secondSpinnerData = secondSpinnerData;
}
}
#OnClick(R.id.yourfragment_thebutton)
public void theButtonClicked() {
SingletonBus.INSTANCE.postToSameThread(new SpinnersSelectedEvent(firstSpinner.getSelectedItem(), secondSpinner.getSelectedItem()); //pseudo code on the spinner part
}
}
public class MainActivity extends AppCompatActivity {
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
//...
SingletonBus.INSTANCE.register(this);
}
public void onDestroy() {
super.onDestroy();
SingletonBus.INSTANCE.unregister(this);
}
#Subscribe
public void onSpinnersSelectedEvent(YourFragment.SpinnersSelectedEvent e) {
String firstData = e.firstSpinnerData;
String secondData = e.secondSpinnerData;
// do things
}
}
I am very frustrated as I've been trying to implement a super simple loading wheel while waiting on a network call. I have searched and read dozens of SO questions and I just feel like I must be missing something, unless nobody really does what I'm trying to do. I have tried going down the AsyncTask route, but that's not what I want.
Let me also say that right now my app works perfectly, it's just that the transition from screen to screen appears to hang as it waits on the network. I just want a loading wheel so that in the 1-2 seconds the user knows the app is working and didn't freeze.
Here's what my current network call looks like:
private static String sendDataToServer(String arg1, String arg2)
{
Thread dbThread = new Thread()
{
public void run()
{
// do the call that takes a long time
}
};
dbThread.start();
try {
// I do this so that my program doesn't continue until
// the network call is done and I have received the information
// I need to render my next screen
dbThread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
Now, why can't I just add the ProgressDialog like this? If I do this, the progressDialog never appears.
private static String sendDataToServer(String arg1, String arg2)
{
final ProgressDialog progress = new ProgressDialog(BaseActivity.getInstance());
progress.setIndeterminate(true);
progress.setMessage("Loading...");
progress.show();
Thread dbThread = new Thread()
{
public void run()
{
// do the call that takes a long time
}
};
dbThread.start();
try {
dbThread.join();
progress.dismiss();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
I think I'm stuck because the network call needs to be on a separate thread from the UI thread, yet I don't want to continue in my application because I need the results of that call to continue. But if I do thread.join() I hold up everything. I thought I needed AsyncTask but that went downhill quickly. Here's my question on that if you're curious.
Android's AsyncTask: multiple params, returning values, waiting
How the heck to I just show a loading dialog while this call happens without proceeding through the rest of my application?
EDIT
Here's my AsyncTask attempt.
private class PostToFile extends AsyncTask<PostToFile, Void, Void>{
private String functionName;
private ArrayList<NameValuePair> postKeyValuePairs;
private String result = "";
public PostToFile(String function, ArrayList<NameValuePair> keyValuePairs){
functionName= function;
postKeyValuePairs = keyValuePairs;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(BaseActivity.getInstance(), "Loading", "Please wait...", true, false);
}
#Override
protected Void doInBackground(PostToFile... params) {
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair(FUNCTION_KEYWORD, functionName));
for (int i = 0; i < postKeyValuePairs.size(); i++) {
nameValuePairs.add(postKeyValuePairs.get(i));
}
try{
// ***do the POST magic.***
result = response.toString();
}
catch (Exception e){
// clean up my mess
}
return null;
}
private String getResult(){
return result; // can I use this somehow???
}
#Override
protected void onPostExecute(Void result) {
progressDialog.dismiss();
}
}
And when I use it:
new PostToPHP(FUNCTION_NAME, postPairings){
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try
{
if (result != null && !result.startsWith("null"))
{
JSONArray jArray = new JSONArray(result);
parseData(jArray);
}
}
catch (JSONException e)
{
Log.e(Constants.LOG_TAG, e.toString());
}
};
}.execute()
The problem is, I have a couple of these calls back to back, and they're each dependent on each other. So the first one starts, and the second one starts immediately after the first one starts, but before the first one is finished. So I get erroneous behavior. How can I start the second call only after the first is completely done?
Maybe this will work, I haven't tested, but you can try:
public class MyTask extends AsyncTask<String, Void, String> {
private int flag;
public MyTask(int flag) {
this.flag = flag;
}
#Override
protected String doInBackground(String... params) {
switch (flag) {
case 1:
return doNetworking1();
break;
case 2:
return doNetworking2();
break;
case 3:
return doNetworking3();
break;
default:
return doNetworking1();
}
}
#Override
protected void onPreExecute() {
//show progress dialog
}
#Override
protected void onPostExecute(String s) {
//hide progress dialog
switch (flag) {
case 1: //do something with result
new MyTask(2).execute();
break;
case 2: //do other stuff
new MyTask(3).execute();
break;
case 3: //do event more stuff
break;
default:
//do something
}
}
}
and usage:
new MyTask(1).execute();
In cases of network connections I would use IntentService instead of AsyncTask.
For example create IntentServices for network connection:
public class NetworkCallIntentService extends IntentService {
public static final String BROADCAST_ACTION = "com.yourpackage:NETWORK_CALL_BROADCAST";
public static final String RESULT = "com.yourpackage:NETWORK_CALL_RESULT";
public NetworkCallIntentService() {
super(NetworkCallIntentService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent) {
// get data from intent if needed
// do the call that takes long time
// send broadcast when done
Intent intent = new Intent(BROADCAST_ACTION);
intent.putExtra(RESULT, "some_result");//and more results
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
Next, start that service from activity, show progress dialog and move code responsible for showing next screen to BroadcastReceiver#onReceive() method:
public class SomeActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//start service
Intent serviceIntent = new Intent(this, NetworkCallIntentService.class);
//put extras into intent if needed
//serviceIntent.putExtra("some_key", "some_string_value");
startService(serviceIntent);
//here just show progress bar/progress dialog
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(mNetworkCallReceiver,
new IntentFilter(NetworkCallIntentService.BROADCAST_ACTION));
}
#Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(mNetworkCallReceiver);
}
private BroadcastReceiver mNetworkCallReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//hide progress bar/progress dialog
//here get results from intent extras
String result = intent.getStringExtra(NetworkCallIntentService.RESULT);
//process results and continue program(go to next screen, show error message etc.)
}
}
}
Declare service in manifest file:
<service
android:name="com.yourpackage.DownloadSvtValuesIntentService"
android:exported="false" >
</service>
I have an android app I am just experimenting things on and I cannot seem to figure out why my app force closes when I update a TextView via a while loop. When I comment out the updateText method it runs fine.
public class GameThread extends Thread {
Thread t;
private int i;
private boolean running;
private long sleepTime;
GameView gv;
public GameThread() {
t = new Thread(this);
t.start();
i = 0;
sleepTime = 1000;
}
public void initView(GameView v) {
this.gv = v;
}
public void setRunning(boolean b) {
this.running = b;
}
public boolean getRunning() {
return running;
}
public void run() {
while(running) {
i++;
update();
try {
t.sleep(sleepTime);
} catch(InterruptedException e) {
}
}
}
public void update() {
gv.setText(i); // when this is uncommented, it causes force close
Log.v("Semajhan", "i = " + i);
}
public class GameView extends LinearLayout {
public TextView tv;
public GameView(Context c) {
super(c);
this.setBackgroundColor(Color.WHITE);
tv = new TextView(c);
tv.setTextColor(Color.BLACK);
tv.setTextSize(20);
this.addView(tv);
}
public void setText(int i) {
tv.setText("i count: " + i);
}
public class Exp extends Activity {
GameThread t;
GameView v;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
v = new GameView(this);
setContentView(v);
t = new GameThread();
t.setRunning(true);
t.initView(v);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (t.getRunning() == true) {
t.setRunning(false);
Log.v("Semajhan", "STOPPED");
} else {
t.setRunning(true);
Log.v("Semajhan", "RESTART");
}
}
return true;
}
protected void onDestroy() {
Log.v("Semajhan", "DESTROYING");
super.onDestroy();
}
protected void onStop() {
Log.v("Semajhan", "Stopping");
super.onStop();
}
I though i'd post the whole app since it is relatively small and so that I could get some help without confusion.
First, when you get a Force Close dialog, use adb logcat, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your crash.
In this case, your exception will be something to the effect of "Cannot modify the user interface from a non-UI thread". You are attempting to call setText() from a background thread, which is not supported.
Using a GameThread makes sense if you are using 2D/3D graphics. It is not an appropriate pattern for widget-based applications. There are many, many, many, many examples that demonstrate how to create widget-based applications without the use of a GameThread.
You have to call it from the UI thread.
For more info check: Painless Threading .
If you decide to use a Handler, the easiest solution for you will be to:
Extend a View, override it's onDraw , in it draw the game objects, after you have calculated the game data for them first of course
The Handler: (in your Activity)
private Handler playHandler = new Handler() {
public void handleMessage(Message msg) {
gameView.postInvalidate(); // gameView is the View that you extended
}
};
The game thread has a simple
Message.obtain(playHandler).sendToTarget();
In 2 words, the View is responsible for the drawing (you can move the calculations in a separate class, and call it before the onDraw), the thread is responsible only for scheduled calls to the Handler, and the Handler is responsible only to tell the View to redraw itself.
You cannot update the UI of your app outside of the UI Thread, which is the 'main' thread you start in. In onCreate(Context) of you app, you are creating the game thread object, which is what is doing the updating of your UI.
You should use a Handler:
http://developer.android.com/reference/android/os/Handler.html