汇总:本系列的汇总
新建Android Studio工程。
最重要的一步:添加网络权限!添加网络权限!!。还记得当时debug的时候,后面的我都写好了…然后因为没有添加网络权限怎么都是错…说起来都是泪…
在AndoidManifest中添加如下代码:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
添加下面的依赖,点击sync now。okhttp用于发送http请求,fastjson用于解析JSON串和对象的转化。
implementation 'com.squareup.okhttp:okhttp:2.4.0'
implementation 'com.alibaba:fastjson:1.2.62'
新建RegisterActivity、LoginActivity、SuccessActivity和其对应的xml,分别用于注册、登录、登录成功之后跳转的页面。自行发挥吧,我这因为登录和注册界面xml基本一样就只贴activity_register.xml和activity_success.xml了。非常非常简单的xml。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical"
tools:context=".RegisterActivity">
<EditText
android:id="@+id/reg_uid"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="请设置账号" />
<EditText
android:id="@+id/reg_password"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="请设置密码" />
<Button
android:id="@+id/reg_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="注 册"
android:textSize="20dp" />
LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SuccessActivity">
<TextView
android:id="@+id/welcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Congratulations!"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/userId"
android:layout_width="wrap_content"
android:layout_height="28sp"
android:text="UserId"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/welcome"
app:layout_constraintStart_toStartOf="@+id/welcome"
app:layout_constraintTop_toBottomOf="@+id/welcome"
app:layout_constraintVertical_bias="0.05" />
androidx.constraintlayout.widget.ConstraintLayout>
添加Entity包,在其中加入User类。因为我们在服务端loginCheck时会返回一个User对象,我们客户端也需要一个User对象来容纳返回的数据。
下面是我编写的User类,我特意将User类编写得与服务端不一样:服务端没有age属性。但是这并不会影响其对服务器对象的接收,因为服务端的User里面也有userId和password。
public class User {
String userId; //id
String password; //密码
String age; //age 故意多的一个和服务器不同的变量
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
(非必需)这里面我们可以加入一个小工具,检测网络是否开启,以改善用户体验。在uitl包下加入NetStateUtil:
public class NetStateUtil {
static String NETWORK_STATE_ERROR = "网络连接错误";
public static boolean checkNetworkState(Context context){
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo == null){ //网络连接异常
Toast.makeText(context,NETWORK_STATE_ERROR,Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}
以RegisterActivity为例,我们设置在点击“登录”之后发起http请求,将userId和password送去服务器判断是否能够注册。点击事件和网络相关的代码如下:
//注册对应的点击事件
registerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1. 获取控件中所填写的值, 注意一定要写在listener内!
String userId = uidText.getText().toString();
String password = passwordText.getText().toString();
//2. 网络为连接则直接返回
if(!NetStateUtil.checkNetworkState(RegisterActivity.this))
return;
//3. 发起http请求
OkHttpClient okHttpClient = new OkHttpClient(); //创建OkHttpClient实例
FormEncodingBuilder builder = new FormEncodingBuilder();
RequestBody requestBody = builder.add("userId", userId)
.add("password",password)
.build(); //需要传输的数据存入requestBody
Request request = new Request.Builder()
.url(registerUrl) //需要的url
.post(requestBody) //注册时需要向服务端上传数据,所以使用post
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() { //开启异步进程
@Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//连接超时时的提醒
Toast.makeText(RegisterActivity.this,"Fail",Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResponse(Response response) throws IOException {
//成功获取响应时
//不要toString
String responseData = response.body().string();
//解析,responseData,获取响应数据
final boolean data = JSON.parseObject(responseData,boolean.class); //将所接受的json数据转换成boolean对象
runOnUiThread(new Runnable() {
@Override
public void run() {
//显示回调函数
Toast.makeText(RegisterActivity.this,"data"+data,Toast.LENGTH_SHORT).show();
if(data == true)
LoginActivity.actionStart(RegisterActivity.this);
else
Toast.makeText(RegisterActivity.this,"账号已被注册",Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
其中三个主要的逻辑我们已经写在注释中了,这里就直接解析一下发送http请求时的操作。
大致分为这几个步骤:
步骤很简单,也很固定。需要注意的几个地方:
1 关于requestBody:
.add(“userId”,userId)其实就是在需要传输的JSON数据中加入了一个键值对。和服务端这里相对应:
2 关于Request对象:
不需要向服务端传requestBody的时候,.post那部分不要。
3 关于异步请求:
4 关于JSON解析
:获取了responseData(String类型)之后,我们可以将其解析为Java对象,上面是解析为简单对象,我们也可以解析为自定义对象、List。例子:
boolean data = JSON.parseObject(responseData,boolean.class) //简单
User user = JSON.parseObject(responseData, User.class) //自定义
List<User> users = JSON.parseArray(responseData, User.class) //Array
5 非常重要的一点是,response.body().string()不要写成toString()
,特别细小的bug,犯了凉凉。
LoginActivity的编写。实际上和RegisterActivity基本一模一样,该注意的地方也说了,就不贴代码了。主要是onResponse里面有点不一样,需要解析对象。
@Override
public void onResponse(Response response) throws IOException {
final String responseData = response.body().string(); //响应数据
final User user = JSON.parseObject(responseData,User.class); //将所接受的json数据转换成boolean对象
runOnUiThread(new Runnable() {
@Override
public void run() {
//显示回调函数
if(user!=null) //找到这个user的时候页面跳转
SuccessActivity.actionStart(LoginActivity.this,user.getUserId());
else //错误提示
Toast.makeText(LoginActivity.this,"账号或密码错误",Toast.LENGTH_SHORT).show();
}
});
}
SuccessActivity:与网络通信没什么关系了,主要是模拟登录成功之后的页面:
public class SuccessActivity extends AppCompatActivity {
String uid;
TextView uidText;
/**
* 启动本activity
* @param context 上一个activity的运行环境
* @param uid 用户id,页面间传递数据的用
*/
public static void actionStart(Context context,String uid){
Intent intent = new Intent(context,SuccessActivity.class);
intent.putExtra("uid",uid);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_success);
//获取数据
Intent intent = getIntent();
uid = intent.getStringExtra("userId");
//跳转到本页面之后在上面显示user的id
uidText = findViewById(R.id.userId);
uidText.setText(uid);
}
}
安卓客户端目前已经编写完毕了,可以完成我们一开始展示的效果了。但是我们注意到,其实每一次网络请求步骤都是一样的,不一样的只有url和requestBody,故我们何不将通用的网络操作提取到一个类中,每次需要的时候调用那个类就好了。
直接贴代码吧,比较简单。放在util包下:
public class HttpPostUtil {
/**
* 发起post的http请求
* @param address url地址
* @param requestBody 需要传输的数据
* @param callback 回调接口,请求的最终结果会回调到其中
*/
public static void sendOkHttpRequest(String address, RequestBody requestBody, Callback callback){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address)
.post(requestBody)
.build();
client.newCall(request).enqueue(callback);
}
}
以RegisterActivity为例,我们可以在其中将网络通信相关的代码改写为:
FormEncodingBuilder builder = new FormEncodingBuilder();
RequestBody requestBody = builder.add("userId", userId)
.add("password",password)
.build(); //需要传输的数据存入requestBody
HttpPostUtil.sendOkHttpRequest(loginUrl, requestBody, new Callback() {
@Override
public void onFailure(Request request, IOException e) {
...
}
@Override
public void onResponse(Response response) throws IOException {
...
}
});
这样就会少点代码了。
好啦,客户端也编写完成了。在你的电脑上运行Springboot服务端,在手机注册和登录,就可以实现我们最开始所说的效果了。