I have reviewed all of the documentation for CWAC-endlessAdapteras well as the demo projects. I do understand how it works and mostly where everything goes. But I have a several questions on how to handle some things with how I am currently doing it now (I have yet to find any working example of this).
Here is a typical AsyncTask I use (cleaned up a bit):
class ReviewTask extends AsyncTask<String, String, Void> {
#Override
protected Void doInBackground(String... params) {
ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();
param.add(new BasicNameValuePair("username", userName));
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url_select);
try {
httpPost.setEntity(new UrlEncodedFormEntity(param));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
} catch (Exception e) {
}
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result = sb.toString();
} catch (Exception e) {
}
return null;
}
protected void onPostExecute(Void v) {
String review, newdate, item, rating, cat;
try {
JSONArray jArray = new JSONArray(result);
JSONObject json_data = null;
for (int i = 0; i < jArray.length(); i++) {
json_data = jArray.getJSONObject(i);
newdate = json_data.getString("date");
review = json_data.getString("review");
item = json_data.getString("item");
rating = json_data.getString("rating");
cat = json_data.getString("category");
reviews.add(review);
itemslist.add(item);
datelist.add(newdate);
ratings.add(rating);
cats.add(cat);
}
}
Profile[] p = new Profile[reviews.size()];
int index = 0;
for (String i : reviews) {
p[index] = new Profile(reviews.get(index), datelist.get(index),
itemslist.get(index), ratings.get(index),
cats.get(index));
index++;
}
if (getActivity() != null) {
adapter = new ProfileAdapter(getActivity(), p);
setListAdapter(adapter);
}
}
}
In this task, I get all the data from MySQL Database via php. The SQL query I wrote gathers all data at once. Is this correct to still handle this way?
Also, I call this task in the onCreateView in my ListFragment. But it looks like the task needs to be called in cacheInBackground()?
Lastly, it sounds like I have to set the adapter in onActivityCreated like this:
// from Example
if (adapter==null) {
items=new ArrayList<Integer>();
for (int i=0;i<25;i++) { items.add(i); }
adapter=new DemoAdapter(items);
adapter.setRunInBackground(false);
}
setListAdapter(adapter);
I don't understand or see where there is a constructor for DemoAdapter(items), and based on the fact that I am passing an Array of Objects, would I do something like DemoAdapter(object[])? And it's ok if it is null, because gathering the data actually happens in the adapter, correct?
Last relevant note is, all of my adapters are in a class outside of the Fragment where they are set.
The SQL query I wrote gathers all data at once. Is this correct to still handle this way?
That is up to you. However, doing it this way, you do not need EndlessAdapter, as you already have all your data. The point behind EndlessAdapter is to support situations where you do not "gather all data at once", but rather wish to gather a portion of the data, and gather another portion only when the user scrolls far enough.
Also, I call this task in the onCreateView in my ListFragment. But it looks like the task needs to be called in cacheInBackground()?
That is up to you. If you wish to use your own AsyncTask called whenever you want, that is fine. This is covered in the documentation:
If you would prefer that EndlessAdapter not run its own AsyncTask, then call setRunInBackground(false). In this mode, your cacheInBackground() method will be called on the main application thread. It is up to you to arrange to do the work on your own background thread, then call onDataReady() when you want the adapter to update to reflect the newly added data. Note that appendCachedData() will not be used in this scenario.
I don't understand or see where there is a constructor for DemoAdapter(items)
There isn't one, as the demos did not require one.
based on the fact that I am passing an Array of Objects, would I do something like DemoAdapter(object[])?
That is up to you.
And it's ok if it is null, because gathering the data actually happens in the adapter, correct?
Again, that is up to you.
However, as I pointed out earlier, since you do not need EndlessAdapter, I would recommend that you just stop using it.
Related
I'm doing a REST API in Android with my GET method using Slim framework. I didn't have any problem about retrieving all the values of a table, for example, cars.
The problem comes when I try to access to the name of a Car by it's id. I have the method on Slim framework created and it works perfectly:
GET METHOD
$app->get("/cars/:idCar",function($idCar) use($app)
{
try{
$connection = getConnection();
$dbh = $connection->prepare("SELECT name FROM cars WHERE idCar = ?");
$dbh->bindParam(1,$idCar);
$dbh->execute();
$car = $dbh->fetchObject();
$connection = null;
header("HTTP/1.1 200");
header("Content-Type:application/json; charset=utf-8");
echo json_encode($car,JSON_UNESCAPED_UNICODE );
}catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
});
And if I put the url on my browser:
http://IP of my computer/project/cars/1
it returns to me:
{"name":"Car1"}
So in the GET method there isn't any problem.
The problem it's when I try to execute HttpGet, because I get stuck of what I have to do. What I have right now:
HttpGet method inside the AsyncTask
class FindCar extends AsyncTask<Integer, Integer, Void> {
protected void onPreExecute(){
}
protected Void doInBackground(Integer... idCar) {
String url = "http://IP of my computer/project/cars/" + idCar[0].intValue();
HttpClient httpClient = new DefaultHttpClient();
HttpGet method = new HttpGet(url);
method.setHeader("content-type", "application/json");
try {
HttpResponse resp = httpClient.execute(method);
String respStr = EntityUtils.toString(resp.getEntity());
JSONArray respJSON = new JSONArray(respStr);
for (int i = 0; i < respJSON.length(); i++) {
JSONObject obj = respJSON.getJSONObject(i);
String name = obj.getString("name");
}
} catch (Exception ex) {
}
return null;
}
protected void onProgressUpdate(){
}
protected void onPostExecute(){
}
}
And it gives to me some questions:
Should I create a JSONArray just for one attribute?
What is the best practice to get the name in my AsyncTask?
After getting the name by the id of my Car that I pass to my AsyncTask, how can I return it out of the AsyncTask?
Should I create a NameValuePair to set the value of my id?
Thanks in advance!
You do not need the JSONArray (since there is no square brackets in the responce)! Please try instead of
JSONArray respJSON = new JSONArray(respStr);
for (int i = 0; i < respJSON.length(); i++) {
JSONObject obj = respJSON.getJSONObject(i);
String name = obj.getString("name");
}
the following:
JSONObject respJSON = new JSONObject(respStr);
String name = respJSON.getString("name");
As for returning the result - return it from doInBackground to onPostExecute method then store it somewhere (you can show it on the screen since onPostExecute runs on UI thread). To return value from doInBackground change last type param of AsyncTask<Integer, Integer, Void> from Void to other type.
Best practice is do not use AsyncTask and do not parse JSON manually (when it is possible) :) Retrofit would be a good choice
i use something like this:
public function getCarsById($id) {
$stmt = $this->conn->prepare("SELECT name FROM cars WHERE idCar = ?");
$stmt->bind_param("s", $id);
if ($stmt->execute()) {
$car= $stmt->get_result()->fetch_assoc();
$stmt->close();
return $car;
} else {
return NULL;
}
}
Regarding the java class, i use Retrofi that is incredible fast and can be used Synchronous and Asynchronous.
Hope you are doing well.
I'm developing an android app, and i need to make synchronization between the local SQL and the database on the server.
I'm using PHP on the server side(JSON for retrieving date).
My problem is when i launch the app for the first time , is dowloads the data located on the server, then when i launch it for the second time , the data is dowloaded again, sometimes it happens and sometimes NOT!
And when adding new data , it is downloaded , but the problem still happens, the data is duplicated.
When i try the app on my mobile, the data is downloaded once, but when adding new data, it isn't downloaded!
Here is my code.
protected Void doInBackground(Void... uri) {
myDatabaseHandler.openToRead();
Cursor cursor = myDatabaseHandler.queueBranchId();
try {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, 0);
HttpClient httpClient = new DefaultHttpClient(params);
//prepare the HTTP GET call
HttpGet httpget = new HttpGet("http://restaurants.bugs3.com/branches.php");
//get the response entity
HttpEntity entity = httpClient.execute(httpget).getEntity();
if (entity != null) {
//get the response content as a string
String response = EntityUtils.toString(entity);
//consume the entity
entity.consumeContent();
// When HttpClient instance is no longer needed, shut down the connection manager to ensure immediate deallocation of all system resources
httpClient.getConnectionManager().shutdown();
//return the JSON response
JSONObject object = new JSONObject(response.trim());
JSONArray jsonArray = object.getJSONArray("branches");
if(jsonArray != null) {
for(int i = 0 ; i < jsonArray.length() ; i++) {
JSONObject object1 = (JSONObject) jsonArray.get(i);
int server_branch_id = object1.getInt("server_branch_id");
String branch_name = object1.getString("branch_name");
double branch_lat = object1.getDouble("branch_lat");
double branch_lon = object1.getDouble("branch_lon");
double distance = object1.getDouble("distance");
String restaurant_name = object1.getString("restaurant_name");
boolean exist = false;
if (cursor.moveToFirst())
{
do
{
try
{
if (server_branch_id == cursor.getInt(cursor.getColumnIndex(DatabaseHandler.KEY_LOCAL_BRANCH_ID)))
{
exist = true;
}
}
catch(Exception e)
{
}
}while(cursor.moveToNext());
cursor.close();
}
myDatabaseHandler.close();
if (!exist)
{
myDatabaseHandler.openToWrite();
myDatabaseHandler.insertBranchWithID(server_branch_id, branch_name, restaurant_name, branch_lat, branch_lon, distance);
myDatabaseHandler.close();
}
}}catch (Exception e) {
e.printStackTrace();
}
return null;
}
There could be some workarounds for this. I can suggest two different ways (Assuming you have a specific id for whatever item you download):
Keep track of last id at local sql, on app start, ask server if there is newer id and if there is download them. By this way you can avoid replication.
Use primary key constraint on table and for insertion use insertWithOnConflict(..., CONFLICT_IGNORE). This method will not duplicate any same ids and insert if there is something different.
Hope this helps
In my application, I have a food activity in which the user enters his/her food, and the app requests the food, by the name entered by the user, from a MYSQL database. In the case that the entered food not exist, the string returned by the database should be null.
Currently, when this happens, an exception to occurs since the null value cannot be parsed to a JSON array. My question is: "Is there a way to prevent my app from force closing? Can I handle the exception and display a toast notifying the user that the requested food was not found?" I would like to prevent the app from crashing, and, rather, fail gracefully.
Please help me.
I've shown the relevant code in my application..
private class LoadData extends AsyncTask<Void, Void, String>
{
private JSONArray jArray;
private String result = null;
private InputStream is = null;
private String entered_food_name=choice.getText().toString().trim();
protected void onPreExecute()
{
}
#Override
protected String doInBackground(Void... params)
{
try {
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://10.0.2.2/food.php");
nameValuePairs.add(new BasicNameValuePair("Name",entered_food_name));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs,"UTF-8"));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
//convert response to string
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"utf-8"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
is.close();
result =sb.toString();
result = result.replace('\"', '\'').trim();
}
catch(Exception e){
Log.e("log_tag", " connection" + e.toString());
}
return result;
}
#Override
protected void onPostExecute(String result)
{
try{
String foodName="";
int Description=0;
jArray = new JSONArray(result); // here if the result is null an exeption will occur
JSONObject json_data = null;
for (int i = 0; i < jArray.length(); i++) {
json_data = jArray.getJSONObject(i);
foodName=json_data.getString("Name");
.
.
.
.
.
}
catch(JSONException e){
**// what i can do here to prevent my app from crash and
// make toast " the entered food isnot available " ????**
Log.e("log_tag", "parssing error " + e.toString());
}
}
}
This will fix your code:
jArray = (result == null) ? new JSONArray() : new JSONArray(result);
Now that you have an empty JSONArray, you will be able to test for null JSONObjects later in your program. Many of the JSON methods return a JSONObject if one is found, of null if none exists.
You might also want to initialize your JSONObject with the no-argument JSON constructor, rather than simply setting it to null. It will avoid problems when passing it to other JSON methods (such as using it in a constructor to a JSONArray():
JSONObject json_data = new JSONObject();
Finally, if you're still getting JSONExceptions, it's because you're not actually passing a valid JSON string to the constructor. You can print out the value of result to the log:
Log.d("JSON Data", result);
You may see some SQL error text or if you retrieve from a web server, then an HTTP error code (404 is common if you don't have your url correct).
If your result does look like JSON, then you can verify whether it's actually valid JSON or not using the JSONLint validator. It will help you catch any errors you may have, especially if you're formatting the JSON yourself.
Are you looking to capture the Exception and log it (remotely) to aid in crash reporting and debugging? I've used this package to remotely capture Exceptions and it works pretty good:
http://code.google.com/p/android-remote-stacktrace/
I am relatively a new Android developer and I am not able to understand how to do this. I have been looking through all the forums, I made some advance but still here I am.
So, what I want to do is a common function that send a POST request to a webpage (it only sends one POST argument) and returns the result as a string.
I have the main thread here
public class AppActivity extends Activity {
HTTPPostData PostData = new HTTPPostData("id");
PostData.execute();
txtLabel.setText(PostData.Result);
}
and I have my HTTPPostData asynchronous class
public class HTTPPostData extends AsyncTask<String, Long, Object> {
String Value = null;
String Result = null;
public HTTPPostData(String query) {
Value = query;
}
#Override
protected String doInBackground(String... params) {
byte[] Bresult = null;
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://www.mypage.com/script.php");
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
nameValuePairs.add(new BasicNameValuePair("cmd", Value));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
HttpResponse response = client.execute(post);
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpURLConnection.HTTP_OK){
Bresult = EntityUtils.toByteArray(response.getEntity());
Result = new String(Bresult, "UTF-8");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception e) {
}
return Result;
}
}
I want to use this function several times (inside the same Activity or share it with other Activities of the same application). I am a little bit messed up at this moment so I need your help. What I understand is that I am asking for the result before the doInBackground() is done, and I get an empty result.
Thanks in advance for your help
Regarding this:
HTTPPostData PostData = new HTTPPostData("id");
PostData.execute();
txtLabel.setText(PostData.Result);
Your problem is that you're treating asynctask like it's just a regular function. It's good that you move webpage loading off the main thread, but if you depend on the result for the very next instruction, then it's not doing you much good, you're still blocking the main program waiting for the result. You need to think of AsyncTask like a 'fire and forget' operation, in which you don't know when, if ever, it will come back.
The better thing to do here would be something like:
HTTPPostData PostData = new HTTPPostData("id");
PostData.execute();
txtLabel.setText("Loading...");
and then in the asynctask:
protected void onPostExecute(String result) {
txtLabel.setText(result);
}
This lets your main thread get on with doing it's business without knowing the result of the asynctask, and then as soon as the data is available the asynctask will populate the text label with the result.
I write a client for web service. Like, did request -> got JSON answer.
But if user not authorized I got "bad" JSON answer and NullPointerException. Further, the logic is create ListAdapter for a ListView from JSON answer. But if JSON null then app will crash.
I want do like this for handle exception:
Get Adapter:
public mListAdapter getData(String api){
mListAdapter result = null;
MyData[][] mData = null;
try{
Gson gson = new Gson();
Reader r = new InputStreamReader(getJSONData(api));
// getJSONData, here i can get Exception and return null, but i read what control of logic via exception is bad practice
}catch(Exception ex){
ex.printStackTrace();
Log.i("NullPointerException", "error");
return result;
}
result = new mListAdapter(title, mData);
return result;
}
getJSONData:
public InputStream getJSONData(String url) throws NullPointerException{
InputStream data = null;
try {
HttpGet httpget = new HttpGet(url);
HttpResponse response = httpclient.execute(httpget);
data = response.getEntity().getContent();
//if (response.getEntity().getContent().getStatus()==404) return null, for example
} catch (Exception e) {
e.printStackTrace();
return null;
}
return data;
}
And set Adapter to ListView:
if (adapter!=null) IWatchList.setAdapter(adapter); else IWatchList.setEmptyView(new TextView(this));
I don't like this method, because:
- I came up it.
- First two methods universal, therefore each adapter need describe like third method, this is not comfortably. On the other hand how else to define the adapter is empty or not! Only if check it.
Please, your pros and cons, proposal and tips how better and why.