安卓网络请求demo
背景
因为考虑到目前均是APP与服务端的交互,所以就萌生了一个写客户端网络请求的想法。APP与服务端交互,主要涉及的操作:获取数据,并将数据填写回客户端的view层,还有一个就是像客户端提交数据。今天就后者来构造一个客户端Demo
准备工作
编写代码的工具
现在主流用的都是AS,所以自然选用了AS。
网络请求知识
一般公司都会利用第三方库来编写适合自己的网络库,因为尝试,所以未使用公司的统一网络库,而是直接使用了第三方库。关于第三方库直接在网上搜索可以获取到很多相关知识比如开源网络库介绍1,安卓网络库介绍2 ,安卓网络库介绍3。登录的后端具体是的API形式不太清楚,只是希望能够模拟客户端的请求,Retrofit虽然很强悍,门槛相对高,所以并没有采用。Volley易用性较高,但是为了尝试一下封装,我选择了处于中间地带的Okhttp。
实战
创建项目
现在的AS都是比较智能的,一般一路next都是可以的,遇到问题,一般网上也有响应的解决方方案,这里给出一个官方的创建地址。安卓项目介绍
创建一个新的activity
先给出一个整个代码目录概况。

在最开始的时候只有MainActivity所以要新建一个loginActivity.在com.weidian.login右击-New-Activity-emptyActivity 新建之后默认在在res-layout 中默认添加activity-login.xml的默认文件。
开始页面布局
采用最简单的LineaLinearLayoutrLayout布局。具体的代码如下
<?xml version="1.0" encoding="utf-8"?>
<LineaLinearLayoutrLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#eeee">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:hint="请输入手机号"
/>
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:layout_marginTop="5dp"
android:inputType="textPassword"
android:hint="点击输入密码"
/>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:layout_marginTop="5dp"
android:text="登录"
/>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LineaLinearLayoutrLayout>
两个EditText用于输入用户名与密码,一个登录按钮。开头与底部的一个view是为了评分剩余空间。
网络请求代码
通过抓包发现请求的参数为两个对象,一个是LoginParame一个是上下文,所以新建了两个类,最核心的就是请求的封装,见下面
public class HttpUtils {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static OkHttpClient client = new OkHttpClient();
public void setLoginSuccessListener(OnLoginSuccessListener loginSuccessListener) {
this.loginSuccessListener = loginSuccessListener;
}
public void setNetworkSuccessListener(OnNetworkSuccessListener networkSuccessListener) {
this.networkSuccessListener = networkSuccessListener;
}
private OnLoginSuccessListener loginSuccessListener;
private OnNetworkSuccessListener networkSuccessListener;
public interface OnLoginSuccessListener{
void loginSuccess(Map<String, String> result);
}
private interface OnNetworkSuccessListener{
void executeSuccess(Response response);
}
public static void post(final String url, final String json, final OnNetworkSuccessListener listener) throws IOException {
new Thread(new Runnable() {
@Override
public void run() {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try {
Response response = client.newCall(request).execute();
if(listener != null){
listener.executeSuccess(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public static void checkUsernameAndPassword(LoginParame parame, ContextParam contextParam, String url, final OnLoginSuccessListener loginSuccessListener) throws IOException {
Response response = null;
final StringBuilder stringBuilder = new StringBuilder();
final JSONObject jsonObjectrequest = new JSONObject();
jsonObjectrequest.put("request", JSONObject.toJSON(parame));
jsonObjectrequest.put("context", JSONObject.toJSON(contextParam));
try{
HttpUtils.post(url, jsonObjectrequest.toString(), new OnNetworkSuccessListener() {
@Override
public void executeSuccess(Response response) {
String StringTemp;
JSONObject jsonObjectTemp = new JSONObject();
JSONObject status = new JSONObject();
//发出请求
Map<String, String> resultMap = new HashMap<>();
try {
StringTemp = response.body().string();
jsonObjectTemp = (JSONObject) JSONObject.parse(StringTemp);
status = (JSONObject) jsonObjectTemp.get("status");
resultMap.put("code", status.get("code").toString());
resultMap.put("description",status.get("description").toString());
if(loginSuccessListener != null){
loginSuccessListener.loginSuccess(resultMap);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}catch (IOException e){
Log.e("failed","login error");
}
}
}
有两个回调网络请求是否成功一个登录结果。有两个方法一个是网络请求需要回调请求是否成功,一个登录请求需要回调登录结果。另外网络请求为耗时请求,不能放在主线程,需要放到子线程。代码中的new Thread(new Runnable() {}.start就是这个目的。
atvity整个编写
界面与请求的融合,直接看下面代码
public class LoginActivity extends AppCompatActivity {
private EditText et_username;//用户名输入框
private EditText et_password;//密码输入框
private Button btn_login;//登录按钮
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
et_username = (EditText) findViewById(R.id.et_username);//得到用户名输入框
et_password = (EditText) findViewById(R.id.et_password);//得到密码输入框
btn_login=(Button)findViewById(R.id.btn_login);//得到登录按钮
//为登录按钮设置监听
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String usename = et_username.getText().toString();//得到用户名
String password=et_password.getText().toString();//得到密码
//将密码加密:
String passwordb = WdLoginEncryptUtil.encryptKoudaiPassword(password);
String passwords = WdLoginEncryptUtil.encryptWeidianPassword(password);
LoginParame loginParame=new LoginParame();
loginParame.countryCode="86";
loginParame.phone=usename;
loginParame.passwordb=passwordb;
loginParame.passwords=passwords;
loginParame.version="2";
ContextParam contextParam =new ContextParam();
contextParam.appid="com.vdian.tuwen";
if(usename==null||usename.equals("")){
Toast.makeText(getApplicationContext(),"帐号不能为空",Toast.LENGTH_SHORT).show();
}
if(password==null||password.equals("null")){
Toast.makeText(getApplicationContext(),"密码不能为空",Toast.LENGTH_SHORT).show();
}else {
//发出请求
try {
HttpUtils.checkUsernameAndPassword(loginParame, contextParam,"https://vapdaily.ruyu.com/com.vdian.tuwen/commonserver/login.getLogin/1.0", new HttpUtils.OnLoginSuccessListener() {
@Override
public void loginSuccess(Map<String, String> result) {
postOnUIThread(result);
}
});
} catch (IOException e) {
e.printStackTrace();
Log.e("请求异常","not bussiness error");
}
}
}
});
}
private void postOnUIThread(final Map<String, String> result) {
et_username.post(new Runnable() {
@Override
public void run() {
if(Integer.parseInt(result.get("code"))==0){
//跳转到一个webview 打开百度链接
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
Uri content_url = Uri.parse("https://www.baidu.com");
intent.setData(content_url);
startActivity(intent);
}
else {
Toast.makeText(getApplicationContext(),result.get("description"),Toast.LENGTH_SHORT).show();
}
}
});
}
}
- 显示获取各个组件
- 得到edittext中输入内容
- 组装发出请求的数据
- 先进行本地判断
- 本地判断通过后,发出请求
- 根据请求结果,进入不同页面,处理比较简单,成功跳百度,失败toast弹出失败信息。
注意一点,我们的消息是要更新到UI线程,所以使用了View.post(Runnable)方法,但是复杂的计算建议不要这么做否则容易出现ANR。具体原因见android使用post(Runnable)更新UI的误区.
其他
因为这个属于网络请求,所以在AndroidManifest需要申请网络权限。这个Demo首先就是登录页面,所以需要将loginActivity置为最先启动的activity,对于为什么要这么设置,可以参考这篇文章对于android.intent.action.MAIN和android.intent.category.LAUNCHER的理解具体配置见下面代码。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.weidian.login">
//获取网络权限
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
</activity>
<activity android:name=".LoginActivity">
//将loginActivity置为最先启动的activity
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
运行结果
用户名与密码正确,打开百度视频

输入密码错误,弹出对应的错误信息。

网友评论