使用Retrofit Android库。我试图将POST请求推送到一个Web服务器,该服务器应该在通过POST成功调用“/ login”方法后返回3个字段。信息:
无论应用客户端输入“user”和“password”的值,Web服务器都应发送包含三个字段的单个JSONObject:
基于这些信息,我做的第一件事就是创建一个如下所示的POJO Java方法:
package com.example.joselopez.prueba1;
import java.util.List;
public class LoginInfo_POJO {
public String _ok;
public String _msg;
public Dataset _dataset;
class Dataset {
String _idsession;
}
}
我接下来要做的是创建一个包含登录方法的界面(我可以在成功登录后添加其他方法):
package com.example.joselopez.prueba1;
import retrofit.http.POST;
import retrofit.http.Query;
public interface IApiMethods {
// Log-in method
@FormUrlEncoded
@POST("/login")
LoginInfo_POJO logIn(@Query("user") String user,
@Query("password") String password);
}
几乎就在那里。现在我创建一个从AsyncTask扩展的类,它将在一个单独的线程中执行网络操作。该类位于“MainActivity.java”文件中。
...
...
private class BackgroundTask_LogIn extends AsyncTask<Void, Void, LoginInfo_POJO> {
RestAdapter restAdapter;
@Override
protected LoginInfo_POJO doInBackground(Void... params) {
IApiMethods methods = restAdapter.create(IApiMethods.class);
LoginInfo_POJO loginInfo = methods.logIn(mUser, mPassword);
return loginInfo;
}
@Override
protected void onPreExecute() {
restAdapter = new RestAdapter.Builder()
.setEndpoint("http://example.us.es:3456")
.build();
}
@Override
protected void onPostExecute(LoginInfo_POJO loginInfo_pojo) {
tv.setText("onPostExecute()");
tv.setText(loginInfo_pojo._ok + "\n\n");
tv.setText(tv.getText() + loginInfo_pojo._msg + "\n\n");
tv.setText(tv.getText() + loginInfo_pojo.data.id_sesion);
}
}
}
MainActivity布局包含一个TextView(在RelativeLayout中),其id为“textView”,并在代码中实例化为“tv”,您将在下面看到。 MainActivity的完整代码是:
package com.example.joselopez.prueba1;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import retrofit.RestAdapter;
public class MainActivity extends AppCompatActivity {
TextView tv;
String mUser, mPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textView);
mUser = "test_user";
mPassword = "test_password";
BackgroundTask_LogIn tryLogin = new BackgroundTask_LogIn();
tryLogin.execute();
}
private class BackgroundTask_LogIn extends AsyncTask<Void, Void, LoginInfo_POJO> { ... }
一切都应该有效。但事实并非如此,经过一些调试后我发现类BackgroundTask_LogIn中的onPostExecute()方法在行中停止:
for (LoginInfo_POJO.Dataset dataset : loginInfo_pojo._dataset) {
抛出的错误是:
com.example.joselopez.prueba1 E / AndroidRuntime:FATAL EXCEPTION:main 显示java.lang.NullPointerException
所以我在这一行设置一个断点并猜测是什么?我的LoginInfo_POJO实例为其内部变量保存了这些值:
这意味着我的变量没有从服务器响应填充,但是连接似乎成功,因为doInBackground方法完全运行并且正在调用onPostExecute。
那你觉得怎么样?也许我没有以正确的方式执行POST请求?
正如@Gaëtan所说,我在POJO课上犯了一个大错误;那里的局部变量名必须与生成的JSON中的那些相等。我说我期待来自JSON的字段“ok”,“msg”,“data”和“id_session”,但我的LoginInfo_POJO中的局部变量的名称为“_ok”,“_ msg”,“_ datatt”,“_ idsession” (注意领先的下划线)。从Retrofit的角度来看,这是一个巨大的错误,因此重写POJO方法可以解决这个问题。
答案 0 :(得分:1)
有关如何使用Retrofit的一些信息:
_ok
,_msg
和_dataset
,而JSON响应包含ok
,msg
和data
。这里有两个选项:重命名字段以匹配JSON响应,或者在每个字段上使用@SerializedName
注释来提供JSON字段的名称。public class LoginInfo_POJO {
// If you rename the fields
public String ok;
public String msg;
public List<Dataset> data;
// If you use annotation
@SerializedName("ok")
public String _ok;
@SerializedName("msg")
public String _msg;
@SerializedName("data")
public String _dataset;
}
AsyncTask
的方法。不要使用API方法的返回类型(此处为LoginInfo_POJO logIn(String, String
),而是使用类型为Callback<LoginInfo_POJO>
的最后一个参数。请求将通过改造在后台线程上执行,并且在请求完成时将在主线程上调用回调(或者如果出现错误则会失败)。有关详细信息,请参阅{{3>},在 SYNCHRONOUS VS.异步VS.可观察的部分。
答案 1 :(得分:1)
在onPreExecute中构建适配器时,可以尝试将logLevel选项设置为RestAdapter.LogLevel.FULL,以获得有关实际进行的更多信息。
来自Square Retrofit API Declaration:
如果您需要仔细查看请求和响应,可以使用LogLevel属性轻松地将日志记录级别添加到RestAdapter。
将logLevel设置为FULL的示例:
Employee
那就是说,你不需要AsyncTask和Retrofit。