Android-線程和后臺(tái)任務(wù)

Android線程的幾種方式

AsyncTask/HandlerThread/Thread/IntentService
http://www.itdecent.cn/p/34cffd700f75

AsyncTask

單個(gè)線程的線程池,一個(gè)一個(gè)執(zhí)行,會(huì)阻塞后邊線程。可以設(shè)置 AsyncTask.executeOnExecutor()來同時(shí)執(zhí)行任務(wù)。寫的時(shí)候注意避免內(nèi)存泄露

HandlerThread

實(shí)質(zhì)是一個(gè)Thread,不過添加了Looper和MessageQueue,這樣就可以使用Handler來進(jìn)行控制代碼執(zhí)行了。

// 創(chuàng)建一個(gè)線程,線程名字 : handlerThreadTest
        mHandlerThread = new HandlerThread("handlerThreadTest");
        mHandlerThread.start();
        
        // Handler 接收消息
        final Handler mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                Log.e("Test", "收到 " + msg.obj.toString() + " 在 "
                        + Thread.currentThread().getName());
            }
        };
        mTextView = (TextView) findViewById(R.id.text_view);
        // 主線程發(fā)出消息
        mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = new Message();
                msg.obj = "第一條信息";
                mHandler.sendMessage(msg);
                Log.e("Test", "發(fā)出 " + msg.obj.toString() + " 在 "
                        + Thread.currentThread().getName());
            }
        });
        // 子線程發(fā)出消息
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.obj = "第二條信息";
                mHandler.sendMessage(msg);
                Log.e("Test", "發(fā)出 " + msg.obj.toString() + " 在 "
                        + Thread.currentThread().getName());
            }
        }).start();  

但是最后不要忘記mHandlerThread.quit();否則將一直循環(huán)。另外可以在run方法里設(shè)置不同優(yōu)先級(jí)android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);同樣HandlerThread的構(gòu)造方法也提供了設(shè)置優(yōu)先級(jí)的功能,new HandlerThread("LightTaskThread", Process.THREAD_PRIORITY_BACKGROUND);AsyncTask同樣設(shè)置了優(yōu)先級(jí)THREAD_PRIORITY_BACKGROUND。
HandlerThread的默認(rèn)優(yōu)先級(jí)是Process.THREAD_PRIORITY_DEFAULT,具體值為0。線程的優(yōu)先級(jí)的取值范圍為-20到19。優(yōu)先級(jí)高的獲得的CPU資源更多,反之則越少。-20代表優(yōu)先級(jí)最高,19最低。0位于中間位置,但是作為工作線程的HandlerThread沒有必要設(shè)置這么高的優(yōu)先級(jí),因而需要我們降低其優(yōu)先級(jí)。
THREAD_PRIORITY_DEFAULT,默認(rèn)的線程優(yōu)先級(jí),值為0。
THREAD_PRIORITY_LOWEST,最低的線程級(jí)別,值為19。
THREAD_PRIORITY_BACKGROUND 后臺(tái)線程建議設(shè)置這個(gè)優(yōu)先級(jí),值為10。
THREAD_PRIORITY_MORE_FAVORABLE 相對THREAD_PRIORITY_DEFAULT稍微優(yōu)先,值為-1。
THREAD_PRIORITY_LESS_FAVORABLE 相對THREAD_PRIORITY_DEFAULT稍微落后一些,值為1。

IntentService

使用了HandlerThread使得IntentService可以運(yùn)行耗時(shí)任務(wù),一般使用時(shí)結(jié)合主線程Handler或者LocalBroadCastManager來通知主界面UI的更新。

ThreadPool線程池

線程池不允許使用 Executors 去創(chuàng)建,而是通過ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
說明: Executors 返回的線程池對象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允許的請求隊(duì)列長度為 Integer.MAX_VALUE ,可能會(huì)堆積大量的請求,從而導(dǎo)致 OOM 。
2) CachedThreadPool 和ScheduledThreadPool :
允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE ,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致 OOM 。

https://liuzho.github.io/2017/04/17/%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%8C%E8%BF%99%E4%B8%80%E7%AF%87%E6%88%96%E8%AE%B8%E5%B0%B1%E5%A4%9F%E4%BA%86/

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
);

public class ThreadPoolTest {
    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int BLOCK_POOL_SIZE = 2;
    private static final int ALIVE_POOL_SIZE = 2;
    private static ThreadPoolExecutor executor;

    public static void main(String args[]) {
        executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,// 核心線程數(shù) 最小
                MAX_POOL_SIZE,// 最大執(zhí)行線程數(shù)
                ALIVE_POOL_SIZE,// 空閑線程超時(shí)
                TimeUnit.SECONDS,// 超時(shí)時(shí)間單位
                // 當(dāng)線程池達(dá)到corePoolSize時(shí),新提交任務(wù)將被放入workQueue中,
                // 等待線程池中任務(wù)調(diào)度執(zhí)行
                new ArrayBlockingQueue<Runnable>(BLOCK_POOL_SIZE),// 阻塞隊(duì)列大小
                // 線程工廠,為線程池提供創(chuàng)建新線程的功能,它是一個(gè)接口,
                // 只有一個(gè)方法:Thread newThread(Runnable r)
                Executors.defaultThreadFactory(),
                // 線程池對拒絕任務(wù)的處理策略。一般是隊(duì)列已滿或者無法成功執(zhí)行任務(wù),
                // 這時(shí)ThreadPoolExecutor會(huì)調(diào)用handler的rejectedExecution
                // 方法來通知調(diào)用者
                new ThreadPoolExecutor.AbortPolicy()
        );
        executor.allowCoreThreadTimeOut(true);
        /*
         * ThreadPoolExecutor默認(rèn)有四個(gè)拒絕策略:
         *
         * 1、ThreadPoolExecutor.AbortPolicy()   直接拋出異常RejectedExecutionException
         * 2、ThreadPoolExecutor.CallerRunsPolicy()    直接調(diào)用run方法并且阻塞執(zhí)行
         * 3、ThreadPoolExecutor.DiscardPolicy()   直接丟棄后來的任務(wù)
         * 4、ThreadPoolExecutor.DiscardOldestPolicy()  丟棄在隊(duì)列中隊(duì)首的任務(wù)
         */

        for (int i = 0; i < 10; i++) {
            try {
                executor.execute(new WorkerThread("線程 --> " + i));
                LOG();
            } catch (Exception e) {
                System.out.println("AbortPolicy...");
            }
        }
        executor.shutdown();

        // 所有任務(wù)執(zhí)行完畢后再次打印日志
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("\n\n---------執(zhí)行完畢---------\n");
                    LOG();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 打印 Log 信息
     */
    private static void LOG() {
        System.out.println(" ==============線程池===============\n"
                + "   線程池中線程數(shù) : " + executor.getPoolSize()
                + "   等待執(zhí)行線程數(shù) : " + executor.getQueue().size()
                + "   所有的任務(wù)數(shù) : " + executor.getTaskCount()
                + "   執(zhí)行任務(wù)的線程數(shù) : " + executor.getActiveCount()
                + "   執(zhí)行完畢的任務(wù)數(shù) : " + executor.getCompletedTaskCount()

        );
    }

    // 模擬線程任務(wù)
    public static class WorkerThread implements Runnable {
        private String threadName;

        public WorkerThread(String threadName) {
            this.threadName = threadName;
        }

        @Override
        public synchronized void run() {

            int i = 0;
            boolean flag = true;
            try {
                while (flag) {
                    i++;
                    if (i > 2) flag = false;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public String getThreadName() {
            return threadName;
        }
    }

}

請解釋下在單線程模型中Message,Handler,Message Queue,Looper之間的關(guān)系。

拿主線程來說,主線程啟動(dòng)時(shí)會(huì)調(diào)用Looper.prepare()方法,會(huì)初始化一個(gè)Looper,放入Threadlocal中,接著調(diào)用Looper.loop()不斷遍歷Message Queue,Handler的創(chuàng)建依賴與當(dāng)前線程中的Looper,如果當(dāng)前線程沒有Looper則必須調(diào)用Looper.prepare()。Handler , sendMessage到MessageQueue,Looper不斷從MessageQueue中取出消息,回調(diào)handleMessage方法。

Android的進(jìn)程間通信,Liunx操作系統(tǒng)的進(jìn)程間通信

binder(所有android上層幾乎都是binder)/socket(zygote-systemserver)
Linux機(jī)制: 管道、消息隊(duì)列、共享內(nèi)存、套接字、信號(hào)量、信號(hào)這些IPC機(jī)制
handler線程之間通信

asynctask的原理

AsyncTask是對Thread和Handler的組合包裝。
https://blog.csdn.net/iispring/article/details/50670388
https://blog.csdn.net/epubit17/article/details/80342004

Alarm機(jī)制

Timer TimerTask
http://coderlin.coding.me/2016/04/02/Android-%E5%88%9D%E6%AD%A5%E4%B9%8BTimmer-AlarmManager-JobSchedule/>

JobScheduler

http://blog.csdn.net/bboyfeiyu/article/details/44809395

ELAPSED_REALTIME_WAKEUP和RTC_WAKEUP的區(qū)別

AlarmManager.ELAPSED_REALTIME_WAKEUP type is used to trigger the alarm since boot time:
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 600000, pendingIntent);
will actually make the alarm go off 10 min after the device boots.
There is a timer that starts running when the device boots up to measure the uptime of the device and this is the type that triggers your alarm according to the uptime of the device.
Whereas, AlarmManager.RTC_WAKEUP will trigger the alarm according to the time of the clock. For example if you do:
long thirtySecondsFromNow = System.currentTimeMillis() + 30 * 1000;
alarmManager.set(AlarmManager.RTC_WAKEUP, thirtySecondsFromNow , pendingIntent);
this, on the other hand, will trigger the alarm 30 seconds from now.
AlarmManager.ELAPSED_REALTIME_WAKEUP type is rarely used compared to AlarmManager.RTC_WAKEUP

來自 http://stackoverflow.com/questions/5938213/android-alarmmanager-rtc-wakeup-vs-elapsed-realtime-wakeup

handler和timer的延遲對比

Handler vs Timer
在我們Android開發(fā)過程中,經(jīng)常需要執(zhí)行一些短周期的定時(shí)任務(wù),這時(shí)候有兩個(gè)選擇Timer或者Handler。然而個(gè)人認(rèn)為:Handler在多個(gè)方面比Timer更為優(yōu)秀,更推薦使用。
一.易用性

  1. 可重復(fù)執(zhí)行
    ? Handler可以重復(fù)執(zhí)行某個(gè)任務(wù)。
    ? Timer若在某個(gè)任務(wù)執(zhí)行/取消之后,再次執(zhí)行則會(huì)拋出一個(gè)IllegalStateException異常。為了避免這個(gè)異常,需要重新創(chuàng)建一個(gè)Timer對象。
  2. 周期可調(diào)整
    若想要執(zhí)行一個(gè)越來越快的定時(shí)任務(wù),Handler可以做到,而Timer則消耗較大。
    ? Handler
    private Handler handler = new Handler();
    int mDelayTime = 1000;
    private Runnable runnable = new Runnable() {
    public void run() {
    update();
    if (mDelayTime > 0) {
    handler.postDelayed(this,mDelayTime);
    mDelayTime -= 100;
    }
    }
    };
    handler.postDelayed(runnable,1000);
    如以上例子,就可以實(shí)現(xiàn)對周期的動(dòng)態(tài)調(diào)整。
    ? Timer的scheduleAtFixedRate(TimerTask task, long delay, long period)只能執(zhí)行固定周期的任務(wù),所以不可以動(dòng)態(tài)地調(diào)整周期。若想要?jiǎng)討B(tài)調(diào)整,則需要在執(zhí)行玩一個(gè)定時(shí)器任務(wù)后,再啟動(dòng)一個(gè)新的任務(wù)時(shí)設(shè)置新的時(shí)間。
  3. UI界面更新
    ? Handler:在創(chuàng)建的時(shí)候可以指定所在的線程,一般在Activity中構(gòu)建的,即主線程上,所以可以在回調(diào)方法中很方便的更新界面。
    ? Timer:異步回調(diào),所以必須借助Handler去更新界面,不方便。
    既然都得用Handler去更新界面了,為何不如把定時(shí)的功能也交給Handler去做呢?
    二.內(nèi)存占比
    Timer比Handler更占內(nèi)存。
    接下來的Demo例子通過兩種方法循環(huán)地打印日志,然后通過MAT插件來查看這兩個(gè)類所需要調(diào)用的對象所產(chǎn)生的占比。

Async轉(zhuǎn)sync的方法 異步->同步

You could do this with CountdownLatch, which might be the lightest synchronization primitive in java.util.concurrent:
private boolean findPrinter(final Context ctx) {
final CountdownLatch latch = new CountdownLatch(1);
final boolean[] result = {false};
...
BluetoothDiscoverer.findPrinters(ctx, new DiscoveryHandler() {
...
public void discoveryFinished() {
result[0] = true;
latch.countDown();
}
public void discoveryError(String arg0) {
result[0] = false;
latch.countDown();
}
...
}
// before final return
// wait for 10 seconds for the response
latch.await(10, TimeUnit.SECONDS);
//return the result, it will return false when there is timeout
return result[0];
}
來自 https://stackoverflow.com/questions/20659961/java-synchronous-callback

Handler集中形式原理

https://blog.csdn.net/reakingf/article/details/52054598
Looper
其中threadlocal保證一個(gè)線程只有一個(gè)Looper
一個(gè)Looper一個(gè)MessageQueue
不斷循環(huán)獲取msg,從target中取得相應(yīng)得handler來進(jìn)行dispatchmsg

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

public static void loop() {
    for (;;) {
            Message msg = queue.next(); // might block
            msg.target.dispatchMessage(msg);
    }
}

處理順序Handler.dispatchMessage

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

msg

  • sendEmptyMessageDelayed(what, time)->sendMessageAtTime
    msg.what = what

  • postAtTime(Runnable r, long uptimeMillis)
    sendMessageDelayed(getPostMessage(r), 0);
    getPostMessage()
    m.callback = r

  • enqueue msg
    msg.target = 記錄不同handler

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

hanlder
Handler(Handler.Callback callback)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容