通过上述解释可以清楚的知道,在处理耗时任务时,为了避免出现ANR,必须要将这些耗时任务放在子线程中去执行。但是,如果子线程中得到结果与UI控件关联,这时候如果直接在子线程中更新UI,Android系统同样会弹出ANR提示,因为UI控件是不安全的,为什么说Android的UI控件是不安全的?
首先得理解什么是线程安全,什么是线程不安全。线程安全就是在多个线程共享一个数据时,当某一个线程访问该数据时,通过加锁机制进行保护,不允许其它线程进行访问,直至访问结束后,才会安排其它线程进行访问,这样会使得被共享的数据在某一段时间内只会被一个线程访问,不会导致数据不一致,但是,可以看出通过这种线程安全的方式肯定会影响数据的访问效率。线程不安全是不采用加锁机制,允许多个线程同时访问共享数据,这种情况下就会导致共享数据被任意更改,但是在线程不安全情况下,数据的访问效率要高于在线程安全情况下。
考虑到系统的整体性能,Android选择了线程不安全,为了进一步避免线程不安全带来的不安全问题,Android系统规定只能在UI线程中更新UI控件的状态。所以,在子线程中更新UI控件状态,Android系统会弹出ANR。
总结上述可以得知会有以下两种情况导致出现ANR:
在很多情况下,子线程的运行结果与UI控件的状态有关。这时候,如果想要在子线程中达到更新UI控件的目的,同时又的避免出现ANR,就需要采用异步消息处理机制,这是Android特有的一种线程间通信机制。
工作原理:Android的异步消息处理机制就是Handler消息机制。这种消息机制简要分为如下几个部分:
//Handler的构造方法
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
//如果没有创建Looper,则报异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
//主线程的main方法
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("" );
//创建Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
//Looper的构造方法
private Looper(boolean quitAllowed) {
//通过这里可以看出构造了一个消息队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//Handler发送消息后最终总是会调用的方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
//调用MessageQueue的enqueueMessage方法将消息插入消息队列
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
//Looper提供的轮询方法
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
//调用MessageQueue提供的next方法
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
//下面方法用于处理轮询到的消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
使用案例:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView textView;
Button button0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button0 = (Button)findViewById(R.id.send);
textView = (TextView) findViewById(R.id.display);
button0.setOnClickListener(this);
}
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
int all = msg.arg1+msg.arg2;
textView.setText(""+all);
break;
}
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
message.arg1 = 1000;
message.arg2 = 1000;
handler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
}
工作原理:AsyncTask是一个抽象类,其对Handler和Thread进行了封装,方便使用者使用,AsyncTask会在后台执行执行相应的任务并将执行结果发送给主线程,从而根据执行结果在主线程中更新UI控件的状态。。在使用时候,需要创建子类并继承它,这个类提供四个非常关键的方法,分别是onPreExecute
,doInBackground
,onProgessUpdata
,onPostExecute
,在继承的时候需要重新实现这几个方法。此外,在继承AsyncTask的时候,可以为AsyncTask指定三个泛型参数,这三个泛型参数分别如下:
在继承类时,要根据具体任务的逻辑重写上述提到的几个方法,下面对这几个方法进行分析:
使用案例:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
Button button0;
ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button0 = (Button)findViewById(R.id.startTask);
button0.setOnClickListener(this);
progressBar = (ProgressBar) findViewById(R.id.progress);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.startTask:
DownLoadTask downLoadTask = new DownLoadTask();
downLoadTask.execute();
break;
default:
break;
}
}
public class DownLoadTask extends AsyncTask<String, Integer,String> {
@Override
protected String doInBackground(String... strings) {
int num = 0;
while (num<100) {
num++;
try {
Thread.sleep(1);
publishProgress(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
@Override
protected void onProgressUpdate(Integer... values) {
progressBar.setProgress(values[0]);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
}
}