0

I am building a registration page for the users of my Android app. The UI asks for the mobile no., age and username. On entering these details, I want to send this data to my Apache server where I have written a PHP script to handle the received data. I have used JSON to send the data from the app but I am unable to receive the data at my server on running the script. It gives the error "Undefined index: json" on the lines in the script wherever i have written json

On running the app on my android-based mobile, it accepts the details entered and shows them in the UI for registration as toasts. I have used toasts to verify that the data is being actually captured by the app or not. I feel there is a problem with the PHP script as the app doesn't give any runtime exceptions as well. It runs fine.

I have searched all over the net but haven't found a solution to this.

The java code for the registration page is as follows:

package com.example.registertest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
public class Register extends Activity {

TextView tv;
String text;
private static final String TAG = "MyActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_register);

    Intent intent = getIntent();
    //String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
    Bundle b= this.getIntent().getExtras();
    String username=b.getString("username");
    String contact=b.getString("contact");
    String ag=b.getString("age");
    int con=Integer.parseInt(contact);
    int age=Integer.parseInt(ag);
    Toast.makeText(getApplicationContext(), username,
               Toast.LENGTH_LONG).show();
    Toast.makeText(getApplicationContext(), contact,
               Toast.LENGTH_SHORT).show();
    Toast.makeText(getApplicationContext(), ag,
               Toast.LENGTH_SHORT).show();

    JSONObject json=new JSONObject();
    try {
        json.put("username", username);
        json.put("contact", con);
        json.put("age", age);
        postData(json);


    } catch (JSONException e) {
        e.printStackTrace();
    }
}


public void postData(JSONObject json) throws JSONException {
    HttpClient httpclient = new DefaultHttpClient();
    String url = "http://My_I.P./scriptname.php";

    try { 
        HttpPost httppost = new HttpPost(url);

       List<NameValuePair> nvp = new ArrayList<NameValuePair>(2);    
        nvp.add(new BasicNameValuePair("json", json.toString()));
        httppost.setHeader("Content-type", "application/json");  
       httppost.setEntity(new UrlEncodedFormEntity(nvp));
       HttpResponse response = httpclient.execute(httppost); 

        if(response != null) {
           InputStream is = response.getEntity().getContent();
           // input stream is response that can be shown back on android
     }

    }catch (Exception e) {
        e.printStackTrace();
    } 


} 



@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.register, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}
}

The PHP script is as follows:

<?php
$jsonstring = urldecode($_REQUEST['json']);
$jsonstring = $_REQUEST['json'];
$data = json_decode($json);
$data = json_decode($json,true);
if( $data === NULL)
{
exit( 'Could not decode JSON');
}
echo "Array: \n";
echo "--------------\n";
var_dump($data);
echo "\n\n";

$name = $data->username;
$con = $data->contact;
echo "Result: \n";
echo "--------------\n";
echo "Name     : ".$name."\n Position : ".$con;
?>
Terrence88
  • 11
  • 1
  • 4
  • `echo $_REQUEST['json']` and `var_dump($data);` You're also overwriting your `$data` object with an associative array in the next line. – AbraCadaver Apr 01 '15 at 18:31
  • 3
    NETWORKONMAINTHREADEXCEPTION – A.S. Apr 01 '15 at 18:34
  • don't urldecode the superglobals. PHP does that for you already. You risk trashing your string by double-decoding it. And have you done any basic debugging, like `var_dump($_REQUEST)` to see what you're really receiving? – Marc B Apr 01 '15 at 18:36
  • I did 'var_dump($jsonstring);'. It shows NULL and also removed the urldecode line – Terrence88 Apr 01 '15 at 19:02

2 Answers2

1

As @A.S. pointed out, you're doing a network operation on the main thread.

See this documentation: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

Use an AsyncTask and do the network operation in the doInBackground() method. There is a good example here you can use for reference: android http post asynctask

In the PHP code, in addition to what @AbraCadaver and @MarcB pointed out, it looks like there is another problem as well.

You're referencing$json instead of $jsonstring.

Good practice is to use isset, see this post: Undefined index: Error in php script

<?php

if(isset($_REQUEST['json'])){

  $jsonstring = $_REQUEST['json'];
  $data = json_decode($jsonstring );
  $dataArray = json_decode($jsonstring ,true);

  if( $data === NULL)
  {
    exit( 'Could not decode JSON');
  }
  echo "Array: \n";
  echo "--------------\n";
  var_dump($dataArray);
  echo "\n\n";

  $name = $data->username;
  $con = $data->contact;
  echo "Result: \n";
  echo "--------------\n";
  echo "Name     : ".$name."\n Position : ".$con;
}
else{
  exit( 'Could not read JSON');
}
?>

Edit: Your Activity should look something like this:

public class Register extends Activity {

TextView tv;
String text;
private static final String TAG = "MyActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_register);

      Intent intent = getIntent();
      //String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
      Bundle b= this.getIntent().getExtras();
      String username=b.getString("username");
      String contact=b.getString("contact");
      String ag=b.getString("age");
      int con=Integer.parseInt(contact);
      int age=Integer.parseInt(ag);
      Toast.makeText(getApplicationContext(), username,
                 Toast.LENGTH_LONG).show();
      Toast.makeText(getApplicationContext(), contact,
                 Toast.LENGTH_SHORT).show();
      Toast.makeText(getApplicationContext(), ag,
                 Toast.LENGTH_SHORT).show();

      //Start AsyncTask
      new AsyncHttpPost().execute(username, contact, ag);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.register, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    public class AsyncHttpPost extends AsyncTask<String, String, String> {


      @Override
      protected String doInBackground(String... params) {


        JSONObject json=new JSONObject();
        try {
            json.put("username", params[0]);
            json.put("contact", Integer.parseInt(params[1]));
            json.put("age", Integer.parseInt(params[2]));
            //postData(json);


        } catch (JSONException e) {
            e.printStackTrace();
        }

        HttpClient httpclient = new DefaultHttpClient();
        String url = "http://My_I.P./scriptname.php";

        try { 
            HttpPost httppost = new HttpPost(url);

            List<NameValuePair> nvp = new ArrayList<NameValuePair>();    
            nvp.add(new BasicNameValuePair("json", json.toString()));
            httppost.setHeader("Content-type", "application/json");  
            httppost.setEntity(new UrlEncodedFormEntity(nvp));
            HttpResponse response = httpclient.execute(httppost); 

            if(response != null) {
               StringBuilder sb = new StringBuilder();
               BufferedReader reader = new BufferedReader(new InputStreamReader( response.getEntity().getContent() ), 65728);
               String line = null;

               while ((line = reader.readLine()) != null) {
                 sb.append(line);
               }
               return sb.toString();

           }

        }catch (Exception e) {
            e.printStackTrace();
        } 

        return null;
      }


      @Override
      protected void onPostExecute(String result) {
        if (result != null){
          // read result...
        }
      }
  }

}

Edit 2: Since this wasn't working, I decided to investigate. I was able to get it working for a simple example.

Here is the modified AsyncTask using a StringEntity instead of a BasicNameValuePair:

public class AsyncHttpPost extends AsyncTask<String, String, String> {


    @Override
    protected String doInBackground(String... params) {


        JSONObject json=new JSONObject();
        try {
            json.put("username", params[0]);
            json.put("contact", Integer.parseInt(params[1]));
            json.put("age", Integer.parseInt(params[2]));
            //postData(json);

            Log.d("json", json.toString());

        } catch (JSONException e) {
            e.printStackTrace();
        }


        HttpClient httpclient = new DefaultHttpClient();
        String url = "http://www.example.com/DevTest/php/test.php";

        try {
            HttpPost httppost = new HttpPost(url);

            //List<NameValuePair> nvp = new ArrayList<NameValuePair>();
            //nvp.add(new BasicNameValuePair("json", json.toString()));
            httppost.setHeader("Content-type", "application/json");
            //httppost.setEntity(new UrlEncodedFormEntity(nvp));
            httppost.setEntity(new StringEntity(json.toString()));
            HttpResponse response = httpclient.execute(httppost);

            if(response != null) {
                StringBuilder sb = new StringBuilder();
                BufferedReader reader = new BufferedReader(new InputStreamReader( response.getEntity().getContent() ), 65728);
                String line = null;

                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                return sb.toString();

            }

        }catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


    @Override
    protected void onPostExecute(String result) {
        if (result != null){
            Toast.makeText(getApplicationContext(), result,
                    Toast.LENGTH_SHORT).show();
            Log.d(TAG, result);
        }

    }
}

Modified PHP code:

<?php

$json = file_get_contents('php://input');

var_dump($json);

$obj = json_decode($json, TRUE);

var_dump($obj);


echo "\n username \n";

echo $obj['username'];

echo "\n contact \n";

echo $obj['contact'];

echo "\n age \n";

echo $obj['age']; 

?>

AndroidManifest.xml:

 <uses-permission android:name="android.permission.INTERNET" />

References: Correct way to POST JSON data from Android to PHP

PHP "php://input" vs $_POST

Community
  • 1
  • 1
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • I edited those lines. You were right. I was wrongly referencing json instead of jsonstring. But this isn't solving the problem sadly. I ran the script you posted. It shows 'could not read JSON'. – Terrence88 Apr 01 '15 at 19:17
  • i added the line: var_dump($_REQUEST['json']); as the first line of the script. It shows the following error: Undefined index: json. Then it displays NULL which is the output of vardump and then the exit statement 'Could not read JSON' – Terrence88 Apr 01 '15 at 19:17
  • @Terrence88, your main issue is that your network operation in the Java code needs to run in a separate thread. Use an `AsyncTask` to do the network operation in a background thread. – Daniel Nugent Apr 01 '15 at 19:19
  • I'll give it a try. But I don't really feel it should affect the outcome of the php script – Terrence88 Apr 01 '15 at 19:24
  • @Terrence88 I think your PHP script is ok now, at this point the error is that your Java code is not successfully sending the JSON to the script, as it's probably throwing an exception. I'll update the answer again. – Daniel Nugent Apr 01 '15 at 19:26
  • I'm using Eclipse IDE but couldn't find any exception in the logcat. Okay do update your answer. Thanks for all the effort! – Terrence88 Apr 01 '15 at 19:31
  • @Terrence88 Take a look at the latest edit. I was able to get it working for a simple example. The key was to use a `StringEntity` to send the JSON data to the PHP. – Daniel Nugent Apr 02 '15 at 03:24
  • I modified both AsyncHttpPost class as well as the PHP script. On running the PHP script, it throws this: string(0) "" NULL username contact age – Terrence88 Apr 02 '15 at 05:06
  • What is 'php://input' in the php script supposed to mean? – Terrence88 Apr 02 '15 at 05:08
  • @Terrence88, it looks like for some reason the JSON isn't reaching the PHP page for you. Just to be sure, try it on the PHP page that I set up and got working: http://dnugent.nicewebsite.info/DevTest/php/test.php – Daniel Nugent Apr 02 '15 at 05:17
  • I copied the your link as 'url' string in AsyncHttpPost class. On entering the details in the app and on running the script: dnugent.nicewebsite.info/DevTest/php/test.php, I get the same outcome: string(0) "" NULL username contact age – Terrence88 Apr 02 '15 at 05:24
  • Very strange. I got output: `string(43) "{"contact":1,"username":"daniel","age":234}"array(3) { ["contact"]=> int(1) ["username"]=> string(6) "daniel" ["age"]=> int(234)} username daniel contact 1 age 234` – Daniel Nugent Apr 02 '15 at 05:26
  • @Terrence88, make sure that you got all the changes to the Java code. Also make sure that your AndroidManifest.xml has `android.permission.INTERNET`. – Daniel Nugent Apr 02 '15 at 05:27
  • Yes. I have added that. I hope i don't have to add any other permissions – Terrence88 Apr 02 '15 at 05:38
  • @Terrence88 that should be all you need. Try loging `json.toString()` in the Java code before it sends to the PHP page. I just did, it looked like this: `{"contact":1,"username":"daniel","age":234}` – Daniel Nugent Apr 02 '15 at 05:40
  • You got that output for the same PHP script you have posted above? – Terrence88 Apr 02 '15 at 05:42
  • @Terrence88 Yep, I got this output from the link I gave you. Here is the full log line where I logged it in the onPostExecute() function: `04-01 22:37:30.963 14581-14581/com.jsonphppractice.daniel.jsontest D/MyActivity﹕ string(43) "{"contact":1,"username":"daniel","age":234}"array(3) { ["contact"]=> int(1) ["username"]=> string(6) "daniel" ["age"]=> int(234)} username daniel contact 1 age 234` – Daniel Nugent Apr 02 '15 at 05:44
  • I tried logging json.toString(), it looks like this: {"username": "smit","contact":"78","age":"22"}. Same as yours. So I feel, upto this point, the code's working properly – Terrence88 Apr 02 '15 at 06:02
  • @Terrence88 That looks good. Very strange that it's not working for you! I just tried it with those values, and got a valid response back from the PHP: `string(41) "{"contact":78,"username":"smit","age":22}"array(3) { ["contact"]=> int(78) ["username"]=> string(4) "smit" ["age"]=> int(22)} username smit contact 78 age 22` – Daniel Nugent Apr 02 '15 at 06:08
  • @Terrence88 Are you building the JSONObject inside the doInBackground() function? – Daniel Nugent Apr 02 '15 at 06:13
  • I have edited my answer and added the warning that i get in the onPostExecute method below. Do have a look – Terrence88 Apr 02 '15 at 06:22
0

I added the AyncTask class named Dummy. You can have a look at the code below.

package com.example.registertest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
public class Register extends Activity {

TextView tv;
String text;
private static final String TAG = "MyActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_register);

    Intent intent = getIntent();
    //String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
    Bundle b= this.getIntent().getExtras();
    String username=b.getString("username");
    String contact=b.getString("contact");
    String ag=b.getString("age");
    int con=Integer.parseInt(contact);
    int age=Integer.parseInt(ag);
    Toast.makeText(getApplicationContext(), username,
               Toast.LENGTH_LONG).show();
    Toast.makeText(getApplicationContext(), contact,
               Toast.LENGTH_SHORT).show();
    Toast.makeText(getApplicationContext(), ag,
               Toast.LENGTH_SHORT).show();

    JSONObject json=new JSONObject();
    try {
        json.put("username", username);
        json.put("contact", con);
        json.put("age", age);
        //postData(json);
        new Dummy().execute(json);
  } catch (JSONException e) {
        e.printStackTrace();
    }
}
private class Dummy extends AsyncTask<JSONObject, Void, Void> {


    @Override
    protected Void doInBackground(JSONObject... json) {
        // TODO Auto-generated method stub
        HttpClient httpclient = new DefaultHttpClient();
        String url = "http://My_I.P./ScriptName.php";

        try { 
            HttpPost httppost = new HttpPost(url);

           List<NameValuePair> nvp = new ArrayList<NameValuePair>(2);    
            nvp.add(new BasicNameValuePair("json", json.toString()));
            httppost.setHeader("Content-type", "application/json");  
           httppost.setEntity(new UrlEncodedFormEntity(nvp));
           HttpResponse response = httpclient.execute(httppost); 

            if(response != null) {
               InputStream is = response.getEntity().getContent();
               // input stream is response that can be shown back on android
         }

        }catch (Exception e) {
            e.printStackTrace();
        } 
        return null;
    }

}



@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.register, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}
}

Edit:

This is warning given in the Toast of onPostExecute method:

<br /><b>Deprecated</b>:  Automatically populating $HTTP_RAW_POST_DATA  is deprecated and will be removed in a future version. To avoid this warning set 'always_populate_raw_post_data' to '-1' in php.ini and use the php://input stream instead. in <b>Unknown</b> on line <b>0</b><br /><br /><b>Warning</b>:  Cannot modify header information - headers already sent in <b>Unknown</b> on line <b>0</b><br />string(44) "{"username":"chinmay","contact":89,"age":22}"array(3) {  ["username"]=>  string(7) "chinmay"  ["contact"]=>  int(89)  ["age"]=>  int(22)} username chinmay contact 89 age 22
Terrence88
  • 11
  • 1
  • 4
  • looks good, but you won't be able to read the `InputStream` in your main thread. Take a look at my updated answer. – Daniel Nugent Apr 01 '15 at 20:21
  • The return type InputStream for doInBackground method is throwing an error that the return type is incompatible. This is true as doInBackground method must return a String as String is the parameter that will be passed to onPostExecute(). Refer 7.2 on this link: http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html – Terrence88 Apr 01 '15 at 20:51
  • Sorry, that was left over from my initial code. I updated the code in the answer again, take a look. – Daniel Nugent Apr 01 '15 at 20:55
  • You are returning a string 'return sb.toString();' while the return type is InputStream. Hence its giving the error: cannot convert string to InputStream – Terrence88 Apr 01 '15 at 21:01
  • Yeah, I had initially coded it that way, but then I changed it. Take a look at the updated code, changed to: `protected String doInBackground(String... params)`. – Daniel Nugent Apr 01 '15 at 21:04
  • Done with that too! Still the same outcome of the php script. Just asking, i have to check the output at the same URL as the one in the java code right? – Terrence88 Apr 01 '15 at 21:14
  • Are you getting `'Could not read JSON'` back from the server in your code? – Daniel Nugent Apr 01 '15 at 21:21
  • Yes. Along with that i get undefind index: json and NULL for var_dump($_REQUEST['json']); – Terrence88 Apr 01 '15 at 21:24
  • Hmm, not sure what's going on. You may want to try just sending the data in separate POST parameters instead of JSON. – Daniel Nugent Apr 01 '15 at 21:27
  • yes yes. Its in the condition of the if-else loop, which is always false, hence its printing 'could not read JSON' – Terrence88 Apr 01 '15 at 21:32