安卓访问webAPI,并取回数据

安卓访问webAPI,并取回数据

前言

  安卓自从4.0以后,所有的网络访问都需要异步进程操作。其自带的异步类有AsyncTask,Handler,以及可以声明Thread等等。涉及到多进程,必须要提到一个问题,线程与线程之间不能直接进行数据的交流,UI线程的数据是受到保护的,不能执行在UI线程上的操作都需要借助其他手段进行数据的传输,本文用的是Handler的sendMessage的方式。

正文

  本文所用涉及Hander和Thread,首先介绍一下Hander。

  Handler, 它直接继承自Object,一个Handler允许发送和处理Message或者Runnable对象,并且会关联到主线程的MessageQueue 中。每个Handler具有一个单独的线程,并且关联到一个消息队列的线程,就是说一个Handler有一个固有的消息队列。当实例化一个Handler 的时候,它就承载在一个线程和消息队列的线程,这个Handler可以把Message或Runnable压入到消息队列,并且从消息队列中取出 Message或Runnable,进而操作它们。

  Handler主要有两个作用:

  • 在工作线程中发送消息。
  • 在UI线程中获取、处理消息。

  上面介绍到Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,Post和sendMessage:

  • Post:Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
  • sendMessage:sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法 有:sendEmptyMessage(int)、sendMessage(Message)、 sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。

  从上面的各种方法可以看出,不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。

  

Post

  对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。

  在Handler中,关于Post方式的方法有:

  • boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
  • boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
  • boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
  • void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。

   下面通过一个Demo,讲解如何通过Handler的post方式在新启动的线程中修改UI组件的属性:

?

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

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

package com.bgxt.datatimepickerdemo;

 

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

 

public class HandlerPostActivity1 extends Activity {

    private Button btnMes1,btnMes2;

    private TextView tvMessage;

    // 声明一个Handler对象

    private static Handler handler=new Handler();

     

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.message_activity);       

         

        btnMes1=(Button)findViewById(R.id.btnMes1);

        btnMes2=(Button)findViewById(R.id.btnMes2);

        tvMessage=(TextView)findViewById(R.id.tvMessage);

        btnMes1.setOnClickListener(new View.OnClickListener() {

             

            @Override

            public void onClick(View v) {

                // 新启动一个子线程

                new Thread(new Runnable() {                   

                    @Override

                    public void run() {

                        // tvMessage.setText("...");

                        // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问

                        // 使用post方式修改UI组件tvMessage的Text属性

                        handler.post(new Runnable() {                   

                            @Override

                            public void run() {

                                tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。");                       

                            }

                        });                               

                    }

                }).start();

            }

        });

         

        btnMes2.setOnClickListener(new View.OnClickListener() {

             

            @Override

            public void onClick(View v) {

                new Thread(new Runnable() {                   

                    @Override

                    public void run() {

                        // 使用postDelayed方式修改UI组件tvMessage的Text属性值

                        // 并且延迟3S执行

                        handler.postDelayed(new Runnable() {

                             

                            @Override

                            public void run() {

                                tvMessage.setText("使用Handler.postDelayed在工作线程中发送一段执行到消息队列中,在主线程中延迟3S执行。");   

                                 

                            }

                        }, 3000);                       

                    }

                }).start();

                 

            }

        });

    }

}

 

效果展示:

  有一点值得注意的是,对于Post方式而言,它其中Runnable对象的run()方法的代码,均执行在UI线程上,所以对于这段代码而言, 不能执行在UI线程上的操作,一样无法使用post方式执行。

Message

  Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重 写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。下面先介绍一下Message。

  Message是一个final类,所以不可被继承。Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对 象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型的键值对,对于基本数据类型,使用起来很简单,这里不 再详细讲解。而对于复杂的数据类型,如一个对象的传递就要相对复杂一些。在Bundle中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的 限制,需要实现特定的接口,当然,一些Android自带的类,其实已经实现了这两个接口中的某一个,可以直接使用。方法如下:

  • putParcelable(String key,Parcelable value):需要传递的对象类实现Parcelable接口。
  • pubSerializable(String key,Serializable value):需要传递的对象类实现Serializable接口。

  还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:

  • int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
  • int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
  • Object obj:传递一个任意的对象。
  • int what:定义的消息码,一般用于设定消息的标志。

   对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者 Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的, 才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。 Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是 调用的Message.obtain()。  

  既然Message是在线程间传递消息,那么先以一个Demo讲解一下Message的使用,还是常规的从互联网上接受WEBAPI返回的数据,下载后使用listview控件展示:

?

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

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

package com.example.myrixin;

 

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

 

import org.apache.http.HttpResponse;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.util.EntityUtils;

import org.json.JSONArray;

import org.json.JSONException;

import org.json.JSONObject;

 

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.app.Activity;

import android.app.ProgressDialog;

import android.content.Intent;

import android.util.Log;

import android.view.Menu;

import android.view.View;

import android.widget.AbsListView;

import android.widget.AbsListView.OnScrollListener;

import android.widget.AdapterView;

import android.widget.AdapterView.OnItemClickListener;

import android.widget.ArrayAdapter;

import android.widget.ListView;

import android.widget.Toast;

 

public class MainActivity extends Activity implements OnItemClickListener, OnScrollListener {

    public ListView listView;

    private static int IS_FINISH = 1;

    private ProgressDialog dialog;

    private static String url="http://10.185.210.173:8008/api/products";

    private ArrayAdapter<List<Map<String, String>>>arr_adapter;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

         

        //实例化listView

        listView=(ListView) findViewById(R.id.sjlb);

         

        dialog = new ProgressDialog(this);

        dialog.setTitle("提示信息");

        dialog.setMessage("正在下载,请稍后...");

        dialog.setCancelable(false);

         

        new Thread(new MyThread()).start();

        dialog.show();

         

//      //适配器

//      arr_adapter=new ArrayAdapter(null, 0, list);

//      //加入listview

//      listView.setAdapter(arr_adapter);

        listView.setOnItemClickListener(this);//单个条目点击的监听器

        listView.setOnScrollListener(this);//滚动变化的监听器

         

    }

     

    private Handler handler = new Handler() {      

        Map<String, String> map = null;

        List<Map<String, String>> list = new ArrayList<Map<String, String>>();

 

        // 在Handler中获取消息,重写handleMessage()方法

        @Override

        public void handleMessage(Message msg) {

            // 判断消息码是否为1

            if (msg.what == IS_FINISH) {

                //text.setText(msg.toString);

                list = jx(msg.toString());

//              try {

//                  //截取API返回的数据   Message传过来的数据加了首尾信息文

//                  int begin=msg.toString().indexOf("[");

//                  int end=msg.toString().indexOf("]");

//                  String request=msg.toString().substring(begin, end+1);

//                 

//                  JSONArray jsonArray = new JSONArray(request);

//                  for (int i = 0; i < jsonArray.length(); i++) {

//                      JSONObject item = jsonArray.getJSONObject(i); // 每条记录又由几个Object对象组成

//                      int id = item.getInt("Id"); // 获取对象对应的值{"Id":1,"Name":"Tomato Soup","Category":"Groceries","Price":1.0}

//                      String name = item.getString("Name");

//                      String categroy=item.getString("Category");

//                      String price = item.getString("Price");

//

//                      map = new HashMap<String, String>(); // 存放到MAP里面

//                      map.put("id", id + "");

//                      map.put("name", name);

//                      map.put("Category", categroy);

//                      map.put("Price", price);

//                      list.add(map);

//                  }

//                  for (Map<String, String> list2 : list) {

//                      String id = list2.get("id");

//                      String name = list2.get("name");

//                      Log.i("info", "id:" + id + " | name:" + name);

//                  }

//              } catch (JSONException e) {

//                  // TODO Auto-generated catch block

//                  e.printStackTrace();

//              }

                //适配器

                arr_adapter=new ArrayAdapter(MainActivity.this, android.R.layout.simple_expandable_list_item_1, list);

                //加入listview

                listView.setAdapter(arr_adapter);

                dialog.dismiss();

            }

        }

    };

 

    public class MyThread implements Runnable {

 

        @Override

        public void run() {

            HttpClient httpClient = new DefaultHttpClient();

            HttpGet httpGet = new HttpGet(url);

            HttpResponse httpResponse = null;

            try {

                httpResponse = httpClient.execute(httpGet);

                if (httpResponse.getStatusLine().getStatusCode() == 200) {

                     String temp = EntityUtils.toString(httpResponse.getEntity());

                    // 获取一个Message对象,设置what为1

                    Message msg = Message.obtain();

                    msg.obj = temp;

                    msg.what = IS_FINISH;

                    // 发送这个消息到消息队列中

                    handler.sendMessage(msg);

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

     

    /*

     * 解析json数据

     */

    public List<Map<String, String>> jx(String msg){

        Map<String, String> map = null;

        List<Map<String, String>> list = new ArrayList<Map<String, String>>();

        try {

            //截取API返回的数据   Message传过来的数据加了首尾信息文

            int begin=msg.toString().indexOf("[");

            int end=msg.toString().indexOf("]");

            String request=msg.toString().substring(begin, end+1);

             

            JSONArray jsonArray = new JSONArray(request);

            for (int i = 0; i < jsonArray.length(); i++) {

                JSONObject item = jsonArray.getJSONObject(i); // 每条记录又由几个Object对象组成

                int id = item.getInt("Id"); // 获取对象对应的值{"Id":1,"Name":"Tomato Soup","Category":"Groceries","Price":1.0}

                String name = item.getString("Name");

                String categroy=item.getString("Category");

                String price = item.getString("Price");

 

                map = new HashMap<String, String>(); // 存放到MAP里面

                map.put("id", id + "");

                map.put("name", name);

                map.put("Category", categroy);

                map.put("Price", price);

                list.add(map);

            }

            for (Map<String, String> list2 : list) {

                String id = list2.get("id");

                String name = list2.get("name");

                Log.i("info", "id:" + id + " | name:" + name);

            }

        } catch (JSONException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

        return list;

    }

     

/*

 * 自定义菜单栏

 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)

 */

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.

        getMenuInflater().inflate(R.menu.main, menu);

        return true;

    }

     

}

 xml中的内容

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context=".MainActivity" >

 

    <ListView

        android:id="@+id/sjlb"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_centerVertical="true" >

    </ListView>

 

</RelativeLayout>

 AndroidManifest.xml给网络访问权限

?

1

<uses-permission android:name="android.permission.INTERNET" />

 

 

运行效果图:

  Message.obtain()方法具有多个重载方法,大致可以分为为两类,一类是无需传递Handler对象,对于这类的方法,当填充好消 息后,需要调用Handler.sendMessage()方法来发送消息到消息队列中。第二类需要传递一个Handler对象,这类方法可以直接使用 Message.sendToTarget()方法发送消息到消息队列中,这是因为在Message对象中有一个私有的Handler类型的属性 Target,当时obtain方法传递进一个Handler对象的时候,会给Target属性赋值,当调用sendToTarget()方法的时候,实 际在它内部还是调用的Target.sendMessage()方法。

  在Handler中,也定义了一些发送空消息的方法,如:sendEmptyMessage(int what)、sendEmptyMessageDelayed(int what,long delayMillis),看似这些方法没有使用Message就可以发送一个消息,但是如果查看源码就会发现,其实内部也是从 Message.obtain()方法中获取一个Message对象,然后给属性赋值,最后使用sendMessage()发送消息到消息队列中。

  Handler中,与Message发送消息相关的方法有:

  • Message obtainMessage():获取一个Message对象。
  • boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
  • boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。
  • boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。
  • boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。
  • void removeMessage():从消息队列中移除一个未响应的消息。

对Message感兴趣的可以参考:http://www.cnblogs.com/shirley-1019/p/3557800.html

本文的很多知识都是参考的次博文,我只对自己需要的部分做了修改

Android开发中,调用WebAPI来读取数据通常涉及到网络请求,最常用的是通过HttpURLConnection、OkHttp、Retrofit等库。以下是基本步骤: 1. **添加依赖**:对于较新的项目,推荐使用Retrofit或Volley这样的高层次库,它们能简化网络请求。 - Retrofit:在build.gradle文件中添加`implementation 'com.squareup.retrofit2:retrofit:2.x.y'` 和 `implementation 'com.squareup.retrofit2:converter-gson:2.x.y'`,`x.y`替换实际版本号。 - OkHttp:`implementation 'com.squareup.okhttp3:okhttp:4.x.y'`,同样替换版本号。 2. **创建接口**:定义一个Java接口,包含HTTP请求方法(GET、POST等),并声明返回的数据类型。例如: ```java public interface ApiService { @GET("api/data") Call<List<DataModel>> fetchData(); } ``` 3. **创建客户端**:使用Retrofit实例化一个实现了ApiService接口的实例,然后发起请求: ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://example.com") // 替换为实际的API地址 .addConverterFactory(GsonConverterFactory.create()) .build(); ApiService service = retrofit.create(ApiService.class); Call<List<DataModel>> call = service.fetchData(); ``` 4. **处理响应**:调用`enqueue()`方法异步发送请求,然后处理`Call`对象: ```java call.enqueue(new Callback<List<DataModel>>() { @Override public void onResponse(Call<List<DataModel>> call, Response<List<DataModel>> response) { if (response.isSuccessful()) { List<DataModel> data = response.body(); // 处理获取到的数据 } else { handleFailure(response.errorBody()); } } @Override public void onFailure(Call<List<DataModel>> call, Throwable t) { handleFailure(t.getMessage()); } private void handleFailure(Throwable error) { // 处理错误情况 } }); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值