✏️
Android应用开发
  • 简介
  • 目录
  • 第一章 熟悉Android Studio开发环境
    • 第一个项目——Hello World!
    • 第二个项目——计数器
    • 第三个项目——新闻阅读器
  • 第二章 活动、组件及布局
    • 第四个项目——消息发送
    • 第五个项目——计数器2
    • 第六个项目——登陆界面
    • 第七个项目——新闻列表
  • 第三章 数据持久化
    • 第八个项目——登陆界面2
    • 第九个项目——新闻列表2
  • 第四章 内容提供器与数据共享
    • 第十个项目——音乐播放器1
  • 第五章 多媒体与服务
    • 第十一个项目——音乐播放器2
    • 第十二个项目——音乐播放器3
  • 第六章 绑定服务与自定义广播
    • 第十三个项目——音乐播放器4
  • 第七章 网络与多线程
    • 第十四个项目——新闻列表3
  • 附录 扩展项目
    • 第十五个项目——使用ViewHolder提升ListView显示性能
    • 第十六个项目——使用SwipeRefreshLayout实现下拉刷新
    • 第十七个项目——使用接口回调实现删除ListView列表中的Item数据
    • 第十八个项目——使用RecyclerView显示列表数据
    • 第十九个项目——使用Fragment组织UI页面
    • 第二十个项目——使用CursorAdapter进行数据绑定及渲染
    • 第二十一个项目——使用Loader实现异步数据加载操作
    • 第二十二个项目——使用TimerTask及Handler实现秒表计时器
    • 第二十三个项目——使用AsyncTask进行网络异步请求
Powered by GitBook
On this page
  • 实验目的
  • 实验要求
  • 实验内容
  • 步骤一,打开第十四个项目
  • 步骤二,定义NewsListAsyncTask类
  • 步骤三,修改MainActivity初始化数据代码
  • 步骤四,编译并运行项目
  • 实验小结

Was this helpful?

  1. 附录 扩展项目

第二十三个项目——使用AsyncTask进行网络异步请求

Previous第二十二个项目——使用TimerTask及Handler实现秒表计时器

Last updated 4 years ago

Was this helpful?

实验目的

  • 掌握AsyncTask进行异步任务处理的基本流程;

  • 能使用AsyncTask、Okhttp网络库实现网络数据异步请求处理;

实验要求

  • 自定义AsyncTask子类NewsListAsyncTask,重载doInBackground()及onPostExecute()方法;

实验内容

在中,使用okhttp网络请求库调用天行数据API新闻接口,并在ListView中显示获取的新闻列表数据。在MainActivity中通过定义okhttp3.Callback回调接口,并使用Call.enqueue(okhttp3.Callback)异步形式进行网络请求。

但由于Android不允许在主线程中进行网络请求操作,因此实际的网络请求必须在子线程中完成。在子线程中获取天行数据API新闻接口返回数据后,再通过runOnUiThread()方法将获取的数据更新。这些步骤看起来十分复杂繁琐,且大量代码堆叠在MainActivity活动中,如果一个活动需要请求多个网络接口的数据,那么会造成该活动的代码十分臃肿也不利于维护。

为了解决上述问题,Android框架提供了AsyncTask异步任务类,AsyncTask封装了子线程创建, 子线程与UI主线程的通信等操作,对开发人员暴露以下方法,以方便使用:

  • void onPreExecute(),进行异步任务操作前的回调接口,用户可在该接口中进行任务初始化的必要操作,该方法将在UI主线程中执行;

  • Result doInBackground(Params... params),AsyncTask将doInBackground()方法中的代码放入子线程中执行,Params为子类化AsyncTask时指定的第一个模板参数类型,用于在UI主线程传递参数给doInBackground()方法。Result为子类化AsyncTask时指定的第三个模板参数类型,表示子线程返回给主线程的数据类型;

  • void onProgressUpdate(Progress ),可以doInBackground()方法内通过publishProgress()更新异步任务进度,onProgressUpdate()方法将在主线程中被执行。通常Progress类型选择为Integer类型;

  • void onPostExecute(Result result),当子线程返回后,该方法将在UI主线程中被执行,其参数Result为doInBackground()方法的返回值。

通过上述几个方法,AsyncTask封装了子线程启动、通过Handler进行主线程与子线程通信等操作,暴露给调用者的仅仅是AsyncTask的对象构造以及execute(Params... params)方法。从而使得AsyncTask对象的调用者代码更加简洁,也使得代码更便于维护。

步骤一,打开第十四个项目

步骤二,定义NewsListAsyncTask类

定义NewsListAsyncTask类继承至AsyncTask类,并重写doInBackground()方法及onPostExecute()方法,具体代码如下所示。

doInBackground()方法中的代码是原项目中放在子线程执行网络请求的代码,其参数为Integer类型的变长参数,其实参为实例化NewsListAsyncTask对象后调用execute()方法所传递进来的参数。在此我们将请求天行数据API所需的频道、每页新闻数量、当前页码三个参数传递给doInBackground()方法。

在介绍AsyncTask异步任务类的几个方法时,我们说过doInBackground()方法的代码将在子线程中执行,而当doInBackground()方法返回后,onPostExecute()方法将被在主线程中执行,而该方法的参数实际对应了doInBackground()方法的返回值。也就是说,doInBackground()方法将子线程处理后的结果交由onPostExecute()方法在主线程中进行进一步处理。其实现机制是Handler异步消息通信机制。

最后NewsListAsyncTask的构造函数接收了两个参数,分别是Context上下文以及NewsAdapter适配器类,主要为了便于在onPostExecute()等方法中更新适配器的数据。

public class NewsListAsyncTask 
        extends AsyncTask<Integer, Void, String> {

    private NewsAdapter adapter;
    private Context context;

    public NewsListAsyncTask(Context context, NewsAdapter adapter) {
        this.context = context;
        this.adapter = adapter;
    }

    @Override
    protected String doInBackground(Integer... integers) {

        Integer col = integers[0];
        Integer newsNum = integers[1];
        Integer page = integers[2];

        NewsRequest requestObj = new NewsRequest();

        requestObj.setCol(col);
        requestObj.setNum(newsNum);
        requestObj.setPage(page);
        String urlParams = requestObj.toString();

        Request request = new Request.Builder()
                .url(Constants.GENERAL_NEWS_URL + urlParams)
                .get().build();
        try {
            OkHttpClient client = new OkHttpClient();
            Response response = client.newCall(request).execute();

            if (response.isSuccessful()) {
                String body = response.body().string();
                return body;
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(String body) {
        Gson gson = new Gson();
        Type type = new TypeToken<BaseResponse<List<News>>>() {}.getType();

        BaseResponse<List<News>> newsListResponse = gson.fromJson(body, type);
        for (News news:newsListResponse.getData()) {
            adapter.add(news);
        }

        adapter.notifyDataSetChanged();
        super.onPostExecute(body);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
}

步骤三,修改MainActivity初始化数据代码

完成上述步骤后,最后需要修改MainActivity活动类中进行数据刷新的refreshData()方法的代码,具体代码如下所示。 在该方法中仅需要实例化NewsListAsyncTask对象并调用其execute()方法传递相应的天行数据API新闻频道、每页新闻数量、当前新闻页码参数即可。

public class MainActivity extends AppCompatActivity {

    ...
    private void refreshData(final int page) {
        new NewsListAsyncTask(MainActivity.this, adapter)
                .execute(new Integer[]{
                        mCols[mCurrentColIndex],
                        Constants.NEWS_NUM,
                        page});
    }

    ...
}

步骤四,编译并运行项目

实验小结

通过本次实验,你应该掌握了如下知识内容:

  • 掌握子类化AsyncTask异步任务进行异步操作的基本流程;

  • 能使用AsyncTask实现异步任务处理;

打开,在该项目的基础上完成本次实验。

在doInBackground()方法内,还需构造Request请求对象、OkHttpClient请求客户端对象等,最后通过Call.execute()方法执行同步网络请求。 这一点与中使用OkHttpClient的Call.enqueue(Callback)进行异步网络请求略微不同。Call.execute()方法会阻塞当前线程直到Request对应的Response可用为止,因此可以直接在doInBackground()方法中获取Response对象,判断其返回状态再获取其ResponseBody响应体。并将ResponseBody响应体转换为String类型作为doInBackground()方法的返回值。

在onPostExecute()方法中,需要将ResponseBody响应体的数据进行解析,并转换成对应的News新闻对象,并添加至ListView绑定的NewsAdapter适配器中。该方法的代码与中okhttp3.Callback接口中的onResponse()方法的代码作用相同。只不过由于onPostExecute()方法本身在UI主线程中执行,因此不在需要runOnUiThread()方法将其切入UI主线程中。

而原来在MainActivity中定义的okhttp3.Callback接口可直接删除。如果仔细对比当前MainActivity的代码与的代码,你会发现使用AsyncTask异步任务类封装原来的网络请求代码后,MainActivity的代码更加简洁清晰,也更利于代码维护。如果需要多个接口请求,则再子类化AsyncTask实现特定的业务即可。

编译并运行项目,其执行效果相同。

第十四个项目
第十四个项目
第十四个项目
第十四个项目
第十四个项目
第十四个项目