Android线程与并行,AsyncTask(AsyncTask回调方法、AsyncTask泛型参数),ListView空状态设置setEmptyView()方法、加载指示、检查网络状态。

14 篇文章 0 订阅
多任务处理

例如现实当中的例子,预热烤箱的同时切好蔬菜,计算机也遵循同样的理念(用到线程的概念)

NetworkOnMainThreadException

意思是我们正在主线程上执行网络操作,而这是不允许的。Android不允许开发人员在主线程上进行网络请求,因为这会造成应用无响应或者延迟

线程

线程是保存指令序列的容器,例如设备执行的Java代码,Android需要它来安排所有需要在设备硬件上运行的任务,构建Android应用时,应用程序代码默认在主线程上运行,主线程也称为UI线程,主线程可处理绘图操作以响应用户输入、点击、滚动之类。但是主线程一次只能处理一个事件。如果在同一时间发生了多起事件,这些事情会被放到队列中,在这里插入图片描述
到目前为止编写的所有代码都是在主线程中执行,运行程序时,移动到下一个任务前,必须等待前一个任务完成,这大致就是单线程的操作流程。

为什么要使用多线程

在使用手机应用过程中可能遇到这种问题,点击一个按钮,这个按钮会执行一系列的操作,例如从Internet中获取数据、播放音乐等等。但是点击了这个按钮之后,你的程序会出现短时间的停滞,而这段时间里,让人非常不舒服,你不确定是程序崩溃了还是出来其他的问题,但是片刻后,一切有回到你的掌控之中。为什么会出现程序停滞又突然恢复正常的情况?多半是因为你的应用正在尝试处理某些耗时的工作,例如从Internet中获取信息,问题是,如果Internet连接不稳定或者需要传输大量数据,网络请求等工作,则可能会花费很长的时间(我们必须设置web服务器的连接、发送请求、等待响应、然后解析该响应)在你等待整个过程完成时,主线程中不会发生任何事情。所以用户在网络请求发生时点击应用中的其他按钮,在完成网络操作之前,该应用无法对用户做出响应。在这里插入图片描述
如果你的应用在使用过程中请求UI线程处理耗时的工作,则会面临应用停滞的风险,事实上如果你的应用阻塞UI线程超过一段时间,Android将会显示对话框告知用户你的应用未响应,并询问用户是否要将其关闭。在这里插入图片描述
构建安卓应用的一条重要原则:不要阻塞主UI线程在这里插入图片描述
但是Android设备很擅长多任务处理,能够同时运行多个线程,可以相互独立的处理两组或者多组任务。如果用户点击按钮,我们希望响应用户的输入,此问题的解决办法就是在各个独立的后台线程中处理网络请求、数据处理、和音频播放等工作,从而释放UI线程。以便用户在等待所有作业完成的过程中仍可以使用应用进行滚动点击并进行任何操作。在这里插入图片描述
后台线程一次只能进行一项操作,但是如果Internet连接很慢或者不稳定,网络请求需要花费较长时间那也没关系。主线程仍然可以在不影响用户体验的情况下快速处理所有用户输入事件。另外,后台工作完成之后,就没有必要让该线程继续处于活跃状态

AsyncTask(asynchronous异步)

AsyncTask适用于短期一次性任务。如果我们不希望应用从Internet检索数据时UI会卡住几秒钟,那么AsyncTask是非常合适的 在这里插入图片描述
在这里插入图片描述
主线程接收到结果并更新UI只需要很短的时间
在这里插入图片描述

AsyncTask回调方法

AsyncTask不同于我们之前使用的所有其他类,所有其他类比如Activity或者View,在主线程中执行工作。但是AsyncTask类的某些部分在前台主线程的位置运行,其他部分在后台单独的线程上运行。

AsyncTask是一个抽象类,包含一些留空的方法,使开发者可以覆盖和提供自己的程序。
要使用AsyncTask我们需要创建一个自定义的子类,在该应用中我们称之为EarthquakeAsyncTask,子类覆盖方法doInBackground()无论后台线程进行的何种操作都是在doInBackground()方法中进行。 从服务器上获取地震数据后,你可能对UI要进行更新,但是必须是在主线程中更新UI而不是后他线程。

从AsyncTask类文档中,可以看到doInBackground是抽象方法,所以该抽象类的子类必须重写此方法。
在这里插入图片描述
**要使用何种方法在主线程和后台线程直接进行通信呢?**可以通过覆盖AsyncTask中名为onPostExecute()的方法,该方法在主线程上运行。将在后台工作完成后,调用此方法。

我们可以查看doInBackground()返回的结果,这个结果会传入伟onPostExecute()方法的输入
(绿色为主线程运行部分,灰色为后台线程运行部分。)
在这里插入图片描述

要是想对后台任务中的进度显示并进行更新,例如从网上下载图片时会显示图片下载的进度条,在doInBackground()方法中,我们可以定期调用publishProgress方法并传入新的值,然后我们可以覆盖主线程上onProgressUpdate的方法,并用新的进度值来更新UI
在这里插入图片描述

onPreExecute()是Async启动时,主线程上会立即调用它。
然后doingBackground会在后台线程上执行。
执行doingBackground时 如果调用publishProgress会通过一个新的进度值,触发onProgressUpdate回调,这是在主线程上进行的
在这里插入图片描述

AsyncTask泛型参数

AsyncTask<Params, Progress, Result>
AsyncTask具有三个泛型参数: params、progress、result
以QuakeReport应用为例, 在EarthquakeActivity中声明的内部类 EarthquakeAsyncTask继承至AsyncTask(这样便于UI线程和后台线程交互),代码如下:
第一个泛型参数Params,在该例子中是String,这意味着该例子中的doInBackground方法protected ArrayList<Earthquake> doInBackground(String... urls)将接收String输入列表,其中的...标记的意思是可以在这里放入数量可变的参数, 这种参数称为可变参数,只要urls中的数据类型相同就可,输入参数存储在urls的数组变量中,可以使用urls.length检查URL数组的长度,下面的urls[0]代表使用第一个url。
第二个泛型参数是Progress,它是用于测量线程中进行的工作进度的数据类型,又叫进度参数,这里Void表示不在前端UI中显示后台进程进度。
第三个泛型参数是Result, 它是doInBackground方法中的返回值数据类型,可以将其看作来自后台工作的结果数据类型,在该例子中是ArrayList< Earthquake>,从doInBackground中的返回的数组列表被传递到onPostExecute中作为输入参数。
注意:三个泛型数据类型在这里必须是对象数据类型,所以Void的V必须大写

public class EarthquakeActivity extends AppCompatActivity {
	...    
    //将EarthquakeAsyncTask 声明为EarthquakeActivity的内部类,用于在后台线程上执行网络请求
    private class EarthquakeAsyncTask extends AsyncTask<String, Void, ArrayList<Earthquake>> {
        /**
         * 此方法在后台线程上激活(调用),因此我们可以执行
         * 诸如做出网络请求等长时间运行操作。
         *
         * 因为不能从后台线程更新 UI,所以我们仅返回
         * {@link ArrayList<Earthquake>} 对象作为结果。
         */
        @Override
        protected ArrayList<Earthquake> doInBackground(String... urls) {
            //如果不存在任何url或者第一个url为空
            if (urls.length < 1 || urls[0] == null) {
                return null;
            }
            //Perform the HTTP request for earthquake data and process the response
            //Not use hard code Utils.fetchEarthquakeData(USGS.REQUEST.URL) here
            //instead use the urls[0] to access the url in the urls array.
            //pass the USGS.REQUEST.URL into the execute() method when we want execute the async task.
            ArrayList<Earthquake> earthquakes = QueryUtils.fetchEarthquakeData(urls[0]);
            //返回的值会传到UI线程中作为onPostExecute()的输入参数
            return earthquakes;
        }

        /**
         * 此方法是在完成后台工作后,在主 UI 线程上
         * 激活的。
         *
         * 可以在此方法内修改 UI。我们输入 {@link ArrayList<Earthquake>} 对象
         * (该对象从 doInBackground() 方法返回),并更新屏幕上的视图。
         */
        @Override
        protected void onPostExecute(final ArrayList<Earthquake> earthquakes) {

            // Find a reference to the {@link ListView} in the layout
            ListView earthquakeListView = (ListView) findViewById(R.id.list);
            ProgressBar progressBar = (ProgressBar)findViewById(R.id.progress_bar);

            if (earthquakes != null && !earthquakes.isEmpty()) {

                EarthquakeAdapter adapter = new EarthquakeAdapter(EarthquakeActivity.this, earthquakes);
                // Set the adapter on the {@link ListView}
                // so the list can be populated in the user interface
                earthquakeListView.setAdapter(adapter);

                earthquakeListView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
                    @Override
                    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                        Earthquake currentEarthquake = earthquakes.get(i);

                        //set the Intent to each listItem
                        Uri uri = Uri.parse(currentEarthquake.getUrl());
                        Intent websiteIntent = new Intent(Intent.ACTION_VIEW, uri);
                        startActivity(websiteIntent);
                    }
                });
                //if the earthquake array list is not null then set the progressBar divisible
                progressBar.setVisibility(View.GONE);
            }
            else {
                //if the earthquake array list null then set the emptyView visible and progressBar divisible
                earthquakeListView.setEmptyView(findViewById(R.id.empty_view));
                progressBar.setVisibility(View.GONE);

            }

        }
    }
    ...
    ...

ListView为空时设置setEmptyView()方法

在该例子中,在earthquake_activity.xml中添加一个新的TextView与ListView处于同一级别。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    </ListView>

    <TextView
        android:id="@+id/empty_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="No earthquake found"
        android:layout_centerInParent="true"/>

		...

</RelativeLayout>

在EarthquakeActivity.java中,

                //if the earthquake array list null then set the emptyView visible and progressBar divisible
                earthquakeListView.setEmptyView(findViewById(R.id.empty_view));

加载指示

在earthquake_activity.xml中添加ProgressBar,

	...
    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progress_bar"
        android:layout_centerInParent="true"/>
	...

检查网络状态

在EarthquakeActivity中,




    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
		
		...
        //Check the network
        ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        //If there is network and the network can be connected
        if(networkInfo != null && networkInfo.isConnected()) {
            EarthquakeAsyncTask earthquakeAsynctask = new EarthquakeAsyncTask();
            //AsyncTask.execute方法会将参数传到doInBackground中执行
            earthquakeAsynctask.execute(USGS_REQUEST_URL);
        }
		...

在AndroidManifest.xml中添加

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值