Reputation: 18649
I have an Android app that makes a server call which returns JSON.
The server code returns the right string if I enter the URL into a browser. But it creates exceptions in the Java app (different issues with http and https server calls).
https://www.problemio.com/auth/[email protected]&password=130989
Returns this string:
[{"user_id":"1601470","email":"[email protected]","first_name":"TestName","last_name":null}]
And this is the Java call that parses the JSON but gives an Exception:
@Override
protected void onPostExecute(String result)
{
try {
dialog.dismiss();
} catch (Exception ee) {
// nothing
}
if ( connectionError == true )
{
Toast.makeText(getApplicationContext(), "Please try again. Possible Internet connection error.", Toast.LENGTH_LONG).show();
}
if ( result != null && result.equals( "no_such_user") )
{
Toast.makeText(getApplicationContext(), "Your email and password do not match out records. Please try again or create and account.", Toast.LENGTH_LONG).show();
//final TextView login_error = (TextView) findViewById(R.id.login_error);
}
else
{
Log.d( "CONNECTION*** ERRORS: " , "ok 3 and result length: " + result.length() );
String firstName = null;
String lastName = null;
String email = null;
String user_id = null;
try
{
JSONArray obj = new JSONArray(result);
JSONObject o = obj.getJSONObject(0);
firstName = o.getString("first_name");
lastName = o.getString("last_name");
email = o.getString("email");
user_id = o.getString("user_id");
}
catch ( Exception e )
{
Log.d( "JSON ERRORS: " , "something happened ****" + e.getMessage() );
}
// 1) First, write to whatever local session file that the person is logged in
// - I just really need user id and name and email. And store that.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
LoginActivity.this);
if ( user_id != null && user_id.trim().length() > 0 && !user_id.trim().equals("null") )
{
prefs.edit()
.putString("first_name", firstName)
.putString("last_name", lastName)
.putString("email", email)
.putString("user_id", user_id)
.putBoolean("working", true)
.commit();
if ( user_id.equals("1"))
{
prefs.edit()
.putString("community_subscription", "1")
.commit();
}
}
}
}
}
And this is the exception message:
End of input at character 0 of
It just looks like the string is 0 characters long.
Any idea why this is happening? Before I switched my site to https this call used to work without problems.
Also the server makes an http call. If I change it to https it returns a whole bunch of HTML which is weird since I don't actually send that back.
This is my doInBackground method:
@Override
protected String doInBackground(String... theParams)
{
String myUrl = theParams[0];
final String myEmail = theParams[1];
final String myPassword = theParams[2];
String charset = "UTF-8";
Authenticator.setDefault(new Authenticator()
{
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication( myEmail, myPassword.toCharArray());
}
});
If my doInBackground method is inside the
public class DownloadWebPageTask extends AsyncTask<String, Void, String>
Can it be that the server is just too slow to return the string and that is why it is getting null?
It is always crashing on this with the result string being empty:
JSONArray obj = new JSONArray(result);
Here is the full code:
package com.problemio;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import org.json.JSONArray;
import org.json.JSONObject;
import com.flurry.android.FlurryAgent;
import utils.SendEmail;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class LoginActivity extends BaseActivity
{
//private TextView textView;
private Dialog dialog;
public static final String REQUEST_METHOD = "GET";
public static final int READ_TIMEOUT = 15000;
public static final int CONNECTION_TIMEOUT = 15000;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
FlurryAgent.onStartSession(this, "8CA5LTZ5M73EG8R35SXG");
setContentView(R.layout.login);
//final TextView emailask = (TextView) findViewById(R.id.email_ask);
// Show form for login_email
final EditText loginEmail = (EditText) findViewById(R.id.login_email);
//String name = loginEmail.getText().toString();
// Show field for password
final EditText password = (EditText) findViewById(R.id.password);
//String text = password.getText().toString();
//Log.d( "First parameters: " , "Login email: " + loginEmail + " AND login password: " + text);
// Show button for submit
Button submit = (Button)findViewById(R.id.submit);
submit.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
String email = loginEmail.getText().toString();
String pass = password.getText().toString();
//Set the email pattern string
// Pattern pattern = Pattern.compile(".+@.+\\.[a-z]+");
// //Match the given string with the pattern
// Matcher m = pattern.matcher(email);
// //check whether match is found
// boolean matchFound = m.matches();
// TODO: VALIDATE!!!
if ( email == null || email.trim().length() < 2 )
{
Toast.makeText(getApplicationContext(), "Please enter a valid email address.", Toast.LENGTH_LONG).show();
}
else
if ( pass == null || pass.trim().length() < 2 )
{
Toast.makeText(getApplicationContext(), "Please enter a correct password.", Toast.LENGTH_LONG).show();
}
else
{
sendFeedback(pass, email);
}
}
});
// Show button for submit
Button forgot_password = (Button)findViewById(R.id.forgot_password);
forgot_password.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
Toast.makeText(getApplicationContext(), "Please wait...", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, ForgotPasswordActivity.class);
LoginActivity.this.startActivity(intent);
}
});
// Now add messaging for creating a profile
final TextView create_profile_message = (TextView) findViewById(R.id.create_profile_message);
Button create_profile = (Button)findViewById(R.id.create_profile);
create_profile.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
//sendEmail("Create Profile Clicked", "From Login screen, someone clicked on the create profile button" );
Intent myIntent = new Intent(LoginActivity.this, CreateProfileActivity.class);
LoginActivity.this.startActivity(myIntent);
}
});
}
public void sendFeedback(String pass , String email)
{
String[] params = new String[] { "http://www.problemio.com/auth/mobile_login.php", email, pass };
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(params);
}
// Subject , body
public void sendEmail( String subject , String body )
{
String[] params = new String[] { "http://www.problemio.com/problems/send_email_mobile.php", subject, body };
SendEmail task = new SendEmail();
task.execute(params);
}
public class DownloadWebPageTask extends AsyncTask<String, Void, String>
{
private boolean connectionError = false;
@Override
protected void onPreExecute( )
{
dialog = new Dialog(LoginActivity.this);
dialog.setContentView(R.layout.please_wait);
dialog.setTitle("Logging You In");
TextView text = (TextView) dialog.findViewById(R.id.please_wait_text);
text.setText("Please wait while you are being logged in...");
dialog.show();
}
// orig
@Override
protected String doInBackground(String... theParams)
{
String myUrl = theParams[0];
final String myEmail = theParams[1];
final String myPassword = theParams[2];
String charset = "UTF-8";
Authenticator.setDefault(new Authenticator()
{
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication( myEmail, myPassword.toCharArray());
}
});
String response = null;
String stringUrl = "https://www.problemio.com/auth/[email protected]&password=130989";
String result = "";
String inputLine;
try
{
String query = String.format("login=%s&password=%s",
URLEncoder.encode(myEmail, charset),
URLEncoder.encode(myPassword, charset));
final URL url = new URL( myUrl + "?" + query );
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("login", myEmail);
conn.setRequestProperty("password", myPassword);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.connect();
final InputStream is = conn.getInputStream();
final byte[] buffer = new byte[8196];
int readCount;
final StringBuilder builder = new StringBuilder();
while ((readCount = is.read(buffer)) > -1)
{
builder.append(new String(buffer, 0, readCount));
}
response = builder.toString();
}
catch (Exception e)
{
sendEmail ( "Login Activity 1 Network Error" , "Error: " + e.getMessage() );
}
return response;
}
@Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
try {
dialog.dismiss();
} catch (Exception ee) {
// nothing
}
if ( connectionError == true )
{
Toast.makeText(getApplicationContext(), "Please try again. Possible Internet connection error.", Toast.LENGTH_LONG).show();
}
if ( result != null && result.equals( "no_such_user") )
{
Toast.makeText(getApplicationContext(), "Your email and password do not match out records. Please try again or create and account.", Toast.LENGTH_LONG).show();
//final TextView login_error = (TextView) findViewById(R.id.login_error);
}
else
{
String firstName = null;
String lastName = null;
String email = null;
String user_id = null;
try
{
JSONArray obj = new JSONArray(result);
Log.d( "CONNECTION*** ERRORS: " , ".....5" );
JSONObject o = obj.getJSONObject(0);
firstName = o.getString("first_name");
lastName = o.getString("last_name");
email = o.getString("email");
user_id = o.getString("user_id");
}
catch ( Exception e )
{
Log.d( "JSON ERRORS: " , "some crap happened ****" + e.getMessage() );
}
// 1) First, write to whatever local session file that the person is logged in
// - I just really need user id and name and email. And store that.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
LoginActivity.this);
if ( user_id != null && user_id.trim().length() > 0 && !user_id.trim().equals("null") )
{
prefs.edit()
.putString("first_name", firstName)
.putString("last_name", lastName)
.putString("email", email)
.putString("user_id", user_id)
.putBoolean("working", true)
.commit();
if ( user_id.equals("1"))
{
prefs.edit()
.putString("community_subscription", "1")
.commit();
}
}
}
}
}
// TODO: see if I can get rid of this
public void readWebpage(View view)
{
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] { "http://www.problemio.com/auth/mobile_login.php" });
}
@Override
public void onStop()
{
super.onStop();
}
}
Upvotes: 0
Views: 297
Reputation: 1278
As per google this is old technique Retrieve API data using AsyncTask, I prefer Retrofit + RxJava + RxAndroid + GSON.
It will remove all your boilerplate code. It is so easy to use Retrofit.
Add Below dependency in your app,
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
//Rx android
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
Create MyRetrofit class,
import com.google.gson.GsonBuilder;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MyRetrofit {
private static Retrofit retrofit = null;
public static Retrofit getInstance() {
String BASE_URL = "https://www.problemio.com/";
if (retrofit == null) {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.readTimeout(60, TimeUnit.MINUTES);
httpClient.connectTimeout(60, TimeUnit.SECONDS);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
}
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setLenient().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient.build())
.build();
}
return retrofit;
}
}
Create APIService Class,
import in.kintanpatel.customrecylerview.model.LoginBean;
import io.reactivex.Observable;
import retrofit2.Response;
import retrofit2.http.GET;
import retrofit2.http.Query;
/**
* Created by kintan on 8/11/18.
*/
public interface APIService {
//Add All your method here
@GET("auth/mobile_login.php/")
Observable<Response<ArrayList<LoginBean>>> doLogin(@Query("login") String login, @Query("password") String password);
}
That's it now call your API stuff here,
private void doLoginAPI(String userName, String password) {
//Show Loading here
MyRetrofit.getInstance().create(APIService.class).doLogin(userName, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<ArrayList<LoginBean>>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Response<ArrayList<LoginBean>> response) {
//Hide Loading here
if (response.isSuccessful()) { //check HTTP Response
LoginBean bean = response.body().get(0);
Log.e("USER_INFO", "User ID: " + bean.getUserId());
Log.e("USER_INFO", "User First Name : " + bean.getFirstName());
Log.e("USER_INFO", "User Last Name : " + bean.getLastName());
Log.e("USER_INFO", "User Email : " + bean.getEmail());
}
}
@Override
public void onError(Throwable e) {
Log.e("USER_INFO", e.getMessage());
}
@Override
public void onComplete() {
}
});
}
And just call your method where you want:
doLoginAPI("[email protected]", "130989");
And Your output like,
11-08 14:44:59.946 25447-25447/com.kintanpatel.baserecyclerview E/USER_INFO: User ID: 1601470
11-08 14:44:59.946 25447-25447/com.kintanpatel.baserecyclerview E/USER_INFO: User First Name : TestName
11-08 14:44:59.946 25447-25447/com.kintanpatel.baserecyclerview E/USER_INFO: User Last Name : null
11-08 14:44:59.946 25447-25447/com.kintanpatel.baserecyclerview E/USER_INFO: User Email : [email protected]
Upvotes: 1
Reputation: 11873
ISSUE #1
From your question description:
The server code returns the right string if I enter the URL into a browser.
I'm assuming you are using HTTP GET. However you are using HTTP POST in your code instead:
String query = String.format("login=%s&password=%s",
URLEncoder.encode(myEmail, charset),
URLEncoder.encode(myPassword, charset));
final URL url = new URL( myUrl + "?" + query );
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST"); // <----------- replace with "GET"
ISSUE #2
conn.setDoOutput(true);
When set to true the request method is changed to POST, since GET or DELETE can't have a request body. To continue with a GET request you must set conn.setDoOutput(false);
Also, comment out those lines:
//conn.setRequestProperty("login", myEmail);
//conn.setRequestProperty("password", myPassword);
//conn.setDoOutput(true);
ISSUE #3
task.execute(new String[] { "http://www.problemio.com/auth/mobile_login.php" });
From Android 8: Cleartext HTTP traffic not permitted
You must change the URL from http to https or add android:usesCleartextTraffic="true"
in the manifest. This will only effect on devices running API level 23+. Before 23+ http is allowed by default.
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:usesCleartextTraffic="true"
...>
...
</application>
</manifest>
For me using https worked properly.
ISSUE #4
Upon providing wrong credentials your server is sending a plain text message
no_such_user
Which needs to be replaced with a valid JSON string.
From my end, the code you provided is working properly after fixing those issues.
Upvotes: 2
Reputation: 118
Looking your Json I can see a null
[
{
"user_id": "1601470",
"email": "[email protected]",
"first_name": "TestName",
"last_name": null
}
]
Json files accept nulls, but json objects do not accept it.
The application is crashing when you try to get the last_name
.
Instead of null use empty.
[
{
"user_id": "1601470",
"email": "[email protected]",
"first_name": "TestName",
"last_name": ""
}
]
Regards
Upvotes: 1
Reputation: 362
I tried your code using HttpURLConnection in async task, It gave me the desired output without error.. But if I give different password in the get url.. the response is not JSONObject. It is coming as String value. may be that causing the issue(u handle that also in postexecute method)
public class HttpGetRequest extends AsyncTask<String, Void, String> {
public static final String REQUEST_METHOD = "GET";
public static final int READ_TIMEOUT = 15000;
public static final int CONNECTION_TIMEOUT = 15000;
@Override
protected String doInBackground(String... params) {
String stringUrl = "https://www.problemio.com/auth/[email protected]&password=130989";
String result = "";
String inputLine;
try {
//Create a URL object holding our url
URL myUrl = new URL(stringUrl);
//Create a connection
HttpURLConnection connection = (HttpURLConnection)
myUrl.openConnection();
//Set methods and timeouts
connection.setRequestMethod(REQUEST_METHOD);
connection.setReadTimeout(READ_TIMEOUT);
connection.setConnectTimeout(CONNECTION_TIMEOUT);
//Connect to our url
connection.connect();
//Create a new InputStreamReader
InputStreamReader streamReader = new
InputStreamReader(connection.getInputStream());
//Create a new buffered reader and String Builder
BufferedReader reader = new BufferedReader(streamReader);
StringBuilder stringBuilder = new StringBuilder();
//Check if the line we are reading is not null
while ((inputLine = reader.readLine()) != null) {
stringBuilder.append(inputLine);
}
//Close our InputStream and Buffered reader
reader.close();
streamReader.close();
//Set our result equal to our stringBuilder
result = stringBuilder.toString();
} catch (Exception e) {
}
return result;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// textView.setText("Response is: " + response);
try {
JSONArray obj = new JSONArray(result);
JSONObject o = obj.getJSONObject(0);
String firstName = o.getString("first_name");
String lastName = o.getString("last_name");
String email = o.getString("email");
String user_id = o.getString("user_id");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Upvotes: 1