1. 程式人生 > >Android效能優化之實現擁有Looper的執行緒--HandlerThread

Android效能優化之實現擁有Looper的執行緒--HandlerThread

1 HandlerThread

1.1 定義

  HandlerThread是能夠新建擁有Looper的Thread,這個Looper能夠用來新建其他的Handler。HandlerThread本質是一個執行緒,線上程內部,程式碼是序列處理的。(執行緒中的Looper)需要注意的是,新建的時候需要被回撥。

1.2 特點

(1) HandlerThread將loop轉到子執行緒中處理,目的就是分擔MainLooper的工作量,降低了主執行緒的壓力,使主介面更流暢。
(2)開啟一個執行緒起到多個執行緒的作用。處理任務是序列執行,按訊息傳送順序進行處理。
(3)但是由於每一個任務都將以佇列的方式逐個被執行到,一旦佇列中有某個任務執行時間過長,那麼就會導致後續的任務都會被延遲處理。
(4)HandlerThread擁有自己的訊息佇列,它不會干擾或阻塞UI執行緒。

1.3 應用場景

  HandlerThread繼承自Thread,一般適應的場景,便是集Thread和Handler之所長,適用於會長時間在後臺執行,並且間隔時間內(或適當情況下)會呼叫的情況,比如上面所說的實時更新。其實使用HandlerThread的效果和使用Thread+Handler差不多。不過後者對開發者的要求更高。
  在我的另一篇部落格詳細應用:Android效能優化之詳解IntentService

1.4 Handler、Thread和HandlerThread的差別

  Handler會關聯一個單獨的執行緒、Looper和訊息佇列;他預設關聯主執行緒,如果要在其他執行緒執行,可以使用HandlerThread。
  HandlerThread繼承於Thread,所以它本質就是個Thread。

它與普通Thread的差別就在於:建立了一個Thread,有自己的Looper和創立了訊息佇列,可以讓在自己的執行緒中分發和處理訊息。

2 HandlerThread原始碼分析

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * 建構函式,並設定執行緒的優先順序
     */
public HandlerThread(String name, int priority) { super(name); mPriority = priority; } protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } /** * 此方法返回與此執行緒關聯的Looper。 * 如果這個執行緒沒有被啟動,或者由於任何原因isAlive()返回false,這個方法將返回null。 * 如果這個執行緒已經啟動,這個方法將被阻塞,直到活套被初始化。 * @return The looper. */ public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } /** * Returns the identifier of this thread. */ public int getThreadId() { return mTid; } }

3 使用例子

3.1 例子

public class HandlerThreadActivity extends AppCompatActivity {

    private TextView mTvServiceInfo;

    private static final int MSG_UPDATE_INFO = 0x110;
    private boolean isUpdateInfo;

    private HandlerThread mCheckMsgThread;
    //使用HandlerThread執行緒的looper建立的mCheckMsgHandler
    private Handler mCheckMsgHandler;

    //主執行緒建立的mHandler來更新ui.
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_handler);

        //建立後臺執行緒
        initBackThread();

        mTvServiceInfo = (TextView) findViewById(R.id.id_textview);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //開始查詢
        isUpdateInfo = true;
        mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //停止查詢
        isUpdateInfo = false;
        mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);
    }

    private void initBackThread() {
        mCheckMsgThread = new HandlerThread("check-message-coming");
        mCheckMsgThread.start();
        mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                checkForUpdate();
                if (isUpdateInfo) {
                    mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
                }
            }
        };
    }

    /**
     * 模擬從伺服器解析資料
     */
    private void checkForUpdate() {
        try {
            //模擬耗時
            Thread.sleep(1000);
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    String result = "實時更新中,當前大盤指數:<font color='red'>%d</font>";
                    result = String.format(result, (int) (Math.random() * 3000 + 1000));
                    mTvServiceInfo.setText(Html.fromHtml(result));
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //釋放資源
        mCheckMsgThread.quit();
    }
}

3.2 執行效果

這裡寫圖片描述

4 解決一個需求

4.1 問題提出

  在子執行緒中開啟相機,並且在子執行緒中預覽回撥(編碼),如何實現?

4.2 子執行緒中的方法執行在子執行緒還是UI執行緒

public class ThreadTest {
    static class MyTask extends Thread {
        @Override
        public void run() {//只有run方法屬於子執行緒
            System.out.println(Thread.currentThread().getName() + " _run");
        }
        void onPreviewFrame(){//在UI執行緒執行
            System.out.println(Thread.currentThread().getName() + " _onPreviewFrame");
        }
    }
    public static void main(String[] args) {
        //子執行緒
        MyTask task = new MyTask();
        task.start();
        //在UI執行緒執行
        task.onPreviewFrame();
    }
}

4.3 方法對比

4.3.1 普通執行緒與Camera

  非同步任務(AsyncTask)的Looper(因為子執行緒沒建立looper),使用的MainLooper。

public class AsyncTaskActivity1 extends Activity implements Callback {
    static final String TAG = "guan";
    Camera mCamera;
    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;
    byte[] buffers;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread2);
        surfaceView = (SurfaceView) findViewById(R.id.surface_view);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        new MyTask().execute();
    }

    class MyTask extends AsyncTask<Void, Void, Void> implements PreviewCallback{

        @Override
        protected Void doInBackground(Void... params) {
            //子執行緒中開啟
            Log.e(TAG, Thread.currentThread().getName() + "_open");
            mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
            try {
                mCamera.setPreviewDisplay(surfaceHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }
            Camera.Parameters parameters = mCamera.getParameters();
            //設定相機引數
            parameters.setPreviewSize(480, 320); //預覽畫面寬高
            mCamera.setParameters(parameters);
            //獲取預覽影象資料
            buffers = new byte[480 * 320 * 4];
            mCamera.addCallbackBuffer(buffers);
            mCamera.setPreviewCallbackWithBuffer(this);
            mCamera.startPreview();

            Log.d(TAG, Thread.currentThread().getName()+ "_doInBackground");
            return null;
        }

        //畫面預覽的回撥
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            if(mCamera != null){
                mCamera.addCallbackBuffer(buffers);
                //編碼
                Log.d(TAG, Thread.currentThread().getName()+ "_onPreviewFrame");
            }
        }
    }
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }
}

  列印結果:
這裡寫圖片描述
  結果表明:畫面預覽的回撥方法onPreviewFrame()在UI執行緒中執行,未實現需求要求。

4.3.2 HandlerThread執行緒與Camera

public class HandlerThreadActivity4 extends Activity implements Callback {
    static final String TAG = "guan";
    Camera mCamera;
    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;
    byte[] buffers;

    HandlerThread mHandlerThread;
    Handler subHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread2);
        surfaceView = (SurfaceView) findViewById(R.id.surface_view);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mHandlerThread = new HandlerThread("my_handlerthread");
        mHandlerThread.start();
        subHandler = new Handler(mHandlerThread.getLooper());
        subHandler.post(new MyTask());
    }

    class MyTask implements Runnable, PreviewCallback{
        @SuppressLint("NewApi") @Override
        public void run() {
            //開啟相機
            //子執行緒中開啟
            Log.d("guan", Thread.currentThread().getName() + "_open");
            mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
            try {
                mCamera.setPreviewDisplay(surfaceHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }
            Camera.Parameters parameters = mCamera.getParameters();
            //設定相機引數
            parameters.setPreviewSize(480, 320); //預覽畫面寬高
            mCamera.setParameters(parameters);
            //獲取預覽影象資料
            buffers = new byte[480 * 320 * 4];
            mCamera.addCallbackBuffer(buffers);
            mCamera.setPreviewCallbackWithBuffer(this);
            mCamera.startPreview();

            Log.d(TAG, Thread.currentThread().getName()+ "_run");
        }

        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            if(mCamera != null){
                mCamera.addCallbackBuffer(buffers);
                //編碼
                Log.d(TAG, Thread.currentThread().getName()+ "_onPreviewFrame");
            }
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }
}

  列印結果:
這裡寫圖片描述
  結果表明:畫面預覽的回撥方法onPreviewFrame()在子執行緒中執行,完美實現需求要求。

4.3.3 解析原因–Camera原始碼

  new Camera -> looper -> EventHandler.handleMessage -> onPreviewFrame

public class Camera {
    private int cameraInitVersion(int cameraId, int halVersion) {
        mShutterCallback = null;
        mRawImageCallback = null;
        mJpegCallback = null;
        mPreviewCallback = null;
        mPostviewCallback = null;
        mUsingPreviewAllocation = false;
        mZoomListener = null;

        Looper looper;
        if ((looper = Looper.myLooper()) != null) {//Handler執行緒Looper
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {//UI執行緒Looper
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }

        return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
                ActivityThread.currentOpPackageName());
    }

    private class EventHandler extends Handler {
        private final Camera mCamera;

        public EventHandler(Camera c, Looper looper) {
            super(looper);
            mCamera = c;
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case CAMERA_MSG_PREVIEW_FRAME:
                    PreviewCallback pCb = mPreviewCallback;
                    if (pCb != null) {
                        if (mOneShot) {
                            mPreviewCallback = null;
                        } else if (!mWithBuffer) {                                            setHasPreviewCallback(true, false);
                        }
                        //回撥onPreviewFrame()方法
                        //onPreviewFrame的執行,在Camera所持有的Looper執行緒中執行
                        pCb.onPreviewFrame((byte[]) msg.obj, mCamera);
                    }
                    return;
            }
        }
    }
}

5 參考連結