Android Honeycomb: Fragment not able to start AsyncTask? - android

I've run into this error before, but thought it was some mistake by the strict mode system. However, it apparently was right as I sadly found out now. :(
My programm is made of one Activity and loads of Fragments. I have a NetworkWorker fragment, which starts URL requests like this:
public void startURLRequest(Fragment target, String url, String message)
{
if (asyncTask != null) asyncTask.cancel(true);
asyncTask = new FragmentHttpHelper(url, message, target);
asyncTask.doInBackground();
return;
}
FragmentHttpHelper is a custom inner class derived from AsyncTask:
private class FragmentHttpHelper extends AsyncTask<Void, Void, String>
{
//...
#Override
protected String doInBackground(Void... params)
{
if (CheckInternet())
{
try
{
URL myURL = new URL(url);
httpClient = new DefaultHttpClient();
if (this.message == null)
{
httpRequest = new HttpGet(myURL.toExternalForm());
}
else
{
httpRequest = new HttpPost(myURL.toExternalForm());
HttpEntity myEntity = new StringEntity(message, "UTF-8");
((HttpPost) httpRequest).setEntity(myEntity);
}
// and so on...
}
//catches
finally
{
// auf jeden Fall Verbindung beenden
if (httpRequest != null) httpRequest.abort();
// if (httpClient != null) httpClient.close();
}
}
else
{
showDialog(getString(R.string.net_notify_no_network), target);
}
//...
}
/**
* gets called after AsyncTask has finished
*/
protected void onPostExecute(String result)
{
if (target == null)
{
((NetworkWorkerListener) getActivity()).onDownloadHasFinished((!result.contentEquals(ERROR)), result);
}
else
{
((NetworkWorkerListener) target).onDownloadHasFinished((!result.contentEquals(ERROR)), result);
}
}
}
NetworkWorkerListener is just an interface for a callback on the Fragment which started the URL request. This class has always worked fine when I used it in my 2.2 app. I would derive it in my Activities then.
Now, if a menu item is selected, another worker Fragment starts the URL request via the above method and opens a loading dialog:
FragmentManager fragmentManager = getFragmentManager();
NetworkWorker network = (NetworkWorker) fragmentManager.findFragmentByTag(TabletMain.NETWORK);
if (network == null) return WorkerFeedback.NO_NETWORK_WORKER;
myDialog = LoadingDialog.createInstance(getString(R.string.notify_download), this);
myDialog.show(fragmentManager, TabletMain.ONETIME);
network.startURLRequest(this, someurl, null);
At least that's what supposed to happen.
Instead, when I click the menu item, my app freezes and no loading dialog is shown until. Next happening is the reaction to the end of the download (or, in my case an error message, as I am sending nonsense strings). Meaning onPostExecute() was reached.
I feel really stuck now - is it not possible to use AsyncTask with Fragments? Or did I do something wrong?
Thanks for your help,
jellyfish

Don't call doInBackground directly, call execute instead (on the async task)

Related

Android: Unable to swap activity and close app when clicking button

I have an activity (let us call "ResultActivity") which receives some results from another activity ("AuditTest"), and there will be two buttons for user:
First button: let user to send the above-mentioned results to the server via connecting to a PHP webpage, and then go back to the main menu (so called "MainActivity")
Second button: let user to send the results to the server by ASyncTask, then ASyncTask will finish
For going back to the main menu, I have tried to use Intent, setClass, startactivity and finish. It could manage to go back to main menu, but this ResultActivity won't finish. Once you press back button on the phone, startActivity will close and that ResultActivity comes out again.
And for quitting the app, I also tried to use simply finish() method to achieve this, but still have no luck.
So here is the code for ResultActivity, and I also included comments to briefly illustrate operations in the app
public class ResultActivity extends AppCompatActivity
{
TextView result_showResultLbl;
Button result_retry,result_leaveBtn;
TextView result_noticeLbl;
boolean fail = false;
char PF = ' ';
final int BUTTON_RETRY=1;
final int BUTTON_LEAVE=2;
#Override
protected void onCreate(Bundle savedInstanceState)
{
//Summary: Just initializing all widgets, get back all elements
//from previous activity and show result to user.
........
........
//setting up onClickListeners for two buttons (result_retry and
result_leavebtn. So the ASyncTask is called SendData.
result_retry.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//send result to server
SendData s = new
SendData(PF,FArecord,questionAnswers,BUTTON_RETRY);
s.execute();
}
});
result_leaveBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SendData s = new
SendData(PF,FArecord,questionAnswers,BUTTON_LEAVE);
s.execute();
}
});
}
//ASyncTask class SendData
private class SendData extends AsyncTask<String, String ,String>
{
.......//variables, not mentioned here
//parameters are data from previous activity and which button that user clicked.
SendData(..........)
{
.........
}
#Override
protected String doInBackground(String... strings)
{
//doInBackground of SendData will get all data sent from
//previous activity, then use HttpURLConnection to send out the data and get back the result.
...............//get all data from previous activity
//send php request
URL dbServerURL = new URL(URLAddr);
HttpURLConnection conn = (HttpURLConnection) dbServerURL.openConnection();
Log.v("ResultActivity","URL = "+URLAddr);//for debug
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
OutputStream out = conn.getOutputStream();
//concat the information fields and then send out the data
parameters = ....;// parameters is a concatenated string to carry the data.
out.write(parameters.getBytes());
Log.v("ResultActivity","Trying to send parameter: "+parameters);
out.flush();
out.close();//finish sending out
//data request is successful
InputStream is = conn.getInputStream();
String response = FacilityAuditTest.convertStreamToString(is);
if (response.contentEquals("Error"))
{
//error occurred
Log.v("ResultActivity","Received String: "+response);
} else
{
//get JSON object if no error
JSONObject json = new JSONObject(response);
success = json.getInt("success");
Log.v("ResultActivity","Success= "+success);
Log.v("ResultActivity","Record sent successfully");
}
is.close();
conn.disconnect();
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String s)
{
super.onPostExecute(s);
if (success == 1)
{
//I use runOnUiThread() to detect which button the user has clicked, then swap activity or close the app.
runOnUiThread(new Runnable() {
#Override
public void run() {
if (clickedButtonType == BUTTON_RETRY)
{
//if user clicked retry button before, show a success message and then go back to information entry page
Toast.makeText(ResultActivity.this,R.string.result_sendSuccess,Toast.LENGTH_SHORT).show();
Intent i = new Intent();
i.setClass(ResultActivity.this,AuditorInf.class);
startActivity(i);
finish();
}
if (clickedButtonType == BUTTON_LEAVE)
{
Toast.makeText(ResultActivity.this,R.string.result_sendSuccess,Toast.LENGTH_SHORT).show();
//I tried to use android.os.Process.killProcess to kill app, but the app still restart and show this ResultActivity after running killProcess. android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
//finish();
}
}
});
} else
{
//just show fail to transmit message, but the action is similar to (success == 1)
}
}
}
}
As what I have added in the comments of the code, the app won't switch to main menu and close current activity. It also cannot close the app by calling finish. I can give more detail code if you need. Thank you.

In which case AsynkTaskLoader uses cache data?

I am learning about the Loader and its implementation with AsynkTaskLoader.
In many examples I have seen that in the AsynkTaskLoader's onStartLoadingmethod is where the cache is checked, and if it is not null the result is delivered directly. However, this method is only called the first time the Loader is
initiated, since for example when the device configuration changes and the Loader is initiated again from Activity's OnCreate method the Loader call directly onLoadFinished method.
To better understand the functionally of the Loader I would like to know how I can recreate a case where the cache is used.
Here some example code:
static class ExampleAsyncTaskLoader extends AsyncTaskLoader<String>{
String mCacheData;
Bundle mArgs;
ExampleAsyncTaskLoader(Context context, Bundle args) {
super(context);
mArgs = args;
}
#Override
protected void onStartLoading() {
/* If no arguments were passed, we don't have a query to perform. Simply return. */
if (mArgs == null) {
return;
}
if (mCacheData != null){
deliverResult(mCacheData);
}else {
forceLoad();
}
}
#Override
public String loadInBackground() {
/* Extract the search query from the args using our constant */
String searchQueryUrlString = mArgs.getString(SEARCH_QUERY_URL_EXTRA);
/* If the user didn't enter anything, there's nothing to search for */
if (searchQueryUrlString == null || TextUtils.isEmpty(searchQueryUrlString)) {
return null;
}
/* Parse the URL from the passed in String and perform the search */
try {
URL githubUrl = new URL(searchQueryUrlString);
return NetworkUtils.getResponseFromHttpUrl(githubUrl);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
#Override
public void deliverResult(String data) {
mCacheData = data;
super.deliverResult(data);
}
}
In case the Activity is destroyed and recreated (as in configuration change) if the Loader data is ready - you'll receive it via the deliverResult.
However, In the case of Activity pause and then resume - the default behavior is to re-fetch the data, This is when the "local cache" makes sense.

Populate global Array from AsyncTask and call on onCreate

Ever get those moments where you stare at a piece of code for an hour and still can't come up with an answer? Yeah that's me now.
I'm working on a final project for class and I can't get this one piece of code to work. It is absolutely crucial that it works, or else it defeats the purpose of the program. I even asked my professor for help... and he doesn't know how to help me solve the issue. I posted a similar problem a day ago but I want to re-ask in a different way to see if it helps (Sorry if its a re-post, I have no other source for help :/).
My problem is that I need to access array elements on my MainActivity after its populated inside an AsyncTask class. The array is defined globally but as soon as I try to access it's element or size, it crashes. I need to be able to call this array outside of AsyncTask.
I've searched for hours and tried "returning" the array from AsyncTask but it crashes as well.
Here is my code (I've included comments as to where it crashes):
public class PostsActivity extends Activity {
public static GlobalRates[] gr;
TextView view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_posts);
view = (TextView) findViewById(R.id.textView1);
BitRateFetcher br = new BitRateFetcher();
br.execute();
// !!! Line below crashes !!!
Log.i("BitRateFetcher", "Size from onCreate: " + gr.length);
}
private class BitRateFetcher extends AsyncTask<Void, Void, GlobalRates[]> {
private static final String TAG = "BitRateFetcher";
public String BIT_PAY_SERVER = "https://bitpay.com/api/rates";
private ProgressDialog dialog;
GlobalRates[] test;
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(PostsActivity.this);
dialog.setMessage("Please Wait... Downloading Information");
dialog.show();
}
#Override
protected GlobalRates[] doInBackground(Void... params) {
try {
// Create an HTTP client
HttpClient client = new DefaultHttpClient();
HttpGet getBitRates = new HttpGet(BIT_PAY_SERVER);
// Perform the request and check the status code
HttpResponse bitRatesResponse = client.execute(getBitRates);
StatusLine bitRatesStatus = bitRatesResponse.getStatusLine();
if (bitRatesStatus.getStatusCode() == 200) {
HttpEntity entity = bitRatesResponse.getEntity();
InputStream content = entity.getContent();
try {
// Read the server response and attempt to parse it as
// JSON
Reader reader = new InputStreamReader(content);
Gson gson = new Gson();
test = gson.fromJson(reader, GlobalRates[].class);
content.close();
entity.consumeContent();
} catch (Exception ex) {
Log.e(TAG, "Failed to parse JSON due to: " + ex);
failedLoadingPosts();
}
} else {
Log.e(TAG, "Server responded with status code: "
+ bitRatesStatus.getStatusCode());
failedLoadingPosts();
}
} catch (Exception ex) {
Log.e(TAG, "Failed to send HTTP POST request due to: " + ex);
failedLoadingPosts();
}
return test;
}
#Override
protected void onPostExecute(GlobalRates[] test) {
Log.i(TAG, "Test Size: " + test.length); // Returns 158
gr = test;
Log.i(TAG, "Gr Size: " + gr.length); // Returns 158
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.posts, menu);
return true;
}
private void failedLoadingPosts() {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(PostsActivity.this,
"Failed to load Posts. Have a look at LogCat.",
Toast.LENGTH_SHORT).show();
}
});
}
}
Here is the error Log:
04-21 20:30:01.954: E/AndroidRuntime(32595): FATAL EXCEPTION: main
04-21 20:30:01.954: E/AndroidRuntime(32595): Process: com.example.postsactivity, PID: 32595
04-21 20:30:01.954: E/AndroidRuntime(32595): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.postsactivity/com.example.postsactivity.PostsActivity}: java.lang.NullPointerException
My class partner and I are unsure as to how we can call the global array (gr) properly. I understand that onCreate is not going to wait for AsyncTask to finish. What can we do to get this to work? Thanks for the help and understanding. You guys can prove more useful than my professor...
the problem is that you are accessing array of globalrates before it was initialized in your postExecute(). There are a lot of way to do this one of which is creating an interface or creating callbacks that waits for globalrate to be initialized after ur background thread is done.
Create a Interface
public interface SampleInterface {
void globalResultBackground(GlobalRates[] gr);
}
implements the interface to ur activity
public class PostsActivity extends Activity implements SampleInterface
pass the interface to your asynctask
//in the activity
BitRateFetcher br = new BitRateFetcher(this);
br.execute();
//in the asynctask class
SampleInterface si;
public BitRateFetcher(SampleInterface si){
this.si = si;
}
pass the result of the background thread to ur interface method
#Override
protected void onPostExecute(GlobalRates[] test) {
si.globalResultBackground(test);
Log.i(TAG, "Test Size: " + test.length); // Returns 158
gr = test;
Log.i(TAG, "Gr Size: " + gr.length); // Returns 158
if (dialog.isShowing()) {
dialog.dismiss();
}
}
in the activity where the you implemented the interface and generated the globalResultBackground method
#Override
public void globalResultBackground(GlobalRates[] gr) {
//you can freely access the globalrates here because this is called when the
//background thread is done
Log.i("BitRateFetcher", "Size from onCreate: " + gr.length);
}
Pass in your activity to the AsyncTask and directly access its gr in your onPostExecute
Sample code:
private class BitRateFetcher extends AsyncTask {
PostsActivity activity;
...
public BitRateFetcher(PostsActivity activity){
this.activity= activity;
}
...
#Override
protected void onPostExecute(GlobalRates[] test) {
activity.gr = ...
}
}
In PostsActivity,
BitRateFetcher br = new BitRateFetcher(this);
Take care to attach and reAttach the activity to handle scenarios such as device rotations.
EDIT: Just noticed you've got a static, so you don't even need to pass in the activity. The same principle applies though.
GlobalRates[] gr is public and static, so you dont really new GlobalRates[] test
simply replace every instance of test with gr:
test = gson.fromJson(reader, GlobalRates[].class);
to
gr = gson.fromJson(reader, GlobalRates[].class);
if you need to access gr in Oncreate then move that logic to postExecute
Log.i("BitRateFetcher", "Size from onCreate: " + gr.length);

Android Async Task Blocks when called from fragment

I'm trying to make what I thought would be a simple call to a google app engine project.
From a unit test, the call works fine if I access the api directly. I can't do this however, since the call is blocking, so it has to be run from an async task.
What seems to happen is:
A unit test inheriting from ActivityInstrumentationTestCase2 sets up the activity and populates some values
Then I click the button to make the request:
final Button mBet = (Button) mActivity.findViewById(R.id.mybutton);
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
mName.setText("blah");
mBet.performClick();
}
});
Then this calls the underlying code to start an async task:
public void bet() throws IOException, InterruptedException {
mMakeTask = new AsyncTask<Void, Void, Model>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
mBetWait = new ProgressDialog(getActivity());
mBetWait.setCancelable(true);
mBetWait.show();
}
#Override
protected Model doInBackground(Void... voids) {
try {
mModel = mController.makeBet(mModel.getBetName(), mModel.getDescription(), mModel.getAnswer(), mModel.getAgainst());
} catch (IOException e) {
e.printStackTrace();
}
return mModel;
}
#Override
protected void onPostExecute(BetModel model) {
super.onPostExecute(model);
Context context = getActivity();
Intent intent = new Intent(context, OtherActivity.class);
intent.putExtra("bet", mModel);
mBetWait.dismiss();
context.startActivity(intent);
}
};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mMakeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
mMakeTask.execute();
}
}
And I forgot to add... when controller is called, it boils down to an appengine call:
public BetInfo makeBet(BetRequest info) throws IOException {
Betrequestendpoint.Builder endpointBuilder = new Betrequestendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
Betrequestendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
BetRequest result = endpoint.insertBetRequest(info).execute();
BetInfo resultInfo = new BetInfo();
resultInfo.setName(result.getName());
resultInfo.setDescription(result.getDescription());
resultInfo.setId(result.getId());
return resultInfo;
}
And to make this more odd... the request call appears to be blocked from the client with the stack trace:
"<13> AsyncTask #2"#831,912,607,944 in group "main": RUNNING
toUpperCase():3266, Character {java.lang}
toUpperCase():3251, Character {java.lang}
toUpperCase():162, CaseMapper {java.lang}
toUpperCase():1548, String {java.lang}
initServiceInfo():152, Services {org.apache.harmony.security.fortress}
getCacheVersion():211, Services {org.apache.harmony.security.fortress}
getInstance():137, Engine {org.apache.harmony.security.fortress}
getInstance():77, KeyManagerFactory {javax.net.ssl}
createDefaultKeyManager():362, SSLParametersImpl {com.android.org.conscrypt}
getDefaultKeyManager():355, SSLParametersImpl {com.android.org.conscrypt}
<init>():111, SSLParametersImpl {com.android.org.conscrypt}
getDefault():146, SSLParametersImpl {com.android.org.conscrypt}
<init>():34, OpenSSLSocketFactoryImpl {com.android.org.conscrypt}
newInstanceImpl():-1, Class {java.lang}
newInstance():1208, Class {java.lang}
getDefault():56, SSLSocketFactory {javax.net.ssl}
<clinit>():114, HttpsURLConnection$NoPreloadHolder {javax.net.ssl}
getDefaultSSLSocketFactory():163, HttpsURLConnection {javax.net.ssl}
copyWithDefaults():363, OkHttpClient {com.android.okhttp}
open():345, OkHttpClient {com.android.okhttp}
open():340, OkHttpClient {com.android.okhttp}
openConnection():28, HttpHandler {com.android.okhttp}
openConnection():479, URL {java.net}
buildRequest():133, NetHttpTransport {com.google.api.client.http.javanet}
buildRequest():68, NetHttpTransport {com.google.api.client.http.javanet}
execute():858, HttpRequest {com.google.api.client.http}
executeUnparsed():410, AbstractGoogleClientRequest {com.google.api.client.googleapis.services}
executeUnparsed():343, AbstractGoogleClientRequest {com.google.api.client.googleapis.services}
execute():460, AbstractGoogleClientRequest {com.google.api.client.googleapis.services}
makeBet():36, BetController {com.chillypixel.youwereright.controller}
makeBet():36, BetApiController {com.chillypixel.youwereright.controller}
doInBackground():129, BetFragment$1 {com.chillypixel.youwereright.bet}
doInBackground():117, BetFragment$1 {com.chillypixel.youwereright.bet}
call():288, AsyncTask$2 {android.os}
run():237, FutureTask {java.util.concurrent}
runWorker():1112, ThreadPoolExecutor {java.util.concurrent}
run():587, ThreadPoolExecutor$Worker {java.util.concurrent}
run():841, Thread {java.lang}
What seems to happen, is I run the test, and it gets to where the progress dialog pops up.
If I then manually interact with the app in the emulator (and hit back or something) the progress dialog goes away and the call completes - then the test passes.
If I just leave it alone though, it appears that the progress dialog just happily spins forever.
Any help would be greatly appreciated.

Android/Java: How do i implement AsyncTask?

I've made an app that sends a request to a webserver in a specified interval and gets XML data. It then parses the XML data, gets information from the phone (text messages, contacts or something similar) and shoots it back to the server with a http post request.
The problem is that it usually takes a few seconds for the app to get the info, which often leaves the app crashing. A dialog comes up saying the app has become unresponsive and asks if i want to close the app or wait, if i press wait it eventually starts working again.
Is AsyncTask the right solution to this problem?
Another thing i don't really understand is how AsyncTask actually works. Let's say i have two methods that do a lot of work and crashes the app, can i put both of them in one AsyncTask and just call them from doInBackground()?
I have also implemented something similar you are trying. That is sending request to server, receive XML response, parse XML, show result. View This. I have used AsyncTask for this.
Here is how i have implemented it using AsynTask
private class AsyncClass extends AsyncTask<Void, Void, Bundle>{
#Override
protected Bundle doInBackground(Void... arg0) {
Bundle b=startProcess();
// startBundle() method do all the processing and return result in a bundle. You can as many methods from within startBundle() method
return b;
}
#Override
protected void onPostExecute(Bundle result) {
Log.d(TAG , "In onPostExecute");
dialog.dismiss();
if(result==null)
Toast.makeText(cont, "Can't process query.\nTry again later.", Toast.LENGTH_LONG).show();
else{
Intent in = new Intent(cont, QueryResultDisplay.class);
Log.d(TAG , "Displaying");
in.putExtras(result);
cont.startActivity(in);
}
}
I give you brief description about your problem.
There are many possibility that you don't get data from server
if your network speed is very slow and you try to get all the
information from server and XML data then in this case if network crash then it show you error
if you're making request to that page which is not in server
Now, if you are facing the problem in code, then I give you the complete code of AsyncTask class which I had implemented in my project and it work fine.
private class GetLoginResponse extends AsyncTask<Void, Void, Boolean> {
private ProgressDialog progressDialog;
private String email;
private String password;
public GetLoginResponse(String emailId, String paswd) {
this.email = emailId;
this.password = paswd;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(LoginActivity.this, "",
"Loading....", true, false);
}
#Override
protected Boolean doInBackground(Void... params) {
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpclient.execute(httpGet);
//here u can check the reponse is ok and 200
} catch (NetworkException e) {
isNetworkError = true;
}
return false;
}
#Override
protected void onPostExecute(Boolean data) {
progressDialog.dismiss();
System.out.println("lOGIN RESPONSE for email = " + email + data);
}
}// end AsyncTask
This would solve your problem.
Yes, you can use AsyncTask.
The code called in doInBackground() must not touch the UI.
You can touch the UI thread with publishProgress().

Categories

Resources