这完全是凭借我自己的看法实现的。因为我发现有的人是把数据存储再Android本地数据库里的,我就不想在本地再新建一个数据库了,直接在需要的时候直接向服务器请求数据。
前一篇文章已经把服务器端的登录搞定了。现在是Android这边的http请求的发出和处理返回的数据,然后进行界面的显示。
这里要注意一个编码得问题,就是安卓这边收发数据使用得是什么编码,然后服务器这边收发数据又是什么编码,这里我们需要把编码全都设置位utf-8编码格式。 不过如果不出现中文的话,编码什么的问题不大,因为ASCII码大家都兼容。所以出了问题再说。
在调试的时候发现: ipconfig出来
下面是wires hark的选项
这里有两个以太网网卡,然后我想抓模拟器的包(虚拟机3),发现抓不到发出去的http。然后转念一想,直接去抓以太网网卡的包,然后就抓到了。
这里遇到了几个问题。这里记录一下,估计是新手常见错误:
- AndroidManifest.xml 文件里没有加
<uses-permission android:name="android.permission.INTERNET"/>
也就是需要访问网络资源,但是你没有给它访问网络资源的权限。现象就是 没反应,程序进行到网络连接部分就进行不了了,但是又没有什么报错。 很难受,然后各种调试。。。。还是自己基础太差了。。
2.连接网络的子线程不能使用
Toast.makeText(getApplicationContext(), "输入"+username+" "+password, Toast.LENGTH_SHORT).show();
进行调试,这个会出现app闪退的情况,然后报:
的错误,即 “很抱歉, xxx已停止运行”。
这位大哥里说的:
在安卓开发环境下,由于主线程不能进行网络访问,因此需要在开启一个子线程向服务器提交数据。为了更加直观的观察数据,可以在程序屏幕上显示服务器反馈信息。又由于子线程无法更改UI界面,因此需要引入Handler代理器(也就是说,在这个handler里可以实现UI界面的更改)。实现get/post提交基本步骤就是,获取URL路径,根据路径得到Http连接,用HttpURLConnection对象设置相关的http配置信息、提交方式以及获取反馈码。当响应码为200时表示提交成功,可以通过HttpURLConnection以流的形式获取反馈信息。
我应该就是由于在子线程里更改UI界面了,所以报错了。
然后也是用wireshark调试了一波。。花费了不少时间。然后忽然记起来wires hark不能抓自己发给自己的包,只能抓往外的包。。 rawcap可以 。参考 :
何柄融:netty的半包和黏包现象的底层深度剖析 tcp的粘包拆包理论 RawCap抓自己发给自己的包zhuanlan.zhihu.com
即可。
下面是app上成功进行访问后的第一次,这个是直接把返回的数据写在了账号框里,这样调试着方便。(我这里是code=100表示成功,200表示失败。)
下面是浏览器上的,可以进行对比:
然后是服务器端的输出:
一切现在都连接起来了。
现在是模拟器是客户端,本地电脑是服务端。然后app连接的服务器地址是本地的ipv4地址。即使不同网段什么的都没影响,都是可以连接的。
把此刻的程序贴出来吧,GitHub上传比较完善一点的比较好。
public class MainActivity extends AppCompatActivity {
private Button loginButton;
private Handler handler; // 声明一个Handler对象
private String result = ""; //声明一个代表显示内容的字符串
private EditText usernameText;
String username;
String password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loginButton=findViewById(R.id.loginButton);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
usernameText=findViewById(R.id.username);
EditText passwordText=findViewById(R.id.password);
username=usernameText.getText().toString();
password=passwordText.getText().toString();
Toast.makeText(getApplicationContext(), "输入"+username+" "+password, Toast.LENGTH_SHORT).show();
// 创建一个新线程,用于发送并读取微博信息
new Thread(new Runnable() {
public void run() {
send(); //发送文本内容到Web服务器
Message m = handler.obtainMessage(); // 获取一个Message
// Toast.makeText(getApplicationContext(), "message"+m, Toast.LENGTH_SHORT).show();
handler.sendMessage(m); // 发送消息
}
}).start(); // 开启线程
}
});
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (result != null) {
Toast.makeText(getApplicationContext(), "返回"+result, Toast.LENGTH_SHORT).show();
usernameText.setText(result);
}
super.handleMessage(msg);
}
};
}
public void send() {
// Toast.makeText(getApplicationContext(), " go in send ", Toast.LENGTH_SHORT).show();
String target="";
target = "http://175.10.104.21/login?username="+username.trim()+"&password="+password.trim(); //要访问的URL地址
URL url;
try {
url = new URL(target);
HttpURLConnection urlConn = (HttpURLConnection) url
.openConnection(); //创建一个HTTP连接
InputStreamReader in = new InputStreamReader(
urlConn.getInputStream()); // 获得读取的内容
BufferedReader buffer = new BufferedReader(in); // 获取输入流对象
String inputLine = null;
//通过循环逐行读取输入流中的内容
while ((inputLine = buffer.readLine()) != null) {
result += inputLine + "n";
}
//Toast.makeText(getApplicationContext(), "result "+result, Toast.LENGTH_SHORT).show();
in.close(); //关闭字符输入流对象
urlConn.disconnect(); //断开连接
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
然后是xml的:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:drawableLeft="@mipmap/zhanghao"
android:id="@+id/username" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:hint=" 密码"
android:inputType="textPassword"
android:drawableLeft="@mipmap/mima"
android:id="@+id/password" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
android:textColor="#FFFFFF"
android:background="#FF009688"
android:id="@+id/loginButton" />
</LinearLayout>
最后注意:下面这个网络权限允许的位置即可。
然后现在把它完善起来。加上界面跳转、服务器返回数据解析之类的。
看着自己这开的一堆东西就怕。。幸好台式机带得动。。。没有成为累赘。。
我们得到服务器返回的数据之后,要先判断登录是否成功,然后再进行界面跳转。
所以要使用Gson进行json数据的解析,然后进行判断。
这篇文章进行的gson使用。这里学习了Android的jar包添加,然后是grade文件的配置,然后
Gson gson=new Gson(); 创建对象,fromJson是字符串转对象,toJson是对象转字符串。处理器的代码如下
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (result != null) {
Gson gson=new Gson();
Msg message=gson.fromJson(result,Msg.class);
if(message.code==100){
Toast.makeText(getApplicationContext(), "登录成功,进行跳转", Toast.LENGTH_SHORT).show();
}else{
usernameText.setText("error number");
}
}
super.handleMessage(msg);
}
};
这样点击登录后即可正常返回code=100,然后进行输出。
可是再点击一次登录就app闪退报错了。这里不太理解Android里面的线程模型,也不知道哪里出错了,欢迎指出问题。
然后是获得数据确认成功后进行跳转: 下面是跳转成功界面:
到这里上传一次github吧。
由于对github操作比较差,现在就不搞它先了。直接上传zip来得简单。还是太菜了。
源码如下 :
https://github.com/jackhbr/--stm32f4-/blob/master/main.zipgithub.com
后面就是app在新界面里发送请求获得正常网关的数据。此时服务器端也要加代码了。
欢迎交流讨论。