2015-06-12 2 views
1

Я пытался выяснить самую быструю скорость, с которой камера моего телефона могла делать снимки. Объект я использую для управления камерой (без логики камеры) выглядит следующим образом:Пользовательский интерфейс блокировки андроида Android, если только не используется Синхронизированный

public class PictureTaker { 
    private boolean mIsTakingPicture; 

    public void takePicture() { 
     mIsTakingPicture = true; 
    } 

    public boolean isTakingPicture() { 
     return mIsTakingPicture; 
    } 
} 

В моей деятельности, я создаю новую нить и оставить работоспособный к нему, чтобы начать цикл и фотографировать, когда краны пользователя кнопка. Я останавливаю нить, снова нажимая кнопку.

import android.os.Handler; 
import android.os.HandlerThread; 
import android.support.v7.app.ActionBarActivity; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.View; 
import android.widget.Button; 

public class CameraTestActivity extends ActionBarActivity { 

    private HandlerThread mThread; 
    private Handler mThreadHandler; 
    private volatile boolean mTestingCameraSpeed; 
    private PictureTaker mPictureTaker; 

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

     mThread = new HandlerThread("testThread"); 
     mThread.start(); 
     mThreadHandler = new Handler(mThread.getLooper()); 
     mPictureTaker = new PictureTaker(); 
    } 

    public void toggleDoingStuff(final View view) { 
     Button b = (Button)view; 
     if(mTestingCameraSpeed) { 
      mTestingCameraSpeed = false; 
      b.setText("Start camera"); 
     } else { 
      mTestingCameraSpeed = true; 

      mThreadHandler.post(new Runnable() { 
       public void run() { 
        loopUntilStop(); 
       } 
      }); 
      b.setText("Stop camera"); 
     } 
    } 

    private void loopUntilStop() { 
     for(;;) { 
      if(!mTestingCameraSpeed) break; 

      if(!mPictureTaker.isTakingPicture()) { 
       Log.d("CameraTestActivity", "Taking picture at time " + System.currentTimeMillis()); 
       mPictureTaker.takePicture(); 
      } 
     } 
    } 
} 

С булевы атомарные, и я не возражаю хватает обновления, я не обернуть чек на mIsTestingCameraSpeed в loopUntilStop() в synchronized блоке. (EDIT: как mttdbrd и Kevin Krumwiede указали, без синхронизации или изменения переменной volatile, нить не увидит изменений).

В любом случае, приведенный выше код блокирует поток пользовательского интерфейса. Однако изменение takePicture() к

public void takePicture() { 
    mIsTakingPicture = false; 
} 

не блокирует пользовательский интерфейс, но тогда mIsTakingPicture правильно не установлен. Для предотвращения блокировки и использовать первую версию takePicture(), я должен был изменить isTakingPicture() использовать блокировку:

public boolean isTakingPicture() { 
    synchronized (this) { 
     return mIsTakingPicture; 
    } 
} 

Почему бы не используя блок стопорного пользовательского интерфейса, когда mIsTakingPicture установлен в true но не false? Я проверил Java documentation on intrinsic locks and synchronization и не нашел ничего, что могло бы объяснить это. Я также имел взглянуть на эти SO вопросы, и ни один из них не ответил на мой вопрос:

EDIT:Here - это журналы для ANR. Первые две строки показывают, что поток приостановлен, ожидая блокировки, и, смотря на строку 216, кажется, что тестовый поток действительно получает блокировку в какой-то момент. Начиная с строки 161 потока_list.cc, ОС пытается получить два разных мьютекса, но я не уверен, связаны ли они с проблемой.

ответ

2

Прежде всего, убедитесь, что вам даже разрешено использовать API-интерфейс камеры вне потока пользовательского интерфейса. Если это явно не задокументировано, вы можете предположить, что не можете. API-интерфейсы Android в основном предназначены для вызова в потоке пользовательского интерфейса.

Во-вторых, в Java существует больше возможностей для обеспечения безопасности потоков, чем атомарность; вам также нужно беспокоиться о видимости. Рабочий поток может не видеть записи в mTestingCameraSpeed, если вы не сделаете это volatile или не синхронизируйте чтения и записей. Аналогично, синхронизация чтения mIsTakingPicture бессмысленна, если вы не синхронизируете запись. (Или, опять же, сделайте это volatile.)

В-третьих, ваша проблема не имеет никакого смысла, поскольку вы ее описываете, поэтому, вероятно, это вызвано одной из вышеупомянутых проблем.

Предполагая, что вы используете Camera#takePicture(...), я бы предложил использовать один из обратных вызовов, чтобы начать делать следующее изображение. Это должно дать вам максимальную практическую ставку.

Edit: Это должно быть невозможно mThread.getLooper() вернуть null после вызова mThread.start(), и я не совсем уверен, что случилось бы, если бы это было. Так что это просто выстрел в темноте. Но попробуйте это изменить:

mThread.start(); 
    mThreadHandler = new Handler(mThread.getLooper()); 

... к этому:

mThread.start(); 
    Looper looper = mThread.getLooper(); 
    Log.d("DERP", "looper is " + looper); 
    mThreadHandler = new Handler(looper); 
+0

Благодарим за хедз-ап большинства API, предназначенных для резьбы интерфейса; Я делал снимки вне основного потока, но я думаю, я не должен полагаться на это. Все это также относится к параллелизму, но я не понимаю, почему поток пользовательского интерфейса может блокировать или заходить в ту или иную ситуацию; где он ждет? –

+0

@Shane_S Вы упомянули, что упростили свой код для этого сообщения. Может быть, рабочий поток зацикливается внутри синхронизированного блока и, следовательно, держится за замок? –

+0

Код, как написано выше, все еще блокирует (без 'synchronized' в' isTakingPicture() ', то есть). –

0

Все операции чтения и записи переменных должны быть синхронизированы. Чтение не гарантирует отражения текущего значения переменной, если только чтение не синхронизировано, даже если переменная отмечена как неустойчивая.

Книга Goetz по параллелизму очень понятна при синхронизации: если есть сомнения, синхронизируйте. Если у вас нет книги, эта статья поможет: http://www.javaworld.com/article/2075692/java-concurrency/avoid-synchronization-deadlocks.html

именно:

В общем, вы должны синхронизировать всякий раз, когда вы читаете любую переменную, которая может быть предварительно написан другой , или всякий раз, когда вы пишете какую-либо переменную, которая может быть впоследствии прочитана другой темой.

+0

Вы правы, что я должен синхронизировать чтение и запись и благодарность за статью. Но код, который блокирует, не содержит 'synchronized', поэтому где он может блокироваться? –

+0

@Shane_S Возможно, я неправильно понял вопрос, но я думаю, что вы пытаетесь прочитать переменную в одном потоке, который записывается в другой поток. Значение переменной в потоке чтения кэшируется, поэтому вы читаете устаревшие данные. Вам нужно синхронизировать, чтобы убедиться, что ваше чтение в одном потоке _синхронизировано с записью другого потока. На самом деле это не блокирует так сильно, как застрял в жесткой петле, всегда читая устаревшую «истину», которая держит ее в цикле. – mttdbrd

+0

Это имеет смысл, но так или иначе зависает нить пользовательского интерфейса. Даже если рабочий поток застрял в бесконечном цикле, я не понимаю, как это заблокирует пользовательский интерфейс. –

Смежные вопросы