當前位置:首頁 » 安卓系統 » handleandroid

handleandroid

發布時間: 2024-07-07 08:02:21

㈠ android 線程可以有幾個handle

android.os.Handler Handler在android里負責發送和處理消息。它的主要用途有: 1)按計劃發送消息或執行某個Runnanble(使用POST方法); 2)從其他線程中發送來的消息放入消息隊列中,避免線程沖突(常見於更新UI線程) 默認情況下,Handler接受的是當前線程下的消息循環實例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定線程),同時一個消息隊列可以被當前線程中的多個對象進行分發、處理(在UI線程中,系統已經有一個Activity來處理了,你可以再起若干個Handler來處理)。在實例化Handler的時候,Looper可以是任意線程的,只要有Handler的指針,任何線程也都可以sendMessage。Handler對於Message的處理不是並發的。一個Looper 只有處理完一條Message才會讀取下一條,所以消息的處理是阻塞形式的(handleMessage()方法里不應該有耗時操作,可以將耗時操作放在其他線程執行,操作完後發送Message(通過sendMessges方法),然後由handleMessage()更新UI)。 android中Handle類的用法 當我們在處理下載或是其他需要長時間執行的任務時,如果直接把處理函數放Activity的OnCreate或是OnStart中,會導致執行過程中整個Activity無響應,如果時間過長,程序還會掛掉。Handler就是把這些功能放到一個單獨的線程里執行,與Activity互不影響。 當用戶點擊一個按鈕時如果執行的是一個常耗時操作的話,處理不好會導致系統假死,用戶體驗很差,而Android則更進一步,如果任意一個Acitivity沒有響應5秒鍾以上就會被強制關閉,因此我們需要另外起動一個線程來處理長耗時操作,而主線程則不受其影響,在耗時操作完結發送消息給主線程,主線程再做相應處理。那麼線程之間的消息傳遞和非同步處理用的就是Handler。 加群討論研究 QQ群:56839077

㈡ android中handle和線程的關系是什麼

作者:李板溪
鏈接:http://www.hu.com/question/24766848/answer/53037579
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。

問題背景,假設你要下載一張美女圖顯示出來。 使用這個問題就可以說明主要的問題了。
好了 上代碼,下載美女圖片,然後顯示在 ImageView 中。 代碼如下:
public class MainActivity extends AppCompatActivity {

public static final String beautyUrl = "http://ww3.sinaimg.cn/large/.jpg";
ImageView mBeautyImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBeautyImageView = (ImageView)findViewById(R.id.beauty);
mBeautyImageView.setImageBitmap(downloadImage(beautyUrl));
}

@Nullable
public Bitmap downloadImage(String urlString){
try {
final URL url = new URL(urlString);
try(InputStream is = url.openStream()){
return BitmapFactory.decodeStream(is);
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

}

然後這樣的一段看似沒有問題的代碼,在 Android 3 以上是會直接報錯的。 主要錯誤原因在
Caused by: android.os.NetworkOnMainThreadException at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
為了保證用戶體驗, Android 在 3.0 之後,就不允許在 主線程(MainThread)即 UI線程 中執行網路請求了。 那怎麼辦呢?
好吧,我們暫不考試 Android 提供的一系統組件及工具類, 用純 Java 的方式來解決這個問題。
在新的線程中下載顯示圖片
在新創建的線程中下載顯示圖片如下:
new Thread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(downloadImage(beautyUrl));
}
}).start();

看起來來錯的樣子,跑起來看看。 啊,又報錯了。
android.view.ViewRootImpl$: Only the original thread that created a view hierarchy can touch its views.
說只能在創建View 層級結構的線程中修改它。 創建它的線程就是主線程。 在別的線程不能修改。 那怎麼辦?
那現在我們遇到的問題是: 下載不能在主線程下載。 更新ImageView 一定要在 主線程中進行。 在這樣的限制下,我們自然而然想去一個解決辦法: 就是在新創建的線程中下載。 下載完成在主線程中更新 ImageView。
但是,怎麼在下載完成之後,將圖片傳遞給主線程呢?這就是我們問題的關鍵了。
線程間通信
我們的通信要求,當下載線程中下載完成時,通知主線程下載已經完成,請在主線程中設置圖片。 純 Java 的實現上面的線程間通信的辦法我暫沒有找到,於是我想到使用 FutureTask 來實現在主線程中等待圖片下載完成,然後再設置。
FutureTask<Bitmap> bitmapFutureTask = new FutureTask<>(new Callable<Bitmap>() {
@Override
public Bitmap call() throws Exception {
return downloadImage(beautyUrl);
}
});
new Thread(bitmapFutureTask).start();
try {
Bitmap bitmap = bitmapFutureTask.get();
mBeautyImageView.setImageBitmap(bitmap);
} catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}

不過這雖然騙過了 Android 系統,但是雖然系統阻塞的現象沒有解決。 例如我在 get() 方法前後設置了輸出語句:
Log.i(TAG,"Waiting Bitmap");
Bitmap bitmap = bitmapFutureTask.get();
Log.i(TAG,"Finished download Bitmap");

設置了下載時間至少 5 秒鍾之後,輸出如下:
06-27 23:30:18.058 21298-21298/com.banxi1988.androiditc I/MainActivity﹕ Waiting Bitmap 06-27 23:30:23.393 21298-21298/com.banxi1988.androiditc I/MainActivity﹕ Finished download Bitmap
讓主線程什麼事做不做,就在那傻等了半天。然後由於 onCreate沒有返回。用戶也就還沒有看到界面。 導致說應用半天啟動不了。。卡死了。。
查閱了一些資料,沒有不用 Android 的框架層的東西而實現在其他進程執行指定代碼的。
而 Android 中線程間通信就得用到 android.os.Handler 類了。 每一個 Handler 都與一個線程相關聯。 而 Handler 的主要功能之一便是: 在另一個線程上安插一個需要執行的任務。
這樣我的問題就通過一個 Handler 來解決了。
於是 onCreate 中的相關代碼變成如下了:
final Handler mainThreadHandler = new Handler();

new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();

看起來很酷的樣子嘛,一層套一層的 Runnable. mainThreadHandler 因為是在 主線程中創建的, 而 Handler創建時,綁定到當前線程。 所以 mainThreadHandler 綁定到主線程中了。
當然 Android 為了方便你在向主線程中安排進操作,在 Activity類中提供了 runOnUiThread 方法。 於是上面的代碼簡化為:
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();

你不用自己創建一個 Handler了。
而 runOnUiThread 的具體實現,也跟我們做得差不多。UI線程即主線程。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

Handler 與 Loop 簡介
上面說到 每一個 Handler 都與一個線程相綁定。 實際上是,通過 Handler 與 Loop 綁定,而每一個 Loop 都與一個線程想綁定的。 比如 Handler 中兩個主要構造函數大概如下:
public Handler(...){
mLooper = Looper.myLooper();
// ...
}

public Handler(Looper looper,...){
mLooper = looper;
// ...
}

public static Looper myLooper() {
return sThreadLocal.get();
}

然後 Looper 的構造函數如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

綁定了當前的線程和生成了一個消息隊列。
值得提起的一點是, Looper 類保持了對 應用的主線程的 Looper 對象的靜態應用。
private static Looper sMainLooper; // guarded by Looper.class
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

這樣就可以方便你在其他線程中,使用一個綁定到主線程的 Handler,從而方便向主線程安插任務。 例如一般的圖片處理庫即是如此。這樣你只要指定一個 圖片的 url,及要更新的ImageView 即可。 如 Picasso 庫可以用如下代碼的讀取並更新圖片。 Picasso.with(context).load(url).into(imageView);
然後 Handler 可以做的事情還有很多。 因為它後面有 Looper 有 MessageQueue。可以深入了解下。
談一下線程池與 AsyncTask 類
Android 早期便有這個便利的類來讓我們方便的處理 工作線程及主線程的交互及通信。 但是現在先思考一下,我們上面的代碼可能遇到的問題。 比如,我們現在要顯示一個圖片列表。 一百多張圖片。 如果每下載一張就開一個線程的話,那一百多個線程,那系統資源估計支持不住。特別是低端的手機。
正確的做法是使用一個線程池。
final ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
executor.shutdown();
}
});

由於我們是在在一個局部方法中使用了一個線程池。所以處理完了之後應該將線程停止掉。 而我們上面只有一個線程,所以直接在下載完成之後,調用 executor停掉線程池。 那如果執行了多個圖片的下載請求。需要怎麼做呢? 那就要等他們都完成時,再停止掉線程池。 不過這樣用一次就停一次還是挺浪費資源的。不過我們可以自己保持一個應用級的線程池。 不過這就麻煩不少。
然後 Android 早已經幫我們想法了這一點了。 我們直接使用 AsyncTask 類即可。
於是我們下載圖片並顯示圖片的代碼如下:
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
return downloadImage(params[0]);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
mBeautyImageView.setImageBitmap(bitmap);
}
}.execute(beautyUrl);

相比之前的代碼簡潔不少。
看一下 AsyncTask 的源代碼,正是集合我們之前考慮的這些東西。
一個全局的線程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

一個綁定主線程的 Handler ,在線程中處理傳遞的消息
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}

@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}

小結
Android 提供的 Looper 和 Handler 可以讓我們非常方便的在各線程中安排可執行的任務

㈢ android中handle在內部類中為什麼不執行

當Android應用啟動的時候,會先創建一個應用主線程的Looper對象,Looper實現了一個簡單的消息隊列,一個一個的處理裡面的Message對象。主線程Looper對象在整個應用生命周期中存在。
當在主線程中初始化Handler時,該Handler和Looper的消息隊列關聯。發送到消息隊列的Message會引用發送該消息的Handler對象,這樣系統可以調用 Handler#handleMessage(Message) 來分發處理該消息。
在Java中,非靜態(匿名)內部類會引用外部類對象。而靜態內部類不會引用外部類對象。
如果外部類是Activity,則會引起Activity泄露 。
當Activity finish後,延時消息會繼續存在主線程消息隊列中1分鍾,然後處理消息。而該消息引用了Activity的Handler對象,然後這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導致該Activity對象無法被回收,從而導致了上面說的 Activity泄露。
要修改該問題,只需要按照Lint提示的那樣,把Handler類定義為靜態即可,然後通過WeakReference 來保持外部的Activity對象。

㈣ android在主線程中使用handle.postdelay做延時操作對主線程資源消耗大嗎

android在主線程中使用handle.postdelay做延時操作對主線程資源消耗不大,因為handler中有一個消息池,是靜態的消息池, 建議去了解一下Android中的Handler, Looper, MessageQueue之間的關系就知道了.

熱點內容
照片視頻加密 發布:2024-10-05 23:58:58 瀏覽:477
北京java培訓班多少錢 發布:2024-10-05 23:49:03 瀏覽:813
subversion源碼安裝 發布:2024-10-05 23:48:17 瀏覽:120
ipad文件怎麼解壓縮 發布:2024-10-05 23:06:28 瀏覽:165
存儲伺服器主控晶元 發布:2024-10-05 23:04:33 瀏覽:571
php學徒 發布:2024-10-05 23:04:30 瀏覽:441
活字格手機端清除緩存了什麼辦 發布:2024-10-05 23:03:23 瀏覽:872
阿杜訪問 發布:2024-10-05 22:44:23 瀏覽:602
我的世界怎麼在別的伺服器開掛 發布:2024-10-05 22:31:14 瀏覽:297
下沉演算法 發布:2024-10-05 21:59:43 瀏覽:999