Aparat na Androida Android.hardware.Camera jest przestarzały

97

jeśli android.hardware.Camerajest przestarzała i nie możesz użyć zmiennej Camera, jaka byłaby alternatywa dla tego?

raja121
źródło
1
Miałem ten problem z aplikacją i uznałem to za bardzo pomocne. Jeśli używasz intencji, jesteś ograniczony. Więc ten samouczek wyjaśnia alternatywę: developer.android.com/guide/topics/media/ ...
Ronaldo Bahia

Odpowiedzi:

103

Dokumentacja API

Według przewodnika dla programistów Androida dla android.hardware.Camera, stwierdzają:

W przypadku nowych aplikacji zalecamy korzystanie z nowego interfejsu API android.hardware.camera2 .

Na stronie informacyjnej na temat android.hardware.camera2(link powyżej) znajduje się:

Pakiet android.hardware.camera2 zapewnia interfejs do poszczególnych kamer podłączonych do urządzenia z systemem Android. Zastępuje wycofaną klasę Camera.

Problem

Po sprawdzeniu tej dokumentacji przekonasz się, że implementacja tych dwóch interfejsów API aparatu jest bardzo różna.

Na przykład włączenie orientacji aparatu android.hardware.camera

@Override
public int getOrientation(final int cameraId) {
    Camera.CameraInfo info = new Camera.CameraInfo();
    Camera.getCameraInfo(cameraId, info);
    return info.orientation;
}

Przeciw android.hardware.camera2

@Override
public int getOrientation(final int cameraId) {
    try {
        CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
        String[] cameraIds = manager.getCameraIdList();
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraIds[cameraId]);
        return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    } catch (CameraAccessException e) {
        // TODO handle error properly or pass it on
        return 0;
    }
}

Utrudnia to przełączanie się z jednej aplikacji na drugą i pisanie kodu, który obsługuje obie implementacje.

Zwróć uwagę, że w tym pojedynczym przykładzie kodu musiałem już obejść fakt, że stary interfejs API kamery działa z intprymitywami dla identyfikatorów kamer, podczas gdy nowy działa z Stringobiektami. W tym przykładzie szybko to naprawiłem, używając int jako indeksu w nowym API. Jeśli zwrócona kamera nie zawsze jest w tej samej kolejności, spowoduje to już problemy. Alternatywnym podejściem jest praca z obiektami String i reprezentacją String starych int cameraIDs, co jest prawdopodobnie bezpieczniejsze.

Jeden w pobliżu

Teraz, aby obejść tę ogromną różnicę, możesz najpierw zaimplementować interfejs i odwołać się do tego interfejsu w swoim kodzie.

Tutaj wymienię kod dla tego interfejsu i dwie implementacje. Możesz ograniczyć implementację do tego, z czego faktycznie korzystasz z interfejsu API aparatu, aby ograniczyć ilość pracy.

W następnej sekcji szybko wyjaśnię, jak załadować jeden lub drugi.

Interfejs opakowujący wszystko, czego potrzebujesz, aby ograniczyć ten przykład, mam tutaj tylko 2 metody.

public interface CameraSupport {
    CameraSupport open(int cameraId);
    int getOrientation(int cameraId);
}

Teraz mamy klasę dla starego interfejsu API sprzętowego aparatu:

@SuppressWarnings("deprecation")
public class CameraOld implements CameraSupport {

    private Camera camera;

    @Override
    public CameraSupport open(final int cameraId) {
        this.camera = Camera.open(cameraId);
        return this;
    }

    @Override
    public int getOrientation(final int cameraId) {
       Camera.CameraInfo info = new Camera.CameraInfo();
       Camera.getCameraInfo(cameraId, info);
       return info.orientation;
    }
}

I jeszcze jeden dla nowego interfejsu API sprzętowego:

public class CameraNew implements CameraSupport {

    private CameraDevice camera;
    private CameraManager manager;

    public CameraNew(final Context context) {
        this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    }

    @Override
    public CameraSupport open(final int cameraId) {
        try {
            String[] cameraIds = manager.getCameraIdList();
            manager.openCamera(cameraIds[cameraId], new CameraDevice.StateCallback() {
                @Override
                public void onOpened(CameraDevice camera) {
                    CameraNew.this.camera = camera;
                }

                @Override
                public void onDisconnected(CameraDevice camera) {
                    CameraNew.this.camera = camera;
                    // TODO handle
                }

                @Override
                public void onError(CameraDevice camera, int error) {
                    CameraNew.this.camera = camera;
                    // TODO handle
                }
            }, null);
        } catch (Exception e) {
            // TODO handle
        }
        return this;
    }

    @Override
    public int getOrientation(final int cameraId) {
        try {
            String[] cameraIds = manager.getCameraIdList();
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraIds[cameraId]);
            return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
        } catch (CameraAccessException e) {
            // TODO handle
            return 0;
        }
    }
}

Ładowanie odpowiedniego API

Teraz, aby załadować swoją klasę CameraOldlub CameraNewklasę, musisz sprawdzić poziom interfejsu API, ponieważ CameraNewjest dostępny tylko od poziomu API 21.

Jeśli masz już skonfigurowaną iniekcję zależności, możesz to zrobić w swoim module podczas dostarczania CameraSupportimplementacji. Przykład:

@Module public class CameraModule {

    @Provides
    CameraSupport provideCameraSupport(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return new CameraNew(context);
        } else {
            return new CameraOld();
        }
    } 
}

Jeśli nie używasz DI, możesz po prostu stworzyć narzędzie lub użyć wzorca Factory, aby stworzyć właściwy. Ważną częścią jest sprawdzenie poziomu API.

Społeczność
źródło
25
A jeśli muszę obsługiwać interfejs API systemu Android na poziomie niższym niż 21?
niveuseverto
1
@Angelius być może ta dokumentacja byłaby pomocna developer.android.com/guide/topics/media/camera.html - ale to może być osobne pytanie lub poszukać pytań dotyczących konieczności używania wycofanych zmiennych.
@Angelius tutaj jest kilka informacji na temat @SuppressWarningstego stackoverflow
5
Myślę nie tylko o użyciu klas @deprecated, ale o tym, jak stworzyć aplikację z kompatybilnością wsteczną? jakaś oficjalna pomoc w tej sprawie? Mam o tym pewien pomysł: Interfejs ICamera, który był wspierany przez obiekt Camera odpowiadający aktualnej wersji telefonu, ale jest to trochę proste i trudne do utrzymania ...
niveuseverto
@Angelius to, co opisujesz, może być osobnym pytaniem (sprawdź, czy zostało zadane wcześniej).
5

W obliczu tego samego problemu , obsługując starsze urządzenia przez przestarzały interfejs API aparatu i potrzebując nowego interfejsu API Camera2 zarówno dla obecnych urządzeń, jak i dla przyszłych; Natknąłem się na te same problemy - i nie znalazłem biblioteki innej firmy, która łączyłaby 2 interfejsy API, prawdopodobnie dlatego, że są one bardzo różne, zwróciłem się do podstawowych zasad OOP .

Te dwa interfejsy API są znacząco różne, co sprawia, że ​​ich wymiana jest problematyczna dla obiektów klientów oczekujących interfejsów prezentowanych w starym API. Nowe API ma różne obiekty z różnymi metodami, zbudowane przy użyciu innej architektury. Uwielbiam Google, ale ragnabbit! to frustrujące.

Dlatego stworzyłem interfejs skupiający się tylko na funkcjach aparatu, których potrzebuje moja aplikacja, i stworzyłem proste opakowanie dla obu interfejsów API, które implementują ten interfejs. W ten sposób moja aktywność kamery nie musi przejmować się tym, na której platformie działa ...

Założyłem również Singletona do zarządzania API (-ami); tworzenie instancji opakowania starszego interfejsu API z moim interfejsem dla starszych urządzeń z systemem Android oraz klasy opakowania nowego interfejsu API dla nowszych urządzeń korzystających z nowego interfejsu API. Singleton ma typowy kod, który pobiera poziom interfejsu API, a następnie instanuje poprawny obiekt.

Ten sam interfejs jest używany przez obie klasy opakowujące , więc nie ma znaczenia, czy aplikacja działa na Jellybean czy Marshmallow - o ile interfejs dostarcza mojej aplikacji to, czego potrzebuje z dowolnego interfejsu API aparatu, używając tych samych sygnatur metod; kamera działa w aplikacji w ten sam sposób zarówno w nowszych, jak i starszych wersjach Androida.

Singleton może również wykonywać pewne czynności niezwiązane z interfejsami API - takie jak wykrywanie, że na urządzeniu rzeczywiście znajduje się kamera, i zapisywanie w bibliotece multimediów.

Mam nadzieję, że ten pomysł ci pomoże.

Robert Sherman
źródło
Na przykład:public interface AllCameraInterface { void open(); boolean setDirection(); Bitmap preview(); Bitmap takePhoto(); void close(); }
Robert Sherman
np. public interface AllCameraInterface { void open(); Bitmap takePhoto(); void close(); etc... } public class NCamera implements AllCameraInterface... public class OCamera implements AllCameraInterface... public class AllCamera { private static AllCamera ourInstance = new AllCamera(); public static AllCamera getInstance() {...} private AllCameraInterface camera; private AllCamera() { if (android.os.Build.VERSION.SDK_INT <= 20) { camera = new OCamera(); } else { camera = new NCamera(); } }metoda zwrotu ...
Robert Sherman
najwyraźniej nie wolno łamać linii w komentarzach ;-), ale to naprawdę działa.
Robert Sherman
4
dlaczego nie dołączyć kodów w komentarzach bezpośrednio do odpowiedzi?
Angel Koh
@RobertSherman Cześć Robert, czy możesz mi pomóc ponownie napisać ten mały fragment na nowy camera2? Jestem naprawdę zdezorientowany ... Potrzebuję tylko enableAutofocusmetody, aby otworzyć kamerę i ustawić jej ostrość: stackoverflow.com/questions/19076316/ ...
0

Teraz musimy używać android.hardware.camera2 jako android.hardware.Camera jest przestarzała, która będzie działać tylko z API> 23 FlashLight

   public class MainActivity extends AppCompatActivity {

     Button button;

     Boolean light=true;

     CameraDevice cameraDevice;

     private CameraManager cameraManager;

     private CameraCharacteristics cameraCharacteristics;

     String cameraId;

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button=(Button)findViewById(R.id.button);
        cameraManager = (CameraManager) 
        getSystemService(Context.CAMERA_SERVICE);
        try {
          cameraId = cameraManager.getCameraIdList()[0];
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(light){
                    try {

                        cameraManager.setTorchMode(cameraId,true);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }

                    light=false;}
                    else {

                    try {

                      cameraManager.setTorchMode(cameraId,false);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }


                    light=true;
                    }


            }
        });
    }
}
abhay rastogi Codeblended
źródło
0

Podane tutaj odpowiedzi dotyczące tego, którego interfejsu API aparatu należy użyć, są nieprawidłowe. Albo lepiej powiedzieć, że są niewystarczające.

Niektóre telefony (na przykład Samsung Galaxy S6) mogą być powyżej poziomu API 21, ale nadal mogą nie obsługiwać interfejsu API Camera2.

CameraCharacteristics mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
Integer level = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
    return false;
}

CameraManager w Camera2Api zawiera metodę odczytywania charakterystyk aparatu. Powinieneś sprawdzić, czy mądre urządzenie obsługuje Camera2 Api, czy nie.

Ale jest więcej problemów do rozwiązania, jeśli naprawdę chcesz, aby działał w poważnej aplikacji: na przykład opcja automatycznego flashowania może nie działać w przypadku niektórych urządzeń lub poziom naładowania baterii telefonu może stworzyć wyjątek Runtime w aparacie lub telefonie może zwrócić nieprawidłowy identyfikator aparatu itp.

Najlepszym rozwiązaniem jest więc posiadanie mechanizmu awaryjnego, ponieważ z jakiegoś powodu Camera2 nie uruchamia się, możesz wypróbować Camera1, a jeśli to również się nie powiedzie, możesz zadzwonić do Androida, aby otworzyć domyślną kamerę.

Oguz Ozcan
źródło
0
 if ( getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {

          CameraManager cameraManager=(CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);


           try {
               String cameraId = cameraManager.getCameraIdList()[0];
               cameraManager.setTorchMode(cameraId,true);
           } catch (CameraAccessException e) {
               e.printStackTrace();
           }


 }
Rohith S
źródło