AsyncLayoutInflater "비동기 로드 레이아웃" 구현

9013 단어
AsyncLayoutInflater 은 비동기 적 으로 레이아웃 을 초기 화 하 는 Helper 클래스 입 니 다.
그것 의 본질은 레이아웃 파일 inflate 을 하위 스 레 드 에 넣 고 초기 화 에 성공 한 후에 인 터 페 이 스 를 통 해 메 인 스 레 드 로 던 지 는 것 이다.Activity 에서 간단 한 코드 는 다음 과 같 습 니 다.
 //      xml,   Activity.onCreate(xxx)   
AsyncLayoutInflater(this).inflate(R.layout.activity_try_everything, null, object : AsyncLayoutInflater.OnInflateFinishedListener {
      override fun onInflateFinished(view: View, p1: Int, p2: ViewGroup?) {
           setContentView(view)
      }
})

일반적으로 우 리 는 setContentView(R.layout.xxx) 의 형식 으로 로드 xml 한다.
사실은 세 가지 서로 다른 방법 매개 변수 setContentView() 함수 가 있 는데 다음 과 같다.
    public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
    }

    public void setContentView(View view) {
        this.getDelegate().setContentView(view);
    }

    public void setContentView(View view, LayoutParams params) {
        this.getDelegate().setContentView(view, params);
    }

우 리 는 inflate 을 통 해 나 와 layoutResID 대응 하 는 view 을 한 다음 에 호출 setContentView(View view) 을 통 해 이렇게 전달 할 수 있다.
그렇다면 AsyncLayoutInflater 어떻게 일 했 을 까?다음 내용 은 다음 과 같은 부분 으로 나 뉜 다.
  • AsyncLayoutInflater 의 간단 한 소개 와 창설
  • 레이아웃 초기 화 요청 추가 request
  • AsyncLayoutInflater.inflate(xxx) 에서 보 낸 메 시 지 를 처리 하고 결 과 를 Handler 스 레 드
  • 에 되 돌려 줍 니 다.
  • 소결
  • 1. Message 의 간단 한 소개 와 창설
    1.1 UI 에 대한 간단 한 소개AsyncLayoutInflater 의 실현 은 주로 세 개의 구성원 변수 와 하나의 내부 유형 이 있다.
  • AsyncLayoutInflater 포석 AsyncLayoutInflater
  • LayoutInflater 메 인 스 레 드 까지 post
  • inflate 는 하나의 Handler 이 고 하나의 스 레 드 로 안에서 완성 InflateThread 과정
  • 이다.
  • Thread 정적 내부 류 는 필요 한 정 보 를 담 는 데 사용 된다
  • inflate 외부 에 누 출 된 인 터 페 이 스 를 외부 에 알 리 는 데 사용 하고 초기 화 InflateRequest 완료
  • 1.2 OnInflateFinishedListener 의 생 성View 의 생 성 은 코드 에서 볼 수 있 습 니 다.
    public AsyncLayoutInflater(@NonNull Context context) {
        //       AsyncLayoutInflater    ,      inflater, handler,   thread
        mInflater = new BasicInflater(context);
        mHandler = new Handler(mHandlerCallback);
        mInflateThread = InflateThread.getInstance();
    }
    

    그 중에서 코드 분석 은 다음 과 같다.
  • AsyncLayoutInflater 는 후속 용 AsyncLayoutInflater 구조
  • 이다.
  • mInflater 는 하나의 사례 대상 으로 획득 할 때 이 inflate 은 이미 작업 을 시작 했다 mInflateThread 코드 는 다음 과 같다.
    private static final InflateThread sInstance;
    //      ,           
       static {
       sInstance = new InflateThread();
       //           
       sInstance.start();
       }
    
  • threadstart 에서 보 낸 메 시 지 를 받 고 mHandler 스 레 드 thread 로 돌아 가 메 시 지 를 보 내 는 위치 입 니 다.
    Message.obtain(request.inflater.mHandler, 0, request).sendToTarget()
    
    그리고 주요 정보 인 '즉 UI' 을 Message 에 넣 습 니 다. request위의 준 비 를 마 친 후, 이때 Message 안 에는 실행 할 요청 이 없 으 며, 그 대열 에 object 이 있 으 면 대응 하 는 논 리 를 집행 할 것 이다.
    2. 레이아웃 초기 화 요청 추가 mInflateThread이 방법 을 호출 할 때 일련의 절차 가 있 습 니 다.
  • 창설 request 요청;
  • 이 요청 을 request 하위 스 레 드 에 넣 기
  • 원본 코드 는 다음 과 같 습 니 다.
    @UiThread
    public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
                @NonNull OnInflateFinishedListener callback) {
        if (callback == null) {
            throw new NullPointerException("callback argument may not be null!");
        }
        InflateRequest request = mInflateThread.obtainRequest();
        request.inflater = this;
        request.resid = resid;
        request.parent = parent;
        request.callback = callback;
        mInflateThread.enqueue(request);
    }
    

    코드 에서 볼 수 있 듯 이 호출 AsyncLayoutInflater.inflate(xxx) 방법 마다 InflateRequest 을 새로 만 들 고 이 mInflateThread.enqueue(request)mInflateThread 대기 열 에 추가 합 니 다.
    2.1 창설 inflate(xxx) 요청InflateRequest 에 요청 을 저장 하기 위 한 대기 열 request 이 있 습 니 다.
    private ArrayBlockingQueue mQueue = new ArrayBlockingQueue<>(10);
    
    mInflateThread 스 레 드 는 InflateRequest 이후 호출 InflateThread 을 시도 하여 mQueue 를 가 져 온 다음 에 대응 하 는 논 리 를 집행 합 니 다.
    @Override
    public void run() {
        while (true) {
            runInner();
        }
    }
    

    주: 여 기 는 결코 사순환 이 아 닙 니 다. 왜냐하면 InflateRequest 방법 때 문 입 니 다.InflateThread 는 차단 이 발생 하 는 대열 로 start() 방법 에서 runInner() 하면 계속 빠 져 든다 inflateRequest.mQueue.take()ArrayBlockingQueue 방법 소스 코드:
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //        ,       
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
    

    2.2 take() 을 통 해 요청 을 추가 한 후 count == 0 비어 있 지 않 음
    notEmpty.await() 에서 ArrayBlockingQueue 를 얻 을 수 있 습 니 다. take() 를 통 해 하위 스 레 드 mInflateThread.enqueue(request) 구축 을 완료 하고 mQueue 를 통 해 해당 하 는 mQueue 에 게 메 시 지 를 보 냅 니 다.코드 는 다음 과 같 습 니 다:
    public void runInner() {
         InflateRequest request;
         try {
             //        
             request = mQueue.take();
         } catch (InterruptedException ex) {
             // Odd, just continue
             Log.w(TAG, ex);
             return;
         }
        // 
         try {
             request.view = request.inflater.mInflater.inflate(
                            request.resid, request.parent, false);
         } catch (RuntimeException ex) {
             // Probably a Looper failure, retry on the UI thread
             Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI" + " thread", ex);
        }
        Message.obtain(request.inflater.mHandler, 0, request).sendToTarget();
    }
    

    3. request 에서 보 낸 메 시 지 를 처리 하고 결 과 를 inflater.inflate(xxx) 스 레 드 에 되 돌려 줍 니 다.view 을 통 해 메 시 지 를 보 낸 후에 진정 으로 메 시 지 를 받 는 곳 은 Messagehandler 방법 안에 있 습 니 다. 이때 이미 Handler 스 레 드 로 잘 랐 습 니 다.
    @Override
    public boolean handleMessage(Message msg) {
        InflateRequest request = (InflateRequest) msg.obj;
        if (request.view == null) {
            request.view = mInflater.inflate(request.resid, request.parent, false);
        }
        request.callback.onInflateFinished(request.view, request.resid, request.parent);
        mInflateThread.releaseRequest(request);
        return true;
    }
    

    코드 논리:
  • Message 에서 UI
  • 판단 Message 여부 Handler
  • 비어 있 으 면 다시 handleMessage(xxx) 합 니 다. 이 때 는 UI 스 레 드 에서 진행 되 며 일반적인 초기 화 와 같 습 니 다.
  • 인터페이스 msg.obj 를 통 해 외부 에 알 리 고 받 은 것 InflateRequest 을 전달 합 니 다
  • 주: 여기 서 호 환 을 해서 비동기 request.view 에서 실 패 를 방지 하고 판단 을 했 습 니 다.
    대외 폭 루 의 인터페이스: null가장 간단 한 inflate 과 마찬가지 로 인 터 페 이 스 를 통 해 일 을 외부 호출 자 에 게 던 지 는 것 과 비슷 하 다.
    4. 소결
    상기 부분 에서 간단하게 UI 의 내부 실현 을 소개 했다.
    마음속 에 약간의 의문 이 생 길 수 있 습 니 다. 이것 은 쉽게 말 하면 OnInflateFinishedListener 새로운 서브 스 레 드 를 만 든 다음 view 좋 은 inflate 을 다시 AsyncLayoutInflater.OnInflateFinishedListener 스 레 드 에 넣 으 면 큰 도움 이 되 지 않 습 니까?
    확실히 View.OnClickListener 쉽게 말 하면 상술 한 표현 을 실현 한 것 이다.
    그리고 단점 도 많 습 니 다. 공식 문 서 는 명확 하 게 쓰 여 있 습 니 다.
    This inflater does not support setting a LayoutInflater.Factory nor LayoutInflater.Factory2. Similarly it does not support inflating layouts that contain fragments.
    

    한계 가 있다. 이것 도 우리 가 일반적으로 이런 방식 을 사용 하지 않 는 큰 원인 이다.
    그런데?하나의 AsyncLayoutInflater 가 비동기 new 를 지원 할 수 있다 면 inflate 할 수 있 습 니까?
    하위 스 레 드 에서 미리 배치 할 수 있 습 니까?그리고 이런 것들 view 이 진정 으로 필요 할 때 레이아웃 을 불 러 오 는 시간 을 절약 할 수 있 습 니까?
    물론 입 니 다. 구체 적 으로 어떻게 실현 되 는 지 는 다음 에 다시 정리 해 야 합 니 다.
    이번 에는 간단 한 소개 UI 일 뿐이다.
    추 후 정리 가 잘 되 기 를 기대한다.AsyncLayoutInflater 글 은 공중 번호 에서 왔 다. XMLinflate 후속 회 의 는 이어서 정리 지식 을 정리 할 것 이다.
    참고:
  • 공식 문서 설명: PreInflate
  • inflate
  • view
  • 좋은 웹페이지 즐겨찾기