在Android应用程序中,当我们需要与可能需要时间的外部资源(例如从外部API或数据库获取数据)进行交互时,我们希望主UI保持交互并阻止UI线程在长时间运行的进程中运行很活跃。另请注意,默认情况下,不允许在Android的UI线程中运行网络任务。
如果主线程用于获取外部数据,则在获取数据时主UI将不会保持交互,并且如果数据获取过程遇到异常,则可能显示异常行为。在这种情况下,android的异步任务变得很方便,尤其是使用后台线程更新UI的一部分。
异步任务是将主线程的工作卸载到某个后台线程的几种方法之一。虽然AsyncTask
不是唯一的选择,但这是一个简单而且非常常见的选择。
在开始之前,我想访问谷歌的开发者页面包含的信息AsyncTask
在https://developer.android.com/reference/android/os/AsyncTask审议有关实施一些内容AsyncTask
秒。
这个AsyncTask
班是一个abstract
班级。实现通常是在UI线程上运行的类的子类。(AsyncTask
即子类)的实现将覆盖至少一种方法,通常是两种方法。
执行异步任务时,任务将执行4个步骤,如Android开发人员页面中所述,网址为https://developer.android.com/reference/android/os/AsyncTask:
-
onPreExecute
,在执行任务之前在UI线程上调用。此步骤用于设置任务,例如通过在用户界面中显示微调器。 -
doInBackground(Params...
,在完成执行后立即在后台线程上调用。此步骤用于执行可能需要很长时间的后台计算。异步任务的参数将传递给此步骤。计算结果必须由此步骤返回,并将传递回最后一步。此步骤还可用于发布一个或多个进度单位。这些值发布在UI线程中steponProgressUpdate(Progress...)
-
onProgressUpdate(Progress...
,在调用publishProgress(Progress
...步骤后在UI线程上调用。执行的时间是不确定的。此方法用于在后台计算仍在执行时显示用户界面中的任何形式的进度。例如,它可用于为进度条设置动画或在文本字段中显示日志。 -
onPostExecute(Result)
,在后台计算完成后在UI线程上调用。背景计算的结果作为参数传递给该步骤。
我将通过代码来说明工作机制。代码来自我为Udacity的Android纳米学位课程所做的顶点项目。完整代码可在https://github.com/benktesh/Capstone-Project获得。在本演示中,我使用代码块中显示的轻量级代码,如下所示:
package benktesh.smartstock;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.util.ArrayList;
import benktesh.smartstock.Model.Stock;
import benktesh.smartstock.UI.CommonUIHelper;
import benktesh.smartstock.UI.StockDetailActivity;
import benktesh.smartstock.Utils.MarketAdapter;
import benktesh.smartstock.Utils.NetworkUtilities;
import benktesh.smartstock.Utils.PortfolioAdapter;
import benktesh.smartstock.Utils.SmartStockConstant;
public class MainActivity extends AppCompatActivity implements
MarketAdapter.ListItemClickListener, PortfolioAdapter.ListItemClickListener {
private static final String TAG = MainActivity.class.getSimpleName();
CommonUIHelper mCommonUIHelper;
ArrayList<Stock> mMarketData;
private Toast mToast;
//The following are for market summary
private MarketAdapter mAdapter;
private RecyclerView mMarketRV;
private ProgressBar spinner;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
spinner = findViewById(R.id.progressbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent Email = new Intent(Intent.ACTION_SEND);
Email.setType(getString(R.string.label_emailtype));
Email.putExtra(Intent.EXTRA_EMAIL,
new String[]{getString
(R.string.label_developer_contat_email)}); //developer 's email
Email.putExtra(Intent.EXTRA_SUBJECT,
R.string.label_feedback_subject); // Email 's Subject
Email.putExtra(Intent.EXTRA_TEXT,
getString(R.string.label_address_developer) + ""); //Email 's Greeting text
startActivity(Intent.createChooser(Email, getString(R.string.label_send_feedback)));
}
});
if (mCommonUIHelper == null) {
mCommonUIHelper = new CommonUIHelper(this);
}
mMarketRV = findViewById(R.id.rv_market_summary);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mMarketRV.setLayoutManager(layoutManager);
mMarketRV.setHasFixedSize(true);
mAdapter = new MarketAdapter(mMarketData, this);
mMarketRV.setAdapter(mAdapter);
LoadView();
}
private void LoadView() {
Log.d(TAG, "Getting Market Data Async");
new NetworkQueryTask().execute(SmartStockConstant.QueryMarket);
}
private void MakeToast(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
public boolean onCreateOptionsMenu(Menu menu) {
return mCommonUIHelper.ConfigureSearchFromMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
if (mCommonUIHelper.MakeMenu(item)) return true;
return super.onOptionsItemSelected(item);
}
@Override
public void onListItemClick(Stock data) {
if (mToast != null) {
mToast.cancel();
}
Intent intent = new Intent(this.getApplicationContext(), StockDetailActivity.class);
intent.putExtra(SmartStockConstant.ParcelableStock, data);
Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
startActivity(intent, bundle);
}
/*
This is an async task that fetches data from network and new data is applied to adapter.
Also makes a long toast message when fails to retrieve information from the network
It takes void, void and returns ArrayList<?>
*/
class NetworkQueryTask extends AsyncTask<String, Integer, ArrayList<Stock>> {
private String query;
@Override
protected void onPreExecute() {
if (spinner != null) {
spinner.setVisibility(View.VISIBLE);
}
}
@Override
protected ArrayList<Stock> doInBackground(String... params) {
query = params[0];
ArrayList<Stock> searchResults = null;
try {
searchResults = NetworkUtilities.getStockData(getApplicationContext(), query);
for (int i = 0; i <= 100; i = i + 25) {
Thread.sleep(500);
publishProgress(i);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
return searchResults;
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
Toast.makeText(getApplicationContext(), "Progress: " +
progress[0] + "(%)", Toast.LENGTH_SHORT).show();
}
@Override
protected void onPostExecute(ArrayList<Stock> searchResults) {
super.onPostExecute(searchResults);
if (searchResults != null && searchResults.size() != 0) {
mAdapter.resetData(searchResults);
}
if (spinner.getVisibility() == View.VISIBLE) {
spinner.setVisibility(View.GONE);
}
}
}
}
NetworkQueryTask
作为子类实现的类MainActivity
和子类扩展了Android的AsyncTask abstract
类。子类可以定义如下:
private class NetworkQueryTask extends AsyncTask<T1, T2, T3> {...}
的T1
,T2
和T3
是参数的数据类型和他们每个人都有一些特定的含义。
上面定义的任务可以执行如下:
new NetworkAsyncTask().execute(param1);
它param1
的类型与T1
。的类型相同。
将MainActivity
在UI线程运行。' onCreate(..)
'方法确实设置了UI。设置涉及为回收器视图等创建适配器,最后调用a LoadView
()。在LoadView
()方法执行AsyncTask
从网络获取数据和更新视图的适配器。
在这样做的过程中,我们创建了一个NetworkQueryTask
从中扩展的子类AsyncTask
。该类有三个参数string
,Void
和ArrayList<Stock>
。股票是一个存储信息的简单类Stock
。一旦进程开始,我们希望我们可以在doInBackground
(..)方法中看到微调器。
在上面的任务中,这三个参数表示用于的输入参数的类型doInBackground(T1 param1)
,onProgressUpdate(T2 param2)
和onPostExecute(T3 param3)
。当doInBackground
步骤完成执行时,param3
将是doInBackground
步骤的输出,并将成为该onPostExecute(param3)
方法的输入。
子类通常至少覆盖一种方法,最常见的是doInBackground(..)
方法,也包括第二种方法,即onPostExecute()
。该onProgressUpdate
和onPreExecute()
方法是可选的,可以跳过。因此,如果没有关于进度更新的任何事情,那么就不需要覆盖onProgressUpdate
,然后param2
可以Void
在类定义本身中使用类型。例如,假设需要将string
参数传递给doInBackground
()而不需要onProgressUpdate()
方法,并且该onPostExecute()
方法接受一个string
参数,那么类定义将如下所示:
private class NetworkQueryTask extends AsyncTask<String, Void, String> {...}
因此,我们可以说这三个参数分别代表输入doInBackground
,输入onProgressUpdate()
和输入onPostExecute
。输出的参数类型doInBackground
与输入相同onPostExectute()
。此外,如果async
任务是作为火灾而忘记诸如触发某事,那么所有参数都可以void
。例如,在这种情况下,子类的定义如下所示:
private class NetworkQueryTask extends AsyncTask<Void, Void, Void> {...}
上面的类执行如下:
new NetworkAsyncTask().execute();
AsyncTask
s不知道应用程序中的其他活动,因此必须在销毁活动时正确处理。因此,AsycnTask
不适合长时间运行的操作,因为如果应用程序在后台运行,当Android的终止调用该应用程序AsyncTask
时,AsyncTask
不被打死,我们必须管理的如何处理的结果做处理AsyncTask
。因此,AsyncTask
s在获取不长时间运行的数据时很有用。还有其他替代AyscTask
它们IntentServices
,Loader
和JobScheduler
许多基于Java的实现。