有时候我么可能会遇到一些比较特殊的需求,比如说要在应用程序里展示一些网页,但又明确指出不允许打开系统的浏览器。这个时候我们就可以利用WebView控件,帮助我们在自己的应用程序中嵌入一个浏览器,从而轻易的展示各种各样的网页。
使用步骤:
1.在布局文件中添加WebView控件。
2.在活动中获得WebView实例。
3.让WebView支持js脚本。
4.让网页在WebView中打开。
5.传入网址
示例:
//步骤一
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView=(WebView) findViewById(R.id.web_view);//步骤二
webView.getSettings().setJavaScriptEnabled(true);//步骤三
webView.setWebViewClient(new WebViewClient());//步骤四
webView.loadUrl("https://www.baidu.com");//步骤五
}
}
这里还有一点要注意的,打开网页属于网络操作,所以我们需要在AndroidManifest.xml文件中申请权限。
HTTP协议,它的工作原理就是客户端向服务器发出一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理就可以了。
Android上发送HTTP请求一般有两种方式:HttpURLConnection和OkHttp。接下来我们就来分别学习一下这两种方式。
使用步骤:
1.获取URL实例,并在器构造参数中传入网址。
2.获取HttpURLConnection实例。
3.给HttpURLConnection设置请求模式"GET"或"POST"。
4.给HttpURLConnection进行自由定制。
5.用IO操作来解析获得的数据。
示例:
//布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:text="Button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
//活动中的代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button=(Button) findViewById(R.id.button);
final TextView textView=(TextView) findViewById(R.id.text);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
//所有的网络操作或耗时操作都需在子线程中进行
@Override
public void run() {
BufferedReader reader = null;
HttpURLConnection connection = null;
try {
URL url=new URL("https://www.baidu.com");//步骤一
connection= (HttpURLConnection) url.openConnection();//步骤二
connection.setRequestMethod("GET");//步骤三
connection.setReadTimeout(8000);//步骤四 这里设置了读取超时的毫秒数
connection.setConnectTimeout(8000);//这里设置了链接超时的毫秒数
InputStream in=connection.getInputStream();//步骤五
reader=new BufferedReader(new InputStreamReader(in));
final StringBuilder content=new StringBuilder();
String line;
while((line=reader.readLine())!=null){
content.append(line);
}
MainActivity.this.runOnUiThread(new Runnable() {
//不能在子线程中更改UI,故采取该措施
@Override
public void run() {
textView.setText(content);
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
if(reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(connection!=null){
connection.disconnect();
}
}
}
}).start();
}
});
}
}
若是想要提交数据给服务器,只需要将HTTP请求的方法改为POST,并在获取输入流之前把要提交的数据写出即可。注意每条数据都要以键值对的形式存在,数据与数据之间用"&"符合隔开。
示例:
connection.setRequestMethod("POST");
DataOutputStream out=new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
OkHttp是一个著名的开源项目,它相较于HttpURLConnection来说更加的简单易用。项目主页
使用步骤:
1.添加依赖。
2.获取OkHttpClient实例。
3.获得Request实例,采用连缀的方式为其添加url。
4.获得Response实例,服务器所返回的数据就存储在response中。
示例:
//步骤一
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
}
//
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button=(Button) findViewById(R.id.button);
final TextView textView=(TextView) findViewById(R.id.text);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client=new OkHttpClient();//步骤二
Request request= new Request.Builder()//步骤三
.url("https://www.baidu.com")
.build();
Response response=client.newCall(request).execute();//步骤四
final String content=response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(content);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
});
}
}
如果你想要提交数据给服务器,就要先构建一个RequestBody实例来存放待提交的数据,然后在Request.Builder中调用post()方法,并将RequestBody实例传入。
示例:
RequestBody requestBody=new FormBody.Builder()
.add("username","admin")
.add("password","123456")
.build();
Request request= new Request.Builder()
.url("https://www.baidu.com")
.post(requestBody)
.build();
在网络上传输数据时最常用的格式有两种:XML和JSON,接下来我们就来逐一进行学习。
解析XML格式数据的方式一般有两种:Pull解析和SAX解析。
我们要解析的XML数据:
<apps>
<app>
<id>1</id>
<name>Google maps</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>Google Play</name>
<version>2.3</version>
</app>
</apps>
使用步骤:
1.获取XmlPullParserFactory实例x。
2.获取XmlPullParser实例。
3.给xmlPullParser实例添加待解析的数据。
4.开始解析。
示例:
private void parseXMLWithPull(String xmlData){
try {
XmlPullParserFactory factory=XmlPullParserFactory.newInstance();//步骤一
XmlPullParser parser=factory.newPullParser();//步骤二
parser.setInput(new StringReader(xmlData));//步骤三
int eventType=parser.getEventType();//步骤四
String id="";
String name="";
String version="";
while (eventType!=XmlPullParser.END_DOCUMENT){
String nodeName=parser.getName();
switch (eventType){
//开始解析某个节点
case XmlPullParser.START_TAG: {
if ("id".equals(nodeName)) {
id = parser.nextText();
} else if ("name".equals(nodeName)) {
name = parser.nextText();
} else if ("version".equals(nodeName)) {
version = parser.nextText();
}
break;
}
//完成解析某个节点
case XmlPullParser.END_TAG:{
if("app".equals(nodeName)){
Log.e("MainActivity",id );
Log.e("MainActivity",name );
Log.e("MainActivity",version );
}
break;
}
default:
break;
}
eventType=parser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
使用步骤:
1.新建一个类继承DefaultHandler类,并重写其中的5大方法。
2.获取SAXParserFactory实例。
3.获取XMLReader实例。
4.获取新建类的实例。
5.给XMLReader设置Handler。
6.给XMLReader传入待解析的数据。
示例:
//步骤一
public class MyHandler extends DefaultHandler {
StringBuilder id;
StringBuilder name;
StringBuilder version;
String nodeName;
@Override
public void startDocument() throws SAXException {
//开始解析时
super.startDocument();
id=new StringBuilder();
name=new StringBuilder();
version=new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//解析某个节点时
super.startElement(uri, localName, qName, attributes);
//记录当前节点名
nodeName=localName;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//获取节点中内容时
super.characters(ch, start, length);
//根据当前节点名判断将内容添加到哪一个StringBuilder中
if("id".equals(nodeName)){
id.append(ch, start, length);
}else if("name".equals(nodeName)){
name.append(ch, start, length);
}else if("version".equals(nodeName)){
version.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//完成解析某个节点时
super.endElement(uri, localName, qName);
if("app".equals(nodeName)){
Log.e("MyHandler",id.toString().trim());
Log.e("MyHandler",name.toString().trim());
Log.e("MyHandler",version.toString().trim());
}
//最后要将StringBuilder清空掉,否则会影响下一次内容的读取
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
@Override
public void endDocument() throws SAXException {
//完成解析时
super.endDocument();
}
}
//活动中代码
private void parseXMLWithSAX(String xmlData){
try {
SAXParserFactory factory=SAXParserFactory.newInstance();//步骤二
XMLReader reader=factory.newSAXParser().getXMLReader();//步骤三
MyHandler myHandler=new MyHandler();//步骤四
reader.setDTDHandler(myHandler);//步骤五
reader.parse(new InputSource(new StringReader(xmlData)));//步骤六
}catch (Exception e){
e.printStackTrace();
}
}
接下来我们来学习如何解析JSON格式的数据。比起XML,JSON的主要优势在于它的体积更小,在网络上传输的时候可以更省流量。但缺点在于,它的语义性较差,看起来不如XML直观。
解析JSON格式数据的方式一般有两种:JSONObject解析和GSON解析。
我们要解析的JSON数据:
[{
"id":"5","version":"5.5","name":"Clash of Clans"},
{
"id":"6","version":"7.0","name":"Boom Beach"},
{
"id":"7","version":"3.5","name":"Clash Royale"}]
使用步骤:
1.将待处理的数据传入到一个JSONArray对象中。
2.遍历JSON数组,获取数据。
示例:
private void parseJSONWithJSONObject(String jsonData){
try {
JSONArray array=new JSONArray(jsonData);//步骤一
for(int i=0;i<array.length();i++){
//步骤二
JSONObject object=array.getJSONObject(i);
String id=object.getString("id");
String name=object.getString("name");
String version=object.getString("version");
Log.e("MainActivity",id);
Log.e("MainActivity",name);
Log.e("MainActivity",version);
}
}catch (Exception e){
e.printStackTrace();
}
}
GSON是一个谷歌提供的开源库,它主要就是可以将一段JSON格式的字符串自动映射成一个对象,从而不需要我们再手动去编写代码进行解析了。
比如一段JSON格式的数据如下所示:
{"name":"Tom","age":20}
那我们就可以定义一个Person类,并加入name和age这两个字段,然后只需要简单的调用如下代码就可以将JSON数据自动解析为一个Person对象了。
Gson gson=new Gson();
Person person=gson.fromJson(jsonData,Person.class);
如果要解析的是一段JSON数组会稍微麻烦一点,我们需要借助TypeToken将期望解析成的数据类型传入到fromJson()方法中,如下所示:
List list=gson.fromJson(jsonData, new TypeToken>(){}.getType());
使用步骤总结:
1.首先添加依赖。
2.创建一个数据类。
3.获取Gson实例。
4.将待解析的数据解析成数据类的实例。
示例:
//步骤一
dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
}
//步骤二
public class Person {
String id;
String name;
String version;
public String getId() {
return id;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
}
//步骤三,四
private void parseJSONWithGSON(String jsonData){
Gson gson=new Gson();
List<App> list=gson.fromJson(jsonData,new TypeToken<List<App>>(){
}.getType());
for(App app:list){
Log.e("MainActivity", app.getId());
Log.e("MainActivity", app.getName());
Log.e("MainActivity", app.getVersion());
}
}
这里涉及到两个问题:首先是网络连接的代码比较长,所以可以把它封装在一个类里面,然后设置一个静态的方法,每次要进行网络连接的时候调用它就可以了;还有一个问题是由于网络连接需要开启子线程,然而子线程又不能返回数据,所以需要设置回调函数。
首先这里先建立一个回调接口:
public interface HttpCallbackListener {
void onFinish(String response);//当服务器成功响应我们请求时调用
void onError(Exception e);//当网络操作错误时调用
}
然后定义HttpUtil,作为网络连接的通用类:
public class HttpUtil {
public static void sendHttpRequest(final String address,final HttpCallbackListener listener){
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection=null;
BufferedReader reader=null;
try{
URL url=new URL(address);
connection=(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setReadTimeout(8000);
connection.setConnectTimeout(8000);
InputStream in=connection.getInputStream();
reader=new BufferedReader(new InputStreamReader(in));
StringBuilder content=new StringBuilder();
String line;
while((line=reader.readLine())!=null){
content.append(line);
}
if(listener!=null){
listener.onFinish(content.toString());
}
}catch (Exception e){
e.printStackTrace();
if(listener!=null){
listener.onError(e);
}
}finally {
if(reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(connection!=null){
connection.disconnect();
}
}
}
}).start();
}
}
然后我们在MainActivity里调用它:
public void onClick(View view) {
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
//在这里根据返回内容执行具体逻辑
}
@Override
public void onError(Exception e) {
//在这里对异常情况进行处理
}
});
}
事实上OkHttp已经帮我们实现好了开辟子线程的方法,以及回调接口的定义。
示例:
//HttUtil中
public class HttpUtil {
public static void sendOkHttpRequest(String address,okhttp3.Callback callback){
//这里的okhttp3.Callback就是OkHttp帮我们定义好的回调接口
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
.url(address)
.build();
client.newCall(request).enqueue(callback);//enqueue会自动帮我们开辟线程
}
}
//MainActivity中调用它
public void onClick(View view) {
HttpUtil.sendOkHttpRequest(address, new okhttp3.Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//在这里对异常情况进行处理
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//得到服务器返回的具体内容
String responseData=response.body().string();
}
});
}
注意:无论是HttpURLConnection还是OkHttp,最终回调接口中的代码依然是在子线程中进行的,所以不能更改UI。