互联网应用

Author Avatar
Euan 10月 06, 2019
  • 在其它设备中阅读本文章

互联网应用

实验目的

1.了解android客户端访问互联网的原理;
2.了解http协议、Web API和JSON;
3.掌握WebView和OkHttp库的使用方法。

实验内容

1.创建一个新闻客户端,从指定的网站的Web API接口获取JSON数据并进行解析为Java对象;
2.根据获得的数据(图片需要下载)填充新闻列表并呈现;
3.当用户点击某一条新闻时,打开一个包含WebView的Activity显示新闻内容;
4.用户可以通过单击顶部的“返回”按钮回到新闻列表,然后再查看下一条新闻的详情。
5.本次实验的新闻数据来自于news.163.com和money.163.com。
实验完成后能获得新闻列表并查看新闻,如图1和图2所示。

Ko4OnP.png

图1新闻列表

Ko4H1A.png

图2 新闻详情
实验原理:
Android客户端访问互联网的方式有多种,其中最常见的一种是访问在互联网(云端)的服务器,通过Web API接口请求数据,数据以JSON形式返回到客户端,客户端解析后(可能需要进一步下载数据)将结果呈现给用户(如图3所示)。

Ko4b6I.png

图3 Android应用访问互联网时序图

具体来说,就是:Android客户端负责发出HTTP请求(可以使用系统原生组件HttpClient和UrlConnection或者使用第三方库如OkHttp实现);服务器响应请求,返回数据(JSON格式),Android客户端收到数据后进行解析,一般会使用GSON将其转化为Java对象以便进一步处理;为了将数据友好地呈现出来,客户端一般会使用一些控件(Widget如ListView)显示数据,为此需要某种类型的Adapter实现用数据填充控件;为了数据能够流畅展示,需要使用Holder模式;为了获得更好的用户体验(如下拉刷新和返回顶部),一般需要使用一些额外的UI组件;当用户点击某一新闻的标题时,客户端将打开一个新的Activtiy,它包含了一个用来显示网页内容的WebView,新闻内容将在这里显示,用户可以随时通过左上角的按钮返回到新闻列表。

实验步骤

分析项目:

该项目使用的是https://news.163.com网站,于是找到国内网站的url,发现其直接显示再前端,一般容易抓取信息

Ko4qXt.png

再分析https://money.163.com网站,发现其也是直接暴露再href标签上

Ko47pd.png

1 使用Android Studio(3.0以上)打开提供的初始工程包(Android API28),修改包名为com.example.internetapp_自己的学号,如“com.example.internetapp_1751302”。具体方法为,在项目的包上右键在菜单中依次选择Refactor->Rename(图4),在弹出的警告框中选择Rename package(图5),在Rename对话框中修改包名(注意:只需要填写”internetapp_自己的学号”即可,图6),修改后点击Refactor将打开预览窗口,单击窗口左下角的Do Refactor按钮完成修改(图7)。

Ko4X0f.png

2.添加Title和TitleAdapter类,如图8所示。

Ko55D0.png

图8添加类菜单

Ko5Pcn.png

图9 添加类对话框

3.在Title类中添加以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private String title;
private String picSrc;
private String link;

public Title(String title, String picSrc, String link){
this.title = title;
this.picSrc= picSrc;
this.link = link;
}

public String getTitle() {
return title;
}

public String getPicSrc() {
return picSrc;
}

public String getLink() {
return link;
}

4 修改TitleAdapter类的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.example.internetapp_1759116;//包名按自己的来

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.List;

public class TitleAdapter extends ArrayAdapter<Title> {
private int resourceId;

public TitleAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<Title> objects) {
super(context, resource, objects);
resourceId = resource;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Title title = getItem(position);
View view;
ViewHolder viewHolder;

if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.titleText = (TextView)view.findViewById(R.id.title_text);
viewHolder.titlePic = (ImageView) view.findViewById(R.id.title_pic);
view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
//加载照片
Glide.with(getContext()).load(title.getPicSrc()).into(viewHolder.titlePic);
viewHolder.titleText.setText(title.getTitle());
return view;

}
public class ViewHolder{
TextView titleText;
ImageView titlePic;
}
}

5.在MainActivity中添加一个titleList和adapter成员,如图10

Ko4ztg.png

Ko4j78.png

图10 titleList和adapter成员

6.在MainActivity中添加一个request函数,封装http请求,当服务器返回数据后,它将JSON数据转换为News对象放入List中,并且设置TitleList供TitleAdapter使用(见Response回调函数)。注意:address变量为请求的服务器地址,本实验服务器地址支持以下三个,同学们可以随意选择一个:

Ko4xAS.png

Ko5ShQ.png

尝试了

http://10.199.105.121:8800/api/news/world

http://10.199.105.121:8800/api/news/money

PS.服务器使用的是海洋大学内网地址,宿舍网络和手机需要使用SSL VPN连接。由于服务器丢包严重(学校网络问题),可能会有数据获取不到的情况,过段时间应该就会好的

详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public void requestNew(){

// 根据返回到的 URL 链接进行申请和返回数据
String address = "http://10.199.105.121:8800/api/news/domestic";
HttpUtil.sendOkHttpRequest(address, new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
/**
* 将json数据解析为newlist对象
*/
final NewsList newlist = Utility.parseJsonWithGson(responseText);

titleList.clear();
for (News news:newlist.newsList){
Title title = new Title(news.Title,news.PicSrc,news.Link);
titleList.add(title);
}

runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
listView.setSelection(0);
refreshLayout.setRefreshing(false);
};
});
}
});
}

7.在MainActivity的OnCreate中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
refreshLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_layout);
refreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary));
listView = (ListView)findViewById(R.id.list_view);
adapter = new TitleAdapter(this,R.layout.list_view_item, titleList);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
Intent intent = new Intent(MainActivity.this,ContentActivity.class);
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Title title = titleList.get(position);
intent.putExtra("title",actionBar.getTitle());
intent.putExtra("link",title.getLink());
startActivity(intent);
}
});
/**
* 点击回到顶部按钮,回到顶部
*/
Button button=findViewById(R.id.rollback);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listView.smoothScrollToPositionFromTop(0,0); //可以滑动着到顶部,后边的第一个参数设置要滑动到哪一个item,第二个不用管
}
});
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshLayout.setRefreshing(true);
requestNew();
}
});
/**
* 向服务器请求数据
*/
requestNew();

8.在ContentActivity的OnCreate函数中加入如下代码:

1
2
3
4
5
6
7
8
9
10
11
/**
* 得到MainActivity传过来的url和title
*/
String uri = getIntent().getStringExtra("link");
String title = getIntent().getStringExtra("title");
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(title);
/**
* 根据uri加载页面
*/
webView.loadUrl(uri);

9.检查最终项目的结构如图11,构建并在模拟器运行程序,测试功能是否正常。

Ko59pj.png

图11 项目结构

思考:

1.查看项目完整代码(包括AndroidManifest和Gradle脚本),理解项目的结构,学习新的控件的使用方法,掌握OkHttp库的使用。

2.如果客户端需要登陆才能查看新闻,该如何实现?

1.控件之前都已经学习过。该实验没有可以学习的控件,通过列表动态新增行的话已经在我的大作业中实现

Ko5C1s.png

这个为okhttp工具的前期配置

TitleAdapter为对布局和实例进行渲染,建立ListView界面与数据之间的桥梁

contentActivity为对各种控件加以逻辑操作

2.可以建立一个BaseFragment作为父类,在加载数据前对BaseFragment中的用户登陆情况进行验证。

自己按照水产新闻对其进行接口解析

Ko5kn0.png

http://www.linyujing.club:59017/json

换成该外网网址

String address = “http://192.168.169.17:5901/json";

内网地址

Ko5iXq.png

json解析结果

本文使用 CC BY-NC-SA 3.0 中国大陆 协议许可
具体请参见 知识共享协议

本文链接:https://zyhang8.github.io/2019/10/06/android-exp8/