ANR是什麼?怎樣避免和解決ANR
Application Not Responding,即應用無響應
出現的原因有三種:
a)KeyDispatchTimeout(5 seconds)主要類型按鍵或觸摸事件在特定時間内無響應
b)BroadcastTimeout(10 seconds)BoradcastReceiver在特定的時間内無法處理
c)ServiceTimeout(20 seconds)小機率類型Service在特定的時間内無法處理完成
避免ANR最核心的一點就是在主線程減少耗時操作。通常需要從那個以下幾個方案下手:
a)使用子線程處理耗時IO操作
b)降低子線程優先級,使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設定優先級,否則仍然會降低程式響應,因為預設Thread的優先級和主線程相同
c)使用Handler處理子線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程
d)Activity的onCreate和onResume回調中盡量避免耗時的代碼
e)BroadcastReceiver中onReceiver代碼也要盡量減少耗時操作,建議使用intentService處理。intentService是一個異步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止并銷毀Service的問題
AsyncTask使用
異步任務類,比handler更輕量,更适合簡單的異步操作
内部實作了對Thread和Handler的封裝,友善背景線程操作後UI的更新
在用AsyncTask進行UI更新時,不用額外建立Handler,直接用AsyncTask内部封裝好的幾個方法。
三個泛型參數
AsyncTask直接繼承于Object類,位置為android.os.AsyncTask,使用AsyncTask要提供三個泛型參數,作用是控制AsyncTask的子類在執行線程任務時每個階段的傳回類型
方法 描述
Params 開始異步任務執行時傳入的參數類型,對應execute()中傳遞的參數
Progress 異步任務執行過程中,傳回任務進度值的類型
Result 異步任務執行完成後,傳回的結果類型,與doInBackground()的傳回值類型保持一緻
核心方法:
execute(),執行AsyncTask
cancel(true),取消AsyncTask
isCancelled(),判斷是否被取消
執行順序onPreExecute->doInBackground->onProgressUpdate->onPostExecute(在執行被取消時調用onCancelled)
重寫方法 描述
doinBackground() 在子線程執行(異步任務)耗時操作,執行完後有傳回結果
onPreExecute() 在doinBackground執行前
onProgressUpdate() 在UI線程中更新doinBackground的執行進度
onPostExecute() 接收doinBackground執行完後的傳回結果,并顯示到界面上去 ,如果執行被取消則無法調用
onCancelled() 在執行被取消時調用
用例
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class ProgressActivity extends AppCompatActivity {
//初始化控件
private TextView text;
private ImageView start,cancel;
private ProgressBar progressBar;
//初始化一個AsyncTask子類
private ProgressTask pTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress);
progressBar=findViewById(R.id.progress_bar);
start=findViewById(R.id.start);
cancel=findViewById(R.id.cancel);
text=findViewById(R.id.text);
//執行個體一個AsyncTask子類
pTask=new ProgressTask();
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//執行異步任務
pTask.execute();
}
});
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消異步任務
pTask.cancel(true);
}
});
}
//子類繼承AsyncTask
class ProgressTask extends AsyncTask<Void,Integer,String>{
//執行doInBackground之前的操作
@Override
protected void onPreExecute() {
super.onPreExecute();
text.setText("加載中");
}
//執行任務中的耗時操作,傳回線程任務的執行結果
@Override
protected String doInBackground(Void... voids) {
//模拟耗時操作
try {
for (int i=1;i<=100;i++){
publishProgress(i);
Thread.sleep(50);
}
return "加載完畢";
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
//在主線程中顯示線程任務的執行進度,在doInBackground方法中調用publishProgress方法則觸發該方法
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressBar.setProgress(values[0]);
text.setText("加載..."+values[0]+"%");
}
//接受線程任務的執行結果,将執行結果顯示在界面上
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(s!=null){
text.setText(s);
}
//異步任務每次隻能用一次,如果還想用,再執行個體一次AsyncTask子類
pTask=new ProgressTask();
}
//取消cancel異步任務時觸發
@Override
protected void onCancelled() {
super.onCancelled();
text.setText("已取消");
progressBar.setProgress(0);
//異步任務每次隻能用一次,如果還想用,再執行個體一次AsyncTask子類
pTask=new ProgressTask();
}
}
}