1.线程
线程,是程序执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。而在单个程序中同时运行多个线程完成不同的工作,称为多线程。
2.Android线程
应用启动时,系统会为应用创建一个名为“主线程”的执行线程。此线程非常重要,因为它负责将事件分派给相应的用户界面小部件,其中包括绘图事件。此外,它也是应用与Android UI 工具包组件(来自
android.widget
和android.view
软件包的组件)进行交互的线程。因此,主线程有时也称为UI 线程。系统不会为每个组件实例创建单独的线程。运行于同一进程的所有组件均在 UI 线程中实例化,并且对每个组件的系统调用均由该线程进行分派。因此,响应系统回调的方法(例如,报告用户操作的
onKeyDown()
或生命周期回调方法)始终在进程的 UI 线程中运行。例如,当用户触摸屏幕上的按钮时,应用的 UI 线程会将触摸事件分派给小部件,而小部件反过来又设置其按下状态,并将失效请求发布到事件队列中。UI 线程从队列中取消该请求并通知小部件应该重绘自身。
在应用执行繁重的任务以响应用户交互时,除非正确实现应用,否则这种单线程模式可能会导致性能低下。具体地讲,如果 UI 线程需要处理所有任务,则执行耗时很长的操作(例如,网络访问或数据库查询)将会阻塞整个 UI。一旦线程被阻塞,将无法分派任何事件,包括绘图事件。从用户的角度来看,应用显示为挂起。更糟糕的是,如果 UI线程被阻塞超过几秒钟时间(目前大约是 5 秒钟),用户就会看到一个让人厌烦的“应用无响应”(ANR)对话框。如果引起用户不满,他们可能就会决定退出并卸载此应用。
此外,Android UI 工具包并非线程安全工具包。因此,您不得通过工作线程操纵 UI,而只能通过 UI 线程操纵用户界面。因此,Android 的单线程模式必须遵守两条规则:
1.不要阻塞 UI 线程
2.不要在 UI 线程之外访问 Android UI 工具包
根据上述单线程模式,要保证应用 UI 的响应能力,关键是不能阻塞 UI 线程。如果执行的操作不能很快完成,则应确保它们在单独的线程(“后台”或“工作”线程)中运行。线程是程序员进阶的一道重要门槛。对于移动开发者来说,“将耗时的任务放到子线程去执行,以保证UI线程的流畅性”是线程编程的第一金科玉律。要通过工作线程处理更复杂的交互,可以考虑在工作线程中使用
Handler
处理来自 UI 线程的消息。当然,最好的解决方案或许是扩展AsyncTask
类,Android提供的该类简化了与UI 进行交互所需执行的工作线程任务。
3.Android--AsyncTask
AsyncTask
public abstract class AsyncTask
extends Object
java.lang.Object
↳ android.os.AsyncTask<Params, Progress, Result>
AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。使用起来简便快捷,过程可控。
AsyncTask定义的三种泛型类型 Params,Progress和Result:
1.Params 启动任务执行的输入参数,比如HTTP请求的URL。
2.Progress 后台任务执行的百分比。
3.Result 后台执行任务最终返回的结果,比如String。
使用过AsyncTask必须重写doInBackground(Params…)方法:
doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
有必要的话还得重写其他方法,但不是必须的:
onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回
onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
onPreExecute(Result) 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
onCancelled() 用户调用取消时,要做的操作
其中执行顺序为:onPreExecute(),doInBackground(Params...), onProgressUpdate(Progress...), onPostExecute(Result)
使用AsyncTask类,尽管方便快捷,但也有很多限制:
1.Task的实例必须在UI thread中创建;
2.execute方法必须在UI thread中调用;
3.不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这些方法;
4.AsyncTask只能被执行一次,多次调用时将会出现异常;
4.Code
如下图,当在UI线程中使用URI连接下载数据显示时将会出现错误:
使用AsyncTask类改进如下:
并在UI线程中启动如下:public class UriTestTask extends AsyncTask<URL, Void, String> { @Override protected String doInBackground(URL... urls) { URL searchUrl = urls[0]; String SearchResults = null; try { SearchResults = NetworkUtils.getResponseFromHttpUrl(searchUrl); } catch (IOException e) { e.printStackTrace(); } return SearchResults; } @Override protected void onPostExecute(String s) { if (s != null && !s.equals("")) { mSearchResultsTextView.setText(s); } } }
App运行正常如下图:
@Override
public boolean onOptionsItemSelected(MenuItem item) { int itemThatWasClickedId = item.getItemId(); if (itemThatWasClickedId == R.id.action_search) { makeSearchQuery(); return true; } return super.onOptionsItemSelected(item); } private void makeSearchQuery() { String queryStr = mSearchBoxEditText.getText().toString(); URL searchUrl = NetworkUtils.buildUrl(queryStr); mUrlDisplayTextView.setText(searchUrl.toString()); new UriTestTask().execute(searchUrl); }
5.参考
AsyncTask:https://developer.android.com/reference/android/os/AsyncTask.html
进程与线程:https://developer.android.com/guide/components/processes-and-threads.html