I recently started developing programs in Android, and have a small problem with this simple code. I am trying to parse the title of a website and stored in a string, but so far unsuccessful. Is it because I am not doing it in Async ? Or can it be a different issues all together?
public class MainActivity extends Activity implements OnClickListener {
private Button btnSearch;
private EditText editTextCarReg;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//-------------------------------------------------
btnSearch = (Button) findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(this);
//------------------------------------------------
editTextCarReg = (EditText) findViewById(R.id.editTextRegistration);
editTextCarReg.setOnClickListener(this);
}
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void example() throws IOException
{
Document doc = Jsoup.connect("http://http://9gag.com/").get();
String title = doc.title();
}
public void onClick(View v)
{
if(v.getId() == btnSearch.getId())
{
try {
example();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
}
From your logs:
android.os.NetworkOnMainThreadException
This exception is thrown when an application attempts to perform a networking operation on its main thread.
Run Jsoup logic in AsyncTask.
(be sure that you added <uses-permission android:name="android.permission.INTERNET"/> as well)
You can write something like:
class JsoupTask extends AsyncTask<String, Void, Void> {
private Exception exception;
protected void doInBackground(String... url) {
Document doc = Jsoup.connect(url).get();
String title = doc.title();
...
}
protected void onPostExecute(RSSFeed feed) {
// here you can update your UI thread through Handler, for example
}
}
And call, like:
new JsoupTask().execute(url);
public static int SDK_INT = android.os.Build.VERSION.SDK_INT;
write this code before your request
if (SDK_INT >= 10) {
ThreadPolicy tp = ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
}
Using asynctask is better practice.
Related
Sorry if this seems a basic question. I've updated Android Studio and notice some memory leak warnings on my AsyncTasks saying I should make them static. I have made them static but can't seem to make anything like List, ProgressBar, ImageView work without getting the same memory leak warning. It seems I can't win no matter which way I try it. I guess my questions are:
Are AsyncTasks supposed to be static? The official documentation doesn't make it static but my IDE fires warnings saying they should.
If they are meant to be static, how can I start and stop a ProgressBar within the static AsyncTask.
EDIT
This still throws "This AsyncTask class should be static or leaks might occur"
private class DownloadCategoryTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
String url = Config.API_URL +
"/Index.aspx?" +
"type=3&" +
"site_id=" + SITE_ID;
String method = "GET";
String array_name = "categories";
Downloaded_category_array = Config.getJSONNew(url, method, array_name, context);
return "";
}
protected void onPostExecute(String result) {
if(isCancelled()){
return;
}
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
//Update your UI here
//showProgressBar();
}
});
Populate_category_list();
}
}
Try this solution which I found:
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progress_bar);
}
public void startAsyncTask(View v) {
ExampleAsyncTask task = new ExampleAsyncTask(this);
task.execute(10);
}
private static class ExampleAsyncTask extends AsyncTask<Integer, Integer, String> {
private WeakReference<MainActivity> activityWeakReference;
ExampleAsyncTask(MainActivity activity) {
activityWeakReference = new WeakReference<MainActivity>(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
MainActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return;
}
activity.progressBar.setVisibility(View.VISIBLE);
}
#Override
protected String doInBackground(Integer... integers) {
for (int i = 0; i < integers[0]; i++) {
publishProgress((i * 100) / integers[0]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Finished!";
}
}
}
Reference: here
No, No need to make AsyncTasks as static.
If non static methods are trying to modify static members then IDE throws warning to make it static.
If you want to update your UI from AsyncTask use 'runOnUiThread'.
runOnUiThread(new Runnable() {
#Override
public void run() {
//Update your UI here
showProgressBar();
}
});
Looks like you are using anonymous inner class.
Here is the solution,
private class LoadData extends AsyncTask<Void, Void, String> {
LoadData() {
}
#Override
protected String doInBackground(Void... params) {
return "task finished";
}
#Override
protected void onPostExecute(String result) {
runOnUiThread(new Runnable() {
#Override
public void run() {
//Update your UI here
//showProgressBar();
}
});
}
}
//Execute your task
new LoadData().execute();
I have a button in my application which on click should run AsyncTask but the doInBackground method is not being called, I am trying to connect to my server using okHTTP inside AsyncTask, here is my code,
public class Home extends Fragment implements View.OnClickListener {
Button button;
public static final String TAG = "aj";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home, container, false);
button = (Button) v.findViewById(R.id.button);
button.setOnClickListener(this);
FirebaseMessaging.getInstance().subscribeToTopic("test");
FirebaseInstanceId.getInstance().getToken();
return v;
}
#Override
public void onClick(View v) {
sendRequest myTask = new sendRequest();
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.HONEYCOMB)
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
myTask.execute();
}
private class sendRequest extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... params) {
Log.v(TAG, "doInBackground");
String token = FirebaseInstanceId.getInstance().getToken();
OkHttpClient client = new OkHttpClient();
RequestBody body = null;
if (token != null) {
body = new FormBody.Builder()
.add("Token", token)
.build();
}
Request request = new Request.Builder()
.url("https://www.example.com/aj20010319")
.post(body)
.build();
try {
client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
I don't know why the doInBackground method is not being called, the logger doesn't log anything regarding it. And if I am correct, it is setup correctly. I tried the solutions in other threads but none is working.
checked your code in my machine removing the complexities its working fine i pasted the code bellow.
Two thing it can happen
problem with build version . just call the myTask.execute();remove all the if else
It might happen with the logcat . if possible make toast from the doInBackground
Toast.makeText(getApplicationContext(), "text", Toast.LENGTH_LONG).show();
If you are searching in the logcat it always searches by message not by tag so search by doInBackground not by aj
public class MainActivity extends Activity implements View.OnClickListener{
Button button;
#Override
public void onClick(View arg0) {
sendRequest myTask = new sendRequest();
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.HONEYCOMB)
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
myTask.execute();
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button)findViewById(R.id.button1);
button.setOnClickListener(this);
}
private class sendRequest extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... params)
{
int i=0;
while(++i<100)
{
Log.i("www", "www");
}
return null;
}
}
}
You need to instantiate the class
new myTask().execute();
Inside the onClickListener
I'm using Jsoup on my Android application, to read xml file get by a webservice restful.
The Jsoup library works perfectly until the xml file contains few number of records.
But when I get a xml with 50k or 60k of records, I observed that the Jsoup allocate memory until 230MB 240MB. This is a problem because with
android:largeHeap="true"
I have 256MB of memory allocable.
This is a saple code, try it yourself
public class MainActivity extends ActionBarActivity {
private static Handler mHandler = new Handler();
private static Context context;
static TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
textView.setText("5 seconds to start task");
context = this.getApplicationContext();
mHandler.postDelayed(new Runnable() {
public void run() {
new syncDataWS(context).execute();
}
}, 5000);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private static class syncDataWS extends AsyncTask<Void, String, Void> {
Context mContext;
public syncDataWS(Context context) {
this.mContext = context;
}
#Override
public void onPreExecute() {
super.onPreExecute();
textView.append("\nStart task");
}
#Override
protected Void doInBackground(Void... params) {
try {
publishProgress("Start call XML page");
Document WS_Document = Jsoup.connect("XML_EXAMPLE").maxBodySize(0).timeout(10 * 100000).get();
publishProgress("End call XML page");
publishProgress("Get rows of document");
Elements XML_RESULT_WS = WS_Document.select("row");
publishProgress("Record number : " + Integer.toString(XML_RESULT_WS.size()));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
if (values != null && values.length > 0) {
textView.append("\n" + values[0]);
}
}
#Override
protected void onPostExecute(Void result) {
textView.append("\nEnd task");
}
}
}
and this is a example of xml
<XMLDataResponse xmlns="www.example.net"><XMLDataResult><root xmlns=""><row ID="1" ID2="2" ID3="3" ID4="4" F1="0.000000000000000e+000" F2="0.000000000000000e+000" F3="0.000000000000000e+000" F4="" F5="0.000000000000000e+000" F6="0.000000000000000e+000"/></root></XMLDataResult></XMLDataResponse>
Take the row tag and copy + paste it until you have 60 thousand records in your XML EXAMPLE. Put it wherever you want, provided it can be achievable with via http call (URL). Copy the url in the code
Jsoup.connect("COPY URL OF XML HERE")
And you can see what I mean.
I need a solution to solve this allocation issue, because sometimes, not always, the allocation arrives at 256MB and my app crashes.
You won't be able to solve this problem with Jsoup because Jsoup creates a complete DOM tree from the parsed XML which grows bigger and bigger inside your memory.
Jsoup btw. is meant as an HTML parser in the first place.
I'd use an event based XML parser like XMLPullParser.
I'm trying to return value from my asynctask in DoInBackground, but calling get() method freezes my UI. How can I re-write my code to a callback method? :
public class GetUrlDataTask extends AsyncTask<String, Integer, String> {
String response;
HttpUtils util;
#Override
protected String doInBackground(String... params) {
try {
util = new HttpUtils(params[0]);
response = util.getContent();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return response;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
In my activity I get result as response = new GetUrlDataTask().execute("site").get;
You shouldn't use .get() if the Async task is going to take any decent amount of time (which it usually is).
Instead, you can either use a message/handler/service/etc, or you can simply use the onPostExecute(Result) method.
EDIT: New Code. Based on your description, it seems like you need to use an interface.
If you need to have Asynctask in another class, then an interface is probably your best option.
TestTask.java (your separate Asynctask):
import android.os.AsyncTask;
// Remember to change object type <> to what you need
public class TestTask extends AsyncTask<Object,Object,Object> {
public interface OnTaskCompleted{
void onTaskCompleted();
}
private OnTaskCompleted listener;
public TestTask(OnTaskCompleted listener){
this.listener = listener;
}
protected void onPostExecute(Object o){
// Call the interface method
if (listener != null)
listener.onTaskCompleted();
}
#Override
protected Object doInBackground(Object... params) {
// The sleep() is just to simulate activity and delay
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
MainActivity.java (or any other activity):
public class MainActivity extends Activity {
private boolean status = false;
private OnTaskCompleted listener = new OnTaskCompleted() {
public void onTaskCompleted() {
status = true;
Toast.makeText(MainActivity.this, "Status: " + status, Toast.LENGTH_SHORT).show();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(MainActivity.this, "Status: " + status, Toast.LENGTH_SHORT).show();
new TestTask(listener).execute("Testing");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
I'm not a big fan of having AsycTask tasks in separate classes, especially if you need to use the response. It makes interacting with the response and local variables overly difficult considering how easy it is when implemented as an inner class.
I'm guessing you put it in its own class so you can reuse it. I would consider keeping the AsycTask as an inner class and calling outside reusable objects/methods in doInBackground(). This will keep the code DRY and allow your activity to do what it needs with the response.
public class MyActivity extends Activity {
TextView textview;
//...
private class GetUrlTask extends AsyncTask<String, Integer, String> {
protected String doInBackground(String... params) {
return new GetHttpResponse().get(params[0]);
}
protected void onPostExecute(String response) {
//Do UI updates...
textview.setText(response);
}
}
}
public class GetHttpResponse {
public String get(String url) {
try {
util = new HttpUtils(url);
response = util.getContent();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return response;
}
}
You could do something like this:
public class MyActivity extends Activity
{
public void someMethod()
{
// Here you could put up a ProgressDialog
GetUrlDataTask myTask = new GetUrlDataTask();
myTask.execute();
}
public class GetUrlDataTask extends AsyncTask<String, Integer, String>
{
#Override
protected String doInBackground(String... params)
{
String response = null;
HttpUtils util;
try
{
util = new HttpUtils(params[0]);
response = util.getContent();
}
catch (Exception e)
{
e.printStackTrace();
response = e.getMessage();
}
return response;
}
#Override
protected void onPostExecute(String result)
{
// Here you can dismiss the ProgressDialog and display the result
}
}
}
I have an android app that I am having trouble with.
Basically the ProgressDialog is not showing at all. I believe this to be a threading issue of some sort but I don't know how to fix it.
I am using ActionBarSherlock with some Fragments. I am also using the new Android DrawerLayout where I have my options on the drawer, which replace a fragment when clicked.
On first load of my app, I want to check the database to see if the inital data has been downloaded. If not, then I go off and begin an AsyncTask to download the data. This SHOULD have a ProgressDialog display during this, but it doesnt.
Can someone see where I am going wrong? Thanks.
MainScreen - The default landing page/fragment when the app opens
public class MainScreen extends SherlockFragment {
public static final String TAG = "MainScreen";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_main, container, false);
setHasOptionsMenu(false);
ImageView imgLogo = (ImageView) rootView.findViewById(R.id.imgMainScreen);
imgLogo.setOnClickListener(new ButtonHandler(getActivity()));
checkDatabase();
return rootView;
}
private void checkDatabase() {
//Ensure there is data in the database
DBHelper db = new DBHelper(this.getSherlockActivity());
db.checkDatabase();
}
...
}
DBHelper.checkDatabase() - The method that initiates the download
public void checkDatabase() {
if (isEmpty()) {
//Connect to net and download data
NetworkManager nm = new NetworkManager(activity);
if (!nm.downloadData()) {
Toast.makeText(activity, R.string.internetCheck, Toast.LENGTH_SHORT).show();
}
}
}
and finally
NetworkManager.downloadData() - The method that kicks off the AsyncTask:
public boolean downloadData() {
try {
return new HttpConnection(activity).execute().get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return false;
}
public class HttpConnection extends AsyncTask<Void, Void, Boolean> {
private ProgressDialog progressDialog;
private Activity m_activity;
protected HttpConnection(Activity activity) {
m_activity = activity;
}
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(m_activity);
progressDialog.setMessage("Wait ...");
progressDialog.setCancelable(false);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... params) {
String[] types = new String[]{"type1", "type2", "type3", "type4", };
StringBuilder sb = new StringBuilder();
for(String type : types) {
sb = new StringBuilder();
if(DBHelper.TYPE4_TABLE.equals(type)) {
InputStream is = activity.getResources().openRawResource(R.raw.dbdata);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
try {
sb.append(reader.readLine());
} catch (IOException e) {
Toast.makeText(activity.getApplicationContext(), "Error retriveving data", Toast.LENGTH_SHORT).show();
Log.e(Constants.TAG, "Error reading data");
e.printStackTrace();
}
} else {
sb = fetchURLData(Constants.ALL_URL+type);
}
cleanDataAndStore(sb, type);
}
return true;
}
#Override
protected void onPostExecute(Boolean result){
progressDialog.hide();
}
}
Using the above code, all I get is a white screen as the app tries to load, and sometimes an ANR. When the download is done, the fragment loads. So it works fine except for the missing ProgressDialog.
PS, Notice I'm setting the activity in each constructor.
Thanks.
Remove .get() from return new HttpConnection(activity).execute().get(); You are basically locking your UI thread. Once removed it should work as AsyncTasks are expected to work.
The purpose is to be Asynchronous so boolean downloadData() should have a return type of void. If you need to do something with the data then you should implement an interface "listener" and pass it to the AsyncTask.
Example Listener:
class TaskConnect extends AsyncTask<Void, Void, ConnectionResponse> {
private final AsyncTaskListener mListener;
/**
*
*/
public TaskConnect(AsyncTaskListener listener) {
...
mListener = listener;
}
#Override
protected void onPreExecute() {
if (mListener != null) {
mListener.onPreExecute(mId);
}
}
#Override
protected ConnectionResponse doInBackground(Void... cData) {
...
return responseData;
}
#Override
protected void onPostExecute(ConnectionResponse response) {
if (mListener != null) {
mListener.onComplete(response);
} else {
LOG.w("No AsyncTaskListener!", new Throwable());
}
}
}
public interface AsyncTaskListener {
public abstract void onPreExecute(int id);
public abstract void onComplete(ConnectionResponse response);
}
My issue was not the common issue of others where they were calling get() method after execute() method. My issue was the Context I was passing to my AsyncTask method. I have a settingsActivity and I have a ReadMeActivity that calls the asynctask task. Instead of using the context in which is was being called (ReadMeActivity.this) I used the settingsActivity which prevented it from being seen. Once I switched it and passed it the context in which the activity was being called it worked.
Hope it helps someone else.