Android 基礎之 Handler
Android 是單執行緒模型,但是僅僅靠主執行緒是不夠的,比如在主執行緒中進行網路請求、下載圖片、下載視訊等耗時操作時,就會造成阻塞,使用者用起來會很不舒服。所以,除了 UI 的更新外,一些耗時的操作需開啟子執行緒來處理。主執行緒和子執行緒之間需要資料交換等通訊,子執行緒和子執行緒之間同樣也需要通訊。
目前執行緒中的通訊是藉助 Handler 實現的,但 Handler 的作用不僅限於執行緒間通訊,還有延時啟動 Runnable,有一點需要說明:一個執行緒 (Thread)對應一個 Looper,一個 Looper 對應一個訊息佇列 (MessageQueue) ,一個訊息佇列(MessageQueue )中可以包含多條 Message 訊息,一個執行緒中可以有多個 Handler 。
Handler
Handler 是一個訊息分發物件。當建立一個 Handle r時,會與建立他的執行緒和執行緒的訊息佇列繫結,從這時候開始,Handler 會向訊息佇列傳送 Message ,並且在 Message 從訊息佇列出來時對Message 進行處理。
Handler 類包含如下方法用於傳送、處理訊息:
- handleMessage(Message msg):處理訊息的方法。
- obtainMessage():獲取訊息。
- sendEmptyMessage(int what):傳送空訊息。
- sendMessage(Message msg):立即傳送訊息。
- sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒後傳送訊息。
- sendEmptyMessageDelayed(int what, long delayMillis):指定多少毫秒後傳送空訊息。
Message
Handler接收和處理的訊息物件。
- 2個整型數值:輕量級儲存int型別的資料。
- 1個Object:任意物件。
- replyTo:執行緒通訊時使用。
- what:使用者自定義的訊息碼,讓接收者識別訊息。
MessageQueue
Message的佇列
- 採用先進先出的方式管理Message。
- 每一個執行緒最多可以擁有一個。
Looper
訊息泵,是 MessageQueue 的管理者,會不斷從 MessageQueue 中取出訊息,並將訊息分給對應的 Handler 處理。
- 每個執行緒只有一個 Looper。
- Looper.prepare():為當前執行緒建立 Looper物件。
- Looper.myLooper():可以獲得當前執行緒的 Looper物件。
- Handler:能把訊息傳送給 MessageQueue,並負責處理 Looper 分給它的訊息。
Android 訊息機制的執行流程
在子執行緒執行完耗時操作,當Handler傳送訊息時,將會呼叫MessageQueue.enqueueMessage,向訊息佇列中新增訊息。當通過Looper.loop開啟迴圈後,會不斷地從執行緒池中讀取訊息,即呼叫MessageQueue.next,然後呼叫目標Handler(即傳送該訊息的Handler)的dispatchMessage方法傳遞訊息,然後返回到Handler所線上程,目標Handler收到訊息,呼叫handleMessage方法,接收訊息,處理訊息。
MessageQueue,Handler和Looper三者之間的關係
每個執行緒中只能存在一個Looper,Looper是儲存在ThreadLocal中的。主執行緒(UI執行緒)已經建立了一個Looper,所以在主執行緒中不需要再建立Looper,但是在其他執行緒中需要建立Looper。每個執行緒中可以有多個Handler,即一個Looper可以處理來自多個Handler的訊息。 Looper中維護一個MessageQueue,來維護訊息佇列,訊息佇列中的Message可以來自不同的Handler。
Handler 如何去實現傳送和處理訊息
1. 使用 Thread 傳送訊息
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1001) {
mTextView.setText("Star");
}
}
};
private TextView mTextView;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.tv_text);
mButton = findViewById(R.id.btn_send);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
}catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.sendEmptyMessage(1001);
}
}).start();
}
});
}
}
2. 使用Handler下載檔案並更新進度條
- 新增許可權
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- 實現程式碼
public class MainActivity extends AppCompatActivity {
private static final int DOWNLOAD_FILE_CODE = 100001 ;
private static final String DOWNLOAD_URL ="http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk" ;
private static final int DOWNLOAD_FILE_FAILE_CODE = 100002;
private Button StartDownload;
private TextView percent;
private ProgressBar progressBar;
private Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
StartDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//執行下載程式
new Thread(new Runnable() {
@Override
public void run() {
download(DOWNLOAD_URL);
}
}).start();
}
});
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 100001:
//更新進度條
progressBar.setProgress((Integer) msg.obj);
percent.setText(String.valueOf(msg.arg1)+"%");
if (progressBar.getProgress()==100){
Toast.makeText(MainActivity.this, "下載完成", Toast.LENGTH_SHORT).show();
}
break;
case 100002:
Toast.makeText(MainActivity.this, "下載失敗", Toast.LENGTH_SHORT).show();
break;
}
}
};
}
private void download(String AppUrl) {
//例項化URL物件
try {
URL url = new URL(AppUrl);
//例項化一個URLConnection物件
URLConnection conn = url.openConnection();
//獲取下載路徑
String path = Environment.getExternalStorageDirectory()
+ File.separator + "ZerGen" + File.separator;
//建立目錄
File PathName = new File(path);
//如果PathName不存在的話 就建立這個目錄
if(!PathName.exists()){
PathName.mkdir();
}
//有了目錄之後,就需要一個檔名
String ApkName = path+"ZerGen.apk";
//判斷一下這個檔案是否已經存在,存在的話就刪除它
File ApkFile = new File(ApkName);
if(ApkFile.exists()){
ApkFile.delete();
}
//獲取檔案的總長度
int ContentLength = conn.getContentLength();
//獲取輸入流
InputStream in = conn.getInputStream();
byte[] b = new byte[1024];
int DownloadLength = 0; //用於儲存實時下載長度
int len =0;
OutputStream out = new FileOutputStream(ApkName);
while((len = in.read(b))>-1){
out.write(b,0,len);
DownloadLength += len;
//將實時的下載長度傳給UI執行緒
Message message=handler.obtainMessage();
message.what =DOWNLOAD_FILE_CODE;
message.obj = DownloadLength*100/ContentLength;
message.arg1 = DownloadLength*100/ContentLength;
handler.sendMessage(message);
}
} catch (java.io.IOException e) {
downloadfail();
e.printStackTrace();
}
}
private void downloadfail() {
Message message=handler.obtainMessage();
message.what =DOWNLOAD_FILE_FAILE_CODE;
handler.sendMessage(message);
}
private void initView() {
StartDownload = (Button) findViewById(R.id.btn_start);
percent = (TextView) findViewById(R.id.percent);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
}
}
3. 使用 handler實現倒數計時功能
public class SplashActivity extends AppCompatActivity {
private TextView mTime;
private static int TIME = 5;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
TIME--;
mTime.setText(TIME + "s");
if (TIME > 0) {
Message message = mHandler.obtainMessage(1);
mHandler.sendMessageDelayed(message, 1000); // send message
} else {
//跳轉到主介面
goHome();
}
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
mTime = findViewById(R.id.textView);
Message message = mHandler.obtainMessage(1);
mHandler.sendMessageDelayed(message,1000);
}
private void goHome() {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
}
相關文章
- Android之HandlerAndroid
- Android多執行緒基礎 解析Handler機制Android執行緒
- Android Handler機制之Handler 、MessageQueue 、LooperAndroidOOP
- Android開發之HandlerAndroid
- Android 進階 ———— Handler系列之建立子執行緒HandlerAndroid執行緒
- Android原始碼學習之handlerAndroid原始碼
- Android Handler機制之ThreadLocalAndroidthread
- Android基礎之Activity全解析Android
- Android面試之Java 基礎篇Android面試Java
- Android Handler機制之總目錄Android
- [Handler]android-Handler解釋Android
- Android NDK開發之JNI基礎Android
- Android基礎之Java集合框架CollectionAndroidJava框架
- 深入探索Android訊息機制之HandlerAndroid
- Android中HandlerAndroid
- Android Handler原理Android
- Android 基礎之圖片載入(二)Android
- 01.Android之基礎元件問題Android元件
- Android面試之Java基礎筆試題Android面試Java筆試
- Android面試之——數學基礎知識Android面試
- MS(2):Android之基礎知識篇Android
- Android基礎之json資料解析AndroidJSON
- Android 之 “只是想來談談 Handler 機制”Android
- Android Handler機制之記憶體洩漏Android記憶體
- Android基礎Android
- 前端基礎之jQuery基礎前端jQuery
- Handler全家桶之 —— Handler 原始碼解析原始碼
- 再談Handler、Looper、Message、MessageQueue基礎流程分析OOP
- 妥妥的去面試之Android基礎(五)面試Android
- 妥妥的去面試之Android基礎(四)面試Android
- Android知識點回顧之Activity基礎Android
- 妥妥的去面試之Android基礎(六)面試Android
- Android知識點回顧之Service基礎Android
- 【Android繪圖】繪圖之基礎篇(一)Android繪圖
- 妥妥的去面試之Android基礎(二)面試Android
- 妥妥的去面試之Android基礎(三)面試Android
- 妥妥的去面試之Android基礎(一)面試Android
- Gradle系列之Android Gradle基礎配置GradleAndroid