Android 개발 노트: 메시지 순환과 Looper 상세 정보

Understanding LooperLooper는 하나의 라인에 메시지 대기열 (MessageQueue) 을 추가하고, 메시지가 있을 때 메시지를 처리하는 도구입니다.일반적으로 Looper는 사용되지 않습니다. Activity, Service 등 시스템 구성 요소에 대해Frameworks는 이미 우리에게 루트 (속칭 UI 루트나 주 루트) 를 초기화했습니다. 그 안에 Looper와 Looper가 만든 메시지 대기열이 포함되어 있기 때문에 메인 루트는 일부 이벤트 (BACK) 가 끝날 때까지 사용자 이벤트를 계속 실행하고 처리합니다.만약, 우리가 새로운 라인을 만들어야 하고, 이 라인이 다른 라인에서 보내는 메시지 이벤트를 순환적으로 처리하거나, 장기적으로 다른 라인과 복잡한 상호작용을 해야 한다면, 이 라인은 Looper로 메시지 대기열을 만들어야 한다.Looper를 사용하는 것도 매우 간단하다. 그 방법은 비교적 적다. 가장 중요한 것은 네 가지가 있다.public static prepare ().    public static myLooper();    public static loop();    public void quit();사용 방법은 다음과 같다. 1.각 스레드의 run () 방법 중 가장 먼저 Looper를 호출합니다.prepare (), 이것은 스레드를 위한 메시지 대기열을 초기화합니다.2. 이후 Looper를 호출합니다.myLooper()에서 이 Looper 객체에 대한 참조를 가져옵니다.이것은 필수적인 것이 아닙니다. 그러나 Looper 대상을 저장하려면 prepare () 다음에 해야 합니다. 그렇지 않으면 이 대상을 호출하는 방법이 효과가 있을 수 없습니다. 예를 들어 looper.quit () 는 종료되지 않습니다.3. run() 방법에 Handler를 추가하여 메시지를 처리합니다. 4.Looper를 추가합니다.loop () 호출, 이것은 라인의 메시지 대기열을 실행하기 시작하여 메시지를 받을 수 있도록 합니다.5. 메시지 순환을 종료하려면 Looper를 호출합니다.quit() 이 방법은 대상 위에서 호출해야 합니다. 분명히 대상 사용은 구체적인 Looper를 종료하는 것을 의미합니다.run () 에서 다른 작업이 없으면 라인도 실행을 중지합니다.다음은 실례입니다. 이 예는 임무를 수행하는 서비스를 실현했습니다

public class LooperDemoActivity extends Activity {
    private WorkerThread mWorkerThread;
    private TextView mStatusLine;
    private Handler mMainHandler;

    @Override
    public void onCreate(Bundle icicle) {
 super.onCreate(icicle);
 setContentView(R.layout.looper_demo_activity);
 mMainHandler = new Handler() {
     @Override
     public void handleMessage(Message msg) {
  String text = (String) msg.obj;
  if (TextUtils.isEmpty(text)) {
      return;
  }
  mStatusLine.setText(text);
     }
 };

 mWorkerThread = new WorkerThread();
 final Button action = (Button) findViewById(R.id.looper_demo_action);
 action.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
  mWorkerThread.executeTask("please do me a favor");
     }
 });
 final Button end = (Button) findViewById(R.id.looper_demo_quit);
 end.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
  mWorkerThread.exit();
     }
 });
 mStatusLine = (TextView) findViewById(R.id.looper_demo_displayer);
 mStatusLine.setText("Press 'do me a favor' to execute a task, press 'end of service' to stop looper thread");
    }

    @Override
    public void onDestroy() {
 super.onDestroy();
 mWorkerThread.exit();
 mWorkerThread = null;
    }

    private class WorkerThread extends Thread {
 protected static final String TAG = "WorkerThread";
 private Handler mHandler;
 private Looper mLooper;

 public WorkerThread() {
     start();
 }

 public void run() {
     // Attention: if you obtain looper before Looper#prepare(), you can still use the looper
     // to process message even after you call Looper#quit(), which means the looper does not
     //really quit.
     Looper.prepare();
     // So we should call Looper#myLooper() after Looper#prepare(). Anyway, we should put all stuff between Looper#prepare()
     // and Looper#loop().
     // In this case, you will receive "Handler{4051e4a0} sending message to a Handler on a dead thread
     // 05-09 08:37:52.118: W/MessageQueue(436): java.lang.RuntimeException: Handler{4051e4a0} sending message
     // to a Handler on a dead thread", when try to send a message to a looper which Looper#quit() had called,
     // because the thread attaching the Looper and Handler dies once Looper#quit() gets called.
     mLooper = Looper.myLooper();
     // either new Handler() and new Handler(mLooper) will work
     mHandler = new Handler(mLooper) {
  @Override
  public void handleMessage(Message msg) {
      /*
       * Attention: object Message is not reusable, you must obtain a new one for each time you want to use it.
       * Otherwise you got "android.util.AndroidRuntimeException: { what=1000 when=-15ms obj=it is my please
       * to serve you, please be patient to wait!........ } This message is already in use."
       */
//      Message newMsg = Message.obtain();
      StringBuilder sb = new StringBuilder();
      sb.append("it is my please to serve you, please be patient to wait!
");
      Log.e(TAG, "workerthread, it is my please to serve you, please be patient to wait!");
      for (int i = 1; i < 100; i++) {
   sb.append(".");
   Message newMsg = Message.obtain();
   newMsg.obj = sb.toString();
   mMainHandler.sendMessage(newMsg);
   Log.e(TAG, "workthread, working" + sb.toString());
   SystemClock.sleep(100);
      }
      Log.e(TAG, "workerthread, your work is done.");
      sb.append("
your work is done");
      Message newMsg = Message.obtain();
      newMsg.obj = sb.toString();
      mMainHandler.sendMessage(newMsg);
  }
     };
     Looper.loop();
 }

 public void exit() {
     if (mLooper != null) {
  mLooper.quit();
  mLooper = null;
     }
 }

 // This method returns immediately, it just push an Message into Thread's MessageQueue.
 // You can also call this method continuously, the task will be executed one by one in the
 // order of which they are pushed into MessageQueue(they are called).
 public void executeTask(String text) {
     if (mLooper == null || mHandler == null) {
  Message msg = Message.obtain();
  msg.obj = "Sorry man, it is out of service";
  mMainHandler.sendMessage(msg);
  return;
     }
     Message msg = Message.obtain();
     msg.obj = text;
     mHandler.sendMessage(msg);
 }
    }
}
이 실례에서 메인 라인에서 임무를 수행하는 것은 서비스 라인에 메시지를 보내는 동시에 관련 데이터를 전달하는 것이다. 데이터는 메시지 대상(Message)으로 포장된 다음에 서비스 라인의 메시지 대기열에 넣고 메인 라인의 호출이 되돌아온다. 이 과정은 매우 빠르기 때문에 메인 라인을 막지 않는다.서비스 라인은 메시지가 메시지 대기열에 들어갈 때마다 깨워서 대기열에서 메시지를 꺼내고 작업을 수행합니다.서비스 스레드는 임의의 수량의 작업을 수신할 수 있습니다. 즉, 주 스레드는 끊임없이 서비스 스레드에 메시지를 보낼 수 있습니다. 이 메시지는 메시지 대기열에 넣고, 서비스 스레드는 모든 작업이 끝날 때까지 계속 실행됩니다. (메시지 대기열이 비어 다른 메시지가 없습니다.) 서비스 스레드는 다시 휴면 상태로 들어갑니다. 새로운 메시지가 올 때까지.서비스 라인을 종료하려면 mLooper 대상에서quit () 를 호출하면 메시지 순환이 종료됩니다. 라인이 다른 작업이 없기 때문에 전체 라인도 종료됩니다.주의해야 할 것은 한 라인의 메시지 순환이 종료된 후에 메시지를 더 이상 보낼 수 없습니다. 그렇지 않으면 "Runtime Exception: Handler {4051e4a0}sending message to a Handler on a dead thread"를 이상 던질 수 있습니다.그래서 Looper에서 권장합니다.prepare () 후 Looper를 호출합니다.이 Looper에 대한 인용을 가져오기 위해 myLooper () 는 종료(quit () 대상에 호출해야 함) 에 사용됩니다.또한 메시지를 받을 때 메시지 순환이 종료되었는지 확인하는 데 사용됩니다 (예를 들어).

좋은 웹페이지 즐겨찾기