2013-12-16 3 views
4

서비스 내에서 처리가 많이 필요한 스레드를 사용하고 있으며이 처리 중에 GUI (작업)를 업데이트하려고합니다. 이렇게하려면 스레드에서 처리기로 메시지를 보내고 처리기에서 GUI를 업데이트합니다. 그러나 문제는 메시지 대기열이 차단 된 것처럼 작업자 스레드가 종료 될 때만 처리기가 메시지를 수신한다는 것입니다. 서비스를 사용하는 이유는 응용 프로그램이 표시되지 않아도 프로세스가 계속되어야하기 때문입니다.안드로이드 : 작업자 스레드가 끝날 때 처리기의 메시지가 지연됩니다.

응용 프로그램 목표는 미리 정의 된 명령 목록을 보내 NFC 칩 (ISO15693)을 테스트하는 것입니다. 따라서 명령 전송은 스레드에 의해 수행되고 각 명령에 대해 결과는 처리기로 전송됩니다. 여기

내 코드입니다 :

응용 프로그램

public class ISO15693Application extends Application { 
... 
    //Handler receiving messages from Worker thread 
    final RunTestHandler runTestHandler = new RunTestHandler(ISO15693Application.this);  
    static class RunTestHandler extends Handler { 
     //Avoid leak with handler   
     //See http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler 
     private final WeakReference<ISO15693Application> mApplication; 

     RunTestHandler(ISO15693Application isoApp) { 
      mApplication = new WeakReference<ISO15693Application>(isoApp); 
     } 

     @Override 
     public void handleMessage(Message msg) { 
      // TODO Auto-generated method stub 
      super.handleMessage(msg); 

      ISO15693Application isoApp = mApplication.get(); 

      switch(msg.what){ 
      case MSG_TEST_RESULT: 
       NfcVResponse r = (NfcVResponse) msg.obj; 
       if(r != null){ 
        Log.i(TAG, "handleMessage Thread id : " + Thread.currentThread().getId()); 
        isoApp.onTestResult((NfcVResponse) msg.obj, msg.arg1); 
       }      
       break; 

      case MSG_TEST_STARTED: 
       isoApp.onTestStarted(msg.arg1); 
       break; 
      case MSG_TEST_TERMINATED: 
       isoApp.onTestTerminated(); 
       break; 
      case MSG_ABORTED: 
       isoApp.onTestAborted(); 
       break; 

      } 
     } 
    } 


    public void onTestResult(NfcVResponse response, int currentCommand) {   
     Log.i(TAG, "onTestResult. Command: " + response.getCommand().toString() 
       + " Status: " + response.getStatus()); 

     if(testResultCallback != null){ 
      testResultCallback.onTestResult(response); 
     } 
    } 

    //Called when user click on "Run" button 
    public synchronized void runTest(HashMap<Integer, List<Byte>> testMap){ 

     this.testMap = testMap;   

     //Start last test result activity 
     Intent intent = new Intent(getApplicationContext(), TestResultsActivity.class); 
     intent.setAction(ISO15693Application.INTENT_ACTION_TEST_STARTED); 
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
     startActivity(intent); 

     //Start service responsible to run the test. 
     runTestHandler.postDelayed(new Runnable() {    
      @Override 
      public void run() { 
       startTestService();     
      } 
     }, 500); 

    } 

    /* 
    * This function will be called by ISO15693Service.Runner to run the test in another thread. 
    * Messages are sent to runTestHandler to indicate start, progress and end . 
    */ 
    public synchronized void _runTest() { 

     boolean tagLost = false; 
     commandList = buildTest(testMap); 

     int total = commandList.size(); 
     Message startMsg = new Message(); 
     startMsg.what = MSG_TEST_STARTED; 
     startMsg.arg1 = total; 
     runTestHandler.sendMessage(startMsg); 

     Log.d(TAG, "Start test Thread id: " + Thread.currentThread().getId()); 

     for(int index = 0;index < total; index++){ 
      NfcVCommand cmd = commandList.get(index); 
      NfcVResponse response = NfcVHelper.sendCommand(getNfcV(), cmd); 

      switch(response.getStatus()){ 
      case NfcHelper.NFC_STATUS_OK: 
       Log.i(TAG, "Command sent successfully"); 
       break; 
      case NfcHelper.NFC_STATUS_NO_TAG: 
       //Tag has been lost, try to reconnect 
       Log.i(TAG, "Tag has been lost, reconnect"); 
       ... 
       break; 
      case NfcHelper.NFC_STATUS_ERROR:     
      default: 
       Log.i(TAG, "Error when sent command " + response.getResponseString()); 
       break; 
      } 

      Message msg = new Message();   
      msg.what = MSG_TEST_RESULT; 
      msg.arg1 = index; 
      msg.arg2 = total; 
      msg.obj = response; 
      //Update UI with last command result    
      runTestHandler.sendMessage(msg); 

      //Even if waiting a short moment to let the message queue processing 
      //messages are handled only when the worker thread ends. 
      //The only difference is in the log message : 
      //I/Choreographer(26709): Skipped 34 frames! The application may be doing too much work on its main thread. 
      //The number of Skipped frams is bigger according to time waited. 
      /*try { 
       Thread.sleep(100); 
      } catch (InterruptedException e1) {     
      }*/ 

      //Check tag lost and user cancellation 
      ... 
     } 

     //Add results to db 
     ...   
     runTestHandler.sendEmptyMessage(MSG_TEST_TERMINATED);   
    } 
} 

서비스

public class ISO15693Service extends Service { 

    @Override 
    public void onCreate() { 
     Log.d(TAG, "onCreate"); 
     //Create updater thread 
     this.testRunner = null; 
     //get application to access preferences 
     isoApp = (ISO15693Application) getApplication(); 

     super.onCreate(); 
    } 

    @Override 
    public void onDestroy() { 

     super.onDestroy(); 
     Log.d(TAG, "onDestroy"); 

     if(this.testRunner != null){   
      this.testRunner.interrupt(); 
      this.testRunner = null; 
     } 
     this.runFlag = false; 
     this.isoApp.setTestRunning(false); 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) {   
     Log.d(TAG, "onStartCommand"); 
     //not already running ? 
     if(!this.runFlag){ 
      try{ 
       this.testRunner = new Runner(); 
       this.runFlag = true; 
       this.isoApp.setTestRunning(true);  
       this.testRunner.start();        
      } catch(IllegalThreadStateException e){     
      } 
     }   
     return super.onStartCommand(intent, flags, startId); 
    } 


    @Override 
    public IBinder onBind(Intent intent) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    private class Runner extends Thread{ 

     Runner(){ 
      super("Runner class"); 
     } 

     @Override 
     public void run() { 
      super.run(); 
      ISO15693Service is = ISO15693Service.this; 
      ISO15693Application isoApp = (ISO15693Application) is.getApplication(); 

      Log.d(TAG, "Runner.run() Thread id: " + Thread.currentThread().getId()); 

      isoApp._runTest(); 
      is.runFlag = false; 
      isoApp.setTestRunning(false);    
     } 
    } 

} 

TestResultActivity (활동 보여주는 결과) 여기

public class TestResultsActivity extends ISO15693BaseActivity 
implements ISO15693Application.TestResultCallback{ 

    ... 

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

     mainLayout = (LinearLayout) findViewById(R.id.resultLayout); 
     progressBar = (ProgressBar) findViewById(R.id.progressBar); 
     ... 
    }  

    @Override 
    public void onResume() {  
     super.onResume(); 
     isoApp.setTestResultCallback(this); 
    } 

    @Override 
    public void onPause() { 
     super.onPause(); 
     //Disable callback if activity is not at foreground 
     isoApp.setTestResultCallback(null); 
    } 

    //Add command result to the main layout 
    @Override 
    public void onTestResult(final NfcVResponse response) { 
     if(mainLayout != null){ 
      LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 
      TestResultView rv = new TestResultView(TestResultsActivity.this, response); 
      mainLayout.addView(rv, params);      
     } 
     progressBar.setProgress(progress++); 

    } 

    @Override 
    public void onTestStarted() { 
     Log.i(TAG, "onTestStarted"); 

     Log.d(TAG, "GUI Thread id: " + Thread.currentThread().getId()); 
     mainLayout.removeAllViews(); 
     progress = 0; 
     progressBar.setVisibility(View.VISIBLE); 
     progressBar.setMax(isoApp.getTestInfo().nbCommands);   
    } 

    @Override 
    public void onTestTerminated() { 
     Log.i(TAG, "onTestTerminated");   
     progressBar.setVisibility(View.GONE);     

    } 
} 

가 로그, 우리는 볼 수 있습니다"handleMessage"및 "onTestResult"는 마지막 "sendCommand"호출 후 끝에서만 호출됩니다. 그러나 그것들은 직접적으로 처리되어야하며, 아니면 약간의 지연을 가지고 처리되어야하지만 지연되지 않아야합니다. 메시지가 처리기로 보내지는 순간은 "Command sent successfully"또는 "Error when sent command ..."라는 줄에 해당합니다.

"34 개의 프레임을 건너 뛰었습니다! 응용 프로그램이 주 스레드에서 너무 많은 작업을 수행하고 있습니다."라는 메시지가 있습니다. 문제는 여기에 있으며이 메시지는 GUI가 34 프레임 동안 정지되었음을 나타냅니다. 하지만 왜 "무거운 처리"가 GUI 스레드보다 다른 스레드 (ID 69595)에서 수행 되었기 때문에 나는 이해하지 못합니다. 나는 또한 각 명령 처리 사이에 (100-1000ms) 기다리려고했지만 더 많은 "프레임"을 건너 뛰는 것을 제외하면 아무것도 변경하지 않습니다.

12-16 10:43:19.600: I/ISO15693Application(26709): Activity TestResultsActivity created 
12-16 10:43:19.615: D/TestResultsActivity(26709): GUI Thread id: 1 
12-16 10:43:20.145: D/ISO15693Service(26709): onCreate 
12-16 10:43:20.145: D/ISO15693Service(26709): onStartCommand 
12-16 10:43:20.145: D/ISO15693Application(26709): Build Test Thread id: 69595 
12-16 10:43:20.150: I/ISO15693Application(26709): Test started: 8 commands 
12-16 10:43:20.150: I/TestResultsActivity(26709): onTestStarted 
12-16 10:43:20.150: D/TestResultsActivity(26709): GUI Thread id: 1 
12-16 10:43:20.150: D/ISO15693Application(26709): Start test Thread id: 69595 
12-16 10:43:20.150: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.150: D/NfcVHelper(26709): Send command : 00 20 10 
12-16 10:43:20.185: D/NfcVHelper(26709): Response : 00 5a a5 5a a5 
12-16 10:43:20.185: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.185: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.185: D/NfcVHelper(26709): Send command : 00 21 10 5a a5 5a a5 
12-16 10:43:20.245: D/NfcVHelper(26709): Response : 00 
12-16 10:43:20.245: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.245: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.245: D/NfcVHelper(26709): Send command : 00 23 01 02 
12-16 10:43:20.290: D/NfcVHelper(26709): Response : 00 00 00 00 00 00 00 00 00 00 00 00 00 
12-16 10:43:20.290: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.290: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.290: D/NfcVHelper(26709): Send command : 00 27 af 
12-16 10:43:20.330: D/NfcVHelper(26709): Response : 00 
12-16 10:43:20.330: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.330: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.330: D/NfcVHelper(26709): Send command : 00 29 d5 
12-16 10:43:20.375: D/NfcVHelper(26709): Response : 00 
12-16 10:43:20.375: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.375: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.375: D/NfcVHelper(26709): Send command : 00 2b 
12-16 10:43:20.410: D/NfcVHelper(26709): Response : 00 xx xx xx xx xx xx xx xx xx xx xx xx xx xx 
12-16 10:43:20.410: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.410: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.410: D/NfcVHelper(26709): Send command : 00 2c 00 00 
12-16 10:43:20.450: D/NfcVHelper(26709): Response : 00 01 
12-16 10:43:20.450: I/ISO15693Application(26709): Command sent successfully 
12-16 10:43:20.450: D/NfcVHelper(26709): SendCommand Thread id: 69595 
12-16 10:43:20.450: D/NfcVHelper(26709): Send command : 00 a5 16 
12-16 10:43:20.505: W/System.err(26709): android.nfc.TagLostException: Tag was lost. 
12-16 10:43:20.505: W/System.err(26709): at android.nfc.TransceiveResult.getResponseOrThrow(TransceiveResult.java:48) 
12-16 10:43:20.505: W/System.err(26709): at android.nfc.tech.BasicTagTechnology.transceive(BasicTagTechnology.java:151) 
12-16 10:43:20.505: W/System.err(26709): at android.nfc.tech.NfcV.transceive(NfcV.java:115) 
12-16 10:43:20.505: W/System.err(26709): at em.marin.nfc.NfcVHelper.sendCommand(NfcVHelper.java:283) 
12-16 10:43:20.505: W/System.err(26709): at em.marin.iso15693test.ISO15693Application._runTest(ISO15693Application.java:447) 
12-16 10:43:20.505: W/System.err(26709): at em.marin.iso15693test.ISO15693Service$Runner.run(ISO15693Service.java:88) 
12-16 10:43:20.505: I/ISO15693Application(26709): Error when sent command IO Exception occured during transmission of the command 
12-16 10:43:20.730: I/Choreographer(26709): Skipped 34 frames! The application may be doing too much work on its main thread. 
12-16 10:43:20.795: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.795: I/ISO15693Application(26709): onTestResult. Command: Read Single Block Status: 0 
12-16 10:43:20.820: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.820: I/ISO15693Application(26709): onTestResult. Command: Write Single Block Status: 0 
12-16 10:43:20.830: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.830: I/ISO15693Application(26709): onTestResult. Command: Read Multiple Blocks Status: 0 
12-16 10:43:20.845: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.845: I/ISO15693Application(26709): onTestResult. Command: Write AFI Status: 0 
12-16 10:43:20.855: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.855: I/ISO15693Application(26709): onTestResult. Command: Write DSFI Status: 0 
12-16 10:43:20.865: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.865: I/ISO15693Application(26709): onTestResult. Command: Get System Information Status: 0 
12-16 10:43:20.875: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.875: I/ISO15693Application(26709): onTestResult. Command: Get Multiple Block Security Status Status: 0 
12-16 10:43:20.885: I/ISO15693Application(26709): handleMessage Thread id : 1 
12-16 10:43:20.885: I/ISO15693Application(26709): onTestResult. Command: Read Sig Status: 1 
12-16 10:43:20.890: I/ISO15693Application(26709): Test has been terminated successfully 
12-16 10:43:20.890: I/TestResultsActivity(26709): onTestTerminated 
12-16 10:43:20.955: D/ISO15693Service(26709): onDestroy 

제 설명이 명확 해지기를 바랍니다. 응용 프로그램의 아키텍처가 이상하게 보일 수 있지만 최대 GUI와 처리를 분리하려고했습니다. 물론 개선을위한 제안이나 더 나은 실천을 환영합니다.

나는이 포럼과 다른 사람들과 비슷한 문제를 찾기 위해 오랜 시간 동안 수색을했으나이 질문에 이미 질문을 한 경우 사전에 사과하지 않았습니다. 미안 나의 영어에 대해서, 그것은 나의 모국어가 아니다.

+0

활동의 브로드 캐스트 리시버를 사용하여 UI를 업데이트하고 서비스를 사용하여 수신자를 트리거 할 수 있습니다. – wolverine

+0

이것은 처리기를 사용할 때 예상되는 동작을 의미합니까? 내 코드에서 무엇이 잘못되었는지 이해하고 싶다. 어쨌든 솔루션 제안을 주셔서 감사합니다.하지만 NfcVResponse 객체를 브로드 캐스트 메시지에 전달하는 해결책을 찾아야합니다. 이것이 브로드 캐스트 메시지 대신 Handler를 선택하는 이유입니다. – Gojir4

+0

매우 이상하게, 나는 BroadcastReceiver로 시도해 보았고 처음 시작했을 때만 작동했다. 그리고 나서 나는 핸들러와 동일한 동작을한다. – Gojir4

답변

2

_runTest() 키워드에서 synchronized 키워드를 제거하십시오. _runTest() 메서드를 완료 할 때까지 UI 스레드를 차단하는 것으로 보이는 전체 Application 개체를 동기화하는 중입니다. for 루프의 처리기를 통해 보낸 메시지를 모두 올리는 것입니다.

게시 한 Application 클래스의 코드에있는 메소드를 동기화 할 필요가 없습니다. 이것은 runTest()으로, 특히 _runTest() (buildTest(testMap)은 개인적인 방법입니다. 대부분 _runTest()에서만 호출됩니다.)

+0

감사합니다.이 해결책입니다. 필자는 '동기화 된'키워드의 역할을 오해했습니다. 두 번 병렬로 호출되는 함수를 피하는 것이었지만 모든 객체를 차단하는 것은 아닙니다. 이제 모든 GUI가 차단 된 것을 이해했습니다. 당신도 맞습니다.이 함수들을 동기화 할 필요가 없습니다. 쓰레드가 실행 중인지 확인할 수 있습니다. 고마워. – Gojir4

관련 문제