汇总:本系列的汇总
Android Studio配置
-
新建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。
<?xml version="1.0" encoding="utf-8"?> <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>
<?xml version="1.0" encoding="utf-8"?> <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请求时的操作。
大致分为这几个步骤:- 创建OkHttpClient对象。
- 将所需要向服务器传输的数据放入requestBody。
- 使用requestBody和要访问的Url创建request对象。
- 开启异步进程进行网络请求。
步骤很简单,也很固定。需要注意的几个地方:
1 关于requestBody:
.add(“userId”,userId)其实就是在需要传输的JSON数据中加入了一个键值对。和服务端这里相对应:
2 关于Request对象:
不需要向服务端传requestBody的时候,.post那部分不要。
3 关于异步请求:
- 网络连接属于耗时操作,直接在UI线程操作会阻塞、闪退。使用上面的写法其实是开启了一个异步进程,不会卡。
- 在异步进程中进行UI操作时需要用runOnUiThread进行UI操作,否则会闪退。当然也还有使用Handler的方法,这里我们暂时留个坑。
- 其实okhttp里面也有同步方式的写法(excute)。但是我看有人说已经弃用了,这里就不说了。
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,故我们何不将通用的网络操作提取到一个类中,每次需要的时候调用那个类就好了。
将重复代码提取为HttpPostUtil类
直接贴代码吧,比较简单。放在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服务端,在手机注册和登录,就可以实现我们最开始所说的效果了。