2012-07-15 18 views
63

मैं एक ऐसे एप्लिकेशन को लागू करने की कोशिश कर रहा हूं जिसके लिए सतह पर कैमरा पूर्वावलोकन की आवश्यकता है। मैं चीजों को देखने के रूप में, दोनों गतिविधि और सतह lifecycles निम्नलिखित राज्यों से मिलकर बनता है:कैसे SurfaceHolder कॉलबैक गतिविधि जीवन चक्र से संबंधित हैं?

  1. जब मैं पहली बार मेरे गतिविधि का शुभारंभ: इस में onPause()->onSurfaceDestroyed()

: onResume()->onSurfaceCreated()->onSurfaceChanged()

  • जब मैं अपने गतिविधि छोड़ योजना, मैं खुले/रिलीज कैमरे जैसे संबंधित कॉल कर सकता हूं और onPause/onResume और onSurfaceCreated()/onSurfaceDestroyed() में पूर्वावलोकन शुरू/बंद कर सकता हूं।

    यह ठीक काम करता है, जब तक कि मैं स्क्रीन को लॉक नहीं करता। मैं एप्लिकेशन लॉन्च करते हैं, तो स्क्रीन को लॉक और इसे अनलॉक बाद में मैं देख रहा हूँ:

    onPause() - और स्क्रीन के बाद और कुछ नहीं अवरोधित किया गया है - अनलॉक के बाद तो onResume() - और फिर बाद कोई सतह कॉलबैक। असल में, onResume() को पावर बटन दबाए जाने के बाद बुलाया जाता है और स्क्रीन चालू होती है, लेकिन लॉक स्क्रीन अभी भी सक्रिय है, इसलिए, यह गतिविधि भी दिखाई देने से पहले है।

    इस योजना के साथ, मुझे अनलॉक करने के बाद एक ब्लैक स्क्रीन मिलती है, और कोई सतह कॉलबैक नहीं कहा जाता है।

    यहां एक कोड खंड है जिसमें कैमरे के साथ वास्तविक काम शामिल नहीं है, लेकिन SurfaceHolder कॉलबैक। मुद्दा ऊपर अपने फोन पर इस कोड के साथ भी गयी है (कॉलबैक एक सामान्य अनुक्रम में कहा जाता है जब आप प्रेस "वापस" बटन, लेकिन जब आप स्क्रीन को लॉक याद कर रहे हैं): पर क्यों

    class Preview extends SurfaceView implements SurfaceHolder.Callback { 
    
        private static final String tag= "Preview"; 
    
        public Preview(Context context) { 
         super(context); 
         Log.d(tag, "Preview()"); 
         SurfaceHolder holder = getHolder(); 
         holder.addCallback(this); 
         holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
        } 
    
        public void surfaceCreated(SurfaceHolder holder) { 
         Log.d(tag, "surfaceCreated"); 
        } 
    
        public void surfaceDestroyed(SurfaceHolder holder) { 
         Log.d(tag, "surfaceDestroyed"); 
        } 
    
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
         Log.d(tag, "surfaceChanged"); 
        } 
    } 
    

    कोई भी विचार गतिविधि रोक दिए जाने के बाद सतह अवांछित बनी हुई है? इसके अलावा, आप ऐसे मामलों में कैमरा लाइफसाइकिल कैसे संभालेंगे?

  • +0

    किस एंड्रॉइड प्लाफॉर्म/एपीआई स्तर में आप विकास कर रहे हैं? – FerranB

    उत्तर

    51

    संपादित करें: अगर targetSDK अधिक है 10 से अधिक, ऐप को नींद में डालने के लिए औरonStopSource

    मैंने अपने जिंजरब्रेड फोन पर एक छोटे से कैमरे ऐप में गतिविधि और सतह दृश्य दोनों के जीवन चक्र को देखा। आप पूरी तरह से सही हैं; फ़ोन को सोने के लिए दबाए जाने पर पावर बटन दबाए जाने पर सतह नष्ट नहीं होती है। जब फोन सो जाता है, गतिविधि करता है। (और onStop नहीं करता है।) यह onResume होता है जब फ़ोन जागता है, और जैसा कि आप इंगित करते हैं, यह तब करता है जब लॉक स्क्रीन अभी भी दिखाई दे रही है और इनपुट स्वीकार कर रही है, जो थोड़ा अजीब है। जब मैं होम बटन दबाकर गतिविधि को अदृश्य कर देता हूं, तो गतिविधि और onStop दोनों करती है। के अंत और onStop की शुरुआत के बीच इस मामले में कुछ कॉलबैक surfaceDestroyed पर कॉलबैक का कारण बनता है।यह बहुत स्पष्ट नहीं है, लेकिन यह बहुत संगत लगता है।

    जब फोन को सोने के लिए पावर बटन दबाया जाता है, जब तक कि इसे रोकने के लिए कुछ स्पष्ट रूप से नहीं किया जाता है, कैमरा चल रहा रहता है! यदि मेरे पास कैमरा प्रत्येक पूर्वावलोकन फ्रेम के लिए एक प्रति-छवि कॉलबैक करता है, तो वहां Log.d() के साथ, लॉग स्टेटमेंट आते रहते हैं जबकि फोन सोने का नाटक कर रहा है। मुझे लगता है कि बहुत स्नीकी है।

    एक और भ्रम की स्थिति के रूप में, surfaceCreated और surfaceChanged के लिए कॉलबैकonResume गतिविधि में के बाद हो, अगर सतह बनाया जा रहा है।

    एक नियम के रूप में, मैं उस कक्षा में कैमरा प्रबंधित करता हूं जो SurfaceHolder कॉलबैक लागू करता है।

    class Preview extends SurfaceView implements SurfaceHolder.Callback { 
        private boolean previewIsRunning; 
        private Camera camera; 
    
        public void surfaceCreated(SurfaceHolder holder) { 
         camera = Camera.open(); 
         // ... 
         // but do not start the preview here! 
        } 
    
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
         // set preview size etc here ... then 
         myStartPreview(); 
        } 
    
        public void surfaceDestroyed(SurfaceHolder holder) { 
         myStopPreview(); 
         camera.release(); 
         camera = null; 
        } 
    
        // safe call to start the preview 
        // if this is called in onResume, the surface might not have been created yet 
        // so check that the camera has been set up too. 
        public void myStartPreview() { 
         if (!previewIsRunning && (camera != null)) { 
          camera.startPreview(); 
          previewIsRunning = true; 
         } 
        } 
    
        // same for stopping the preview 
        public void myStopPreview() { 
         if (previewIsRunning && (camera != null)) { 
          camera.stopPreview(); 
          previewIsRunning = false; 
         } 
        } 
    } 
    

    और फिर गतिविधि में:

    @Override public void onResume() { 
        preview.myStartPreview(); // restart preview after awake from phone sleeping 
        super.onResume(); 
    } 
    @Override public void onPause() { 
        preview.myStopPreview(); // stop preview in case phone is going to sleep 
        super.onPause(); 
    } 
    

    और यह मेरे लिए ठीक काम करने के लिए लगता है। घूर्णन की घटनाएं गतिविधि को नष्ट करने और पुनर्निर्मित करने का कारण बनती हैं, जिससे सतह दृश्य को नष्ट किया जा सकता है और फिर भी बनाया जा सकता है।

    +0

    सुपर कॉल से पहले क्यों? –

    +0

    मुझे पता नहीं है कि यह महत्वपूर्ण है कि सुपर कॉल के पहले या बाद में कोड निष्पादित किया गया है या नहीं। यह सिर्फ इतना महत्वपूर्ण है कि 'super.onResume' को 'onResume' दिनचर्या में कहीं भी कहा जाता है।मुझे लगता है। – emrys57

    +0

    दरअसल यह सतहदृश्य और सतहदृश्यधारक के प्रारंभिक पर निर्भर करता है। यदि आप कुछ सिंक्रोनस कार्य करते हैं तो आप दृश्य को प्रारंभ करते हैं, फिर यह रीज़्यूम पर कभी भी कॉल नहीं करता है। सिंक्रोनस कार्य के बाद भी नहीं। लेकिन जब मैं दूसरी गतिविधि में जाता हूं और फिर से शुरू करता हूं तो यह सर्फसक्रेटेड या चेंज फ़ंक्शन पर कॉलबैक होता है। बीटीडब्ल्यू धन्यवाद! @ Emrys57। कम से कम मैंने अपनी समस्या हल की है सतहदृश्य के साथ। :) –

    1

    SurfaceHolder.Callback इसकी सतह से संबंधित है।

    स्क्रीन पर गतिविधि क्या है? यदि ऐसा है, तो SurfaceHolder नहीं होगा। कॉलबैक, क्योंकि सतह अभी भी स्क्रीन पर है।

    किसी भी SurfaceView को नियंत्रित करने के लिए, आप इसे केवल ऑन/ऑन रीज़्यूम में संभाल सकते हैं। SurfaceHolder.Callback लिए, आप इसे उपयोग कर सकते हैं सतह बदल जाता है (बनाया, sizechanged, और नष्ट कर दिया), प्रारंभ ओपन जब surfaceCreated की तरह, और ओपन नष्ट जब surfaceDestroyed, आदि

    18

    एक और सरल समाधान जो ठीक काम करता है - पूर्वावलोकन सतह की दृश्यता बदलने के लिए।

    private SurfaceView preview; 
    

    पूर्वावलोकन onCreate विधि में init है।

    @Override 
    public void onResume() { 
        preview.setVisibility(View.VISIBLE); 
        super.onResume(); 
    } 
    

    और क्रमशः में सेट दृश्यता View.GONE:

    @Override 
    public void onPause() { 
        super.onPause(); 
        preview.setVisibility(View.GONE); 
        stopPreviewAndFreeCamera(); //stop and release camera 
    } 
    
    +0

    आप एक lifesaver हैं! – gtsouk

    +0

    कैमरे 2 एपीआई के साथ इस काम की पुष्टि भी। –

    1

    दोनों सब पिछले जवाब मैं स्पष्ट रूप से मेरे कैमरे पूर्वावलोकन काम करने में कामयाब रहे, जबकि से वापस जा के लिए धन्यवाद onResume में विधि पूर्वावलोकन सतह के लिए निर्धारित View.VISIBLE या तो पृष्ठभूमि या लॉकस्क्रीन।

    जैसा कि @ e7fendy ने उल्लेख किया है, सतह दृश्य दृश्य के दौरान सतह दृश्य दृश्य स्क्रीन के रूप में दिखाई देगा क्योंकि सतह दृश्य अभी भी सिस्टम के लिए दृश्यमान है।

    इसलिए, जैसा कि @ validcat ने सलाह दी है, preview.setVisibility(View.VISIBLE); और preview.setVisibility(View.GONE); क्रमशः क्रमशः() और ऑनस्यूम() सतह दृश्य को अपने आप को रिलायट करने के लिए मजबूर करेगा और इसे कॉलबैक कॉल करेगा।

    तब तक, से समाधान @ emrys57 प्लस इन दो दृश्यता उस विधि को ऊपर कॉल स्पष्ट रूप से अपने कैमरे पूर्वावलोकन काम :)

    तो मैं केवल +1 आप में से प्रत्येक के रूप में आप सभी यह हकदार दे सकते हैं कर देगा;)

    -2

    यहां सभी कॉलबैक विधियों के लिए एक वैकल्पिक समाधान है, जो सभी गतिविधि चक्र के साथ एक ही अपरिभाषित ईवेंट ऑर्डर व्यवहार के अधीन हो सकते हैं। जब तक आप प्रत्येक कॉल के लिए सभी एंड्रॉइड कोड का निरीक्षण नहीं करेंगे, तब तक आप मूल ट्रिगर को निर्धारित करने के लिए उपयोग करते हैं और जो कार्यान्वयन को नियंत्रित करते हैं और उम्मीद करते हैं कि भविष्य में कोड आधार नहीं बदला जाता है, क्या कोई वास्तव में बता सकता है कि कॉलबैक के बीच ईवेंट ऑर्डर और गतिविधि जीवन चक्र घटनाओं की गारंटी दी जा सकती है।

    अभी विकास के उद्देश्यों के लिए आदेश के इन इंटरैक्शन को अनिर्धारित व्यवहार के रूप में संदर्भित किया जा सकता है।

    तो इस अपरिभाषित व्यवहार को हमेशा सही तरीके से संभालना होगा, जैसे ऑर्डर परिभाषित व्यवहार सुनिश्चित करने के द्वारा यह पहली जगह में कभी भी समस्या नहीं होगी।

    उदाहरण के लिए मेरा सोनी एक्सपीरिया, नींद पर, ऐप को नष्ट करके, और फिर इसे पुनरारंभ करके इसे रोकता है और इसे रोकता है, इसे मानता है या नहीं।

    मेजबान पर्यावरण उपकरणों के लिए विशेष परीक्षण निर्माण के रूप में Google को उनके एसडीके में कितना ईवेंट ऑर्डरिंग व्यवहार परीक्षण प्रदान करता है, मुझे नहीं पता, लेकिन उन्हें निश्चित रूप से यह सुनिश्चित करने के लिए प्रयास करने की आवश्यकता है कि ईवेंट ऑर्डर के व्यवहार सभी बंद हो जाएं बल्कि इस मामले पर सख्त।

    https://code.google.com/p/android/issues/detail?id=214171&sort=-opened&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened

    आयात android.util.Log; आयात android.util.SparseArray;

    /** * 2016/06/24 को वॉलीवर द्वारा बनाया गया। * * एंड्रॉइड होस्ट वातावरण, ऑनक्रेट, ऑनस्टार्ट, ऑनस्यूम, ऑन पॉज़, ऑनस्टॉप, ऑनस्टरी, * के लिए एक गतिविधि लाइफ साइकिल को निर्देशित करता है * जहां हमें अन्य अनुप्रयोगों के उपयोग के लिए स्मृति और हैंडल जारी करने की आवश्यकता होती है। * फिर से शुरू होने पर हमें इन वस्तुओं को अन्य वस्तुओं के साथ पुनर्निर्मित करने और सक्रिय करने की आवश्यकता होती है। * आम तौर पर ये अन्य ऑब्जेक्ट्स मेजबान वातावरण से कॉलबैक विधियां प्रदान करते हैं जो * ऑनक्रेटेड और ऑनड्रॉयय प्रदान करते हैं, जिसमें हम केवल ऑनक्रेटेड से इस ऑब्जेक्ट से जुड़ सकते हैं और * डायस्टोरी पर बाध्य कर सकते हैं। * इस प्रकार के कॉल बैक विधियों, चलाने के लिए शेड्यूल टाइम हमारे होस्ट वातावरण * द्वारा नियंत्रक है और उनकी गतिविधि की लाइफ साइकिल के निष्पादन के व्यवहार/आदेश की गारंटी नहीं है और इन कॉल बैक विधियों * लगातार बना हुआ है। * विकास के उद्देश्य के लिए निष्पादन के अंतःक्रियाओं और आदेश को तकनीकी रूप से अपरिभाषित * कहा जा सकता है क्योंकि यह होस्ट कार्यान्वयन कार्यान्वयनकर्ता, सैमसंग, सोनी, एचटीसी पर निर्भर करता है। * * निम्नलिखित डेवलपर दस्तावेज़ देखें: https://developer.android.com/reference/android/app/Activity.html * उद्धरण: * यदि कोई गतिविधि किसी अन्य गतिविधि द्वारा पूरी तरह से अस्पष्ट है, तो यह बंद हो जाती है। यह अभी भी सभी राज्य * और सदस्य की जानकारी को बरकरार रखता है, हालांकि, यह अब उपयोगकर्ता के लिए दृश्यमान नहीं है, इसलिए इसकी विंडो * छिपी हुई है और जब स्मृति की आवश्यकता होती है तो इसे अक्सर सिस्टम द्वारा मारा जाएगा। * EndQuato: * * यदि गतिविधि छिपी नहीं है, तो किसी भी कॉलबैक को मेजबान * सिस्टम द्वारा कॉल करने की अपेक्षा की जाती है, जिसे ऑनक्रेट और ऑनडिस्टरी विधियों इंटरफ़ेस SurfaceView कॉलबैक नहीं कहा जाएगा। * इसका मतलब है कि आपको उस ऑब्जेक्ट को रोकना होगा जो सतह 0V* पर रोकथाम में अवरुद्ध हो गया है और ऑब्जेक्ट को कभी भी पुनर्विचार नहीं करेगा क्योंकि ऑनक्रेट कॉलबैक कभी नहीं कहा जाएगा। * */

    public abstract class WaitAllActiveExecuter<Size> 
    { 
        private SparseArray<Boolean> mReferancesState = null; 
    
    // Use a dictionary and not just a counter, as hosted code 
    // environment implementer may make a mistake and then may double executes things. 
    private int mAllActiveCount = 0; 
    private String mContextStr; 
    
    public WaitAllActiveExecuter(String contextStr, int... identifiers) 
    { 
        mReferancesState = new SparseArray<Boolean>(identifiers.length); 
    
        mContextStr = contextStr; 
    
        for (int i = 0; i < identifiers.length; i++) 
         mReferancesState.put(identifiers[i], false); 
    } 
    
    public void ActiveState(int identifier) 
    { 
        Boolean state = mReferancesState.get(identifier); 
    
        if (state == null) 
        { 
         // Typically panic here referance was not registered here. 
         throw new IllegalStateException(mContextStr + "ActiveState: Identifier not found '" + identifier + "'"); 
        } 
        else if(state == false){ 
    
         mReferancesState.put(identifier, true); 
         mAllActiveCount++; 
    
         if (mAllActiveCount == mReferancesState.size()) 
          RunActive(); 
        } 
        else 
        { 
         Log.e(mContextStr, "ActivateState: called to many times for identifier '" + identifier + "'"); 
         // Typically panic here and output a log message. 
        } 
    } 
    
    public void DeactiveState(int identifier) 
    { 
        Boolean state = mReferancesState.get(identifier); 
    
        if (state == null) 
        { 
         // Typically panic here referance was not registered here. 
         throw new IllegalStateException(mContextStr + "DeActiveState: Identifier not found '" + identifier + "'"); 
        } 
        else if(state == true){ 
    
         if (mAllActiveCount == mReferancesState.size()) 
          RunDeActive(); 
    
         mReferancesState.put(identifier, false); 
         mAllActiveCount--; 
        } 
        else 
        { 
         Log.e(mContextStr,"DeActiveState: State called to many times for identifier'" + identifier + "'"); 
         // Typically panic here and output a log message. 
        } 
    } 
    
    private void RunActive() 
    { 
        Log.v(mContextStr, "Executing Activate"); 
    
        ExecuterActive(); 
    } 
    
    private void RunDeActive() 
    { 
        Log.v(mContextStr, "Executing DeActivate"); 
    
        ExecuterDeActive(); 
    } 
    
    
    abstract public void ExecuterActive(); 
    
    abstract public void ExecuterDeActive(); 
    } 
    

    कार्यान्वयन और वर्ग का उपयोग, जो या एंड्रॉयड मेजबान वातावरण इसको लागू करने की अपरिभाषित व्यवहार के साथ सौदों का उदाहरण।

    private final int mBCTSV_SurfaceViewIdentifier = 1; 
    private final int mBCTSV_CameraIdentifier = 2; 
    
    private WaitAllActiveExecuter mBindCameraToSurfaceView = 
         new WaitAllActiveExecuter("BindCameraToSurfaceViewe", new int[]{mBCTSV_SurfaceViewIdentifier, mBCTSV_CameraIdentifier}) 
    { 
        @Override 
        public void ExecuterActive() { 
    
         // Open a handle to the camera, if not open yet and the SurfaceView is already intialized. 
         if (mCamera == null) 
         { 
          mCamera = Camera.open(mCameraIDUsed); 
    
          if (mCamera == null) 
           throw new RuntimeException("Camera could not open"); 
    
          // Look at reducing the calls in the following two methods, some this is unessary. 
          setDefaultCameraParameters(mCamera); 
          setPreviewSizesForCameraFromSurfaceHolder(getSurfaceHolderForCameraPreview()); 
         } 
    
         // Bind the Camera to the SurfaceView. 
         try { 
          mCamera.startPreview(); 
          mCamera.setPreviewDisplay(getSurfaceHolderForCameraPreview()); 
         } catch (IOException e) { 
    
          e.printStackTrace(); 
          ExecuterDeActive(); 
    
          throw new RuntimeException("Camera preview could not be set"); 
         } 
        } 
    
        @Override 
        public void ExecuterDeActive() { 
    
         if (mCamera != null) 
         { 
          mCamera.stopPreview(); 
    
          mCamera.release(); 
          mCamera = null; 
         } 
        } 
    }; 
    
    @Override 
    protected void onPause() { 
    
    
        mBindCameraToSurfaceView.DeactiveState(mBCTSV_CameraIdentifier); 
    
        Log.v(LOG_TAG, "Activity Paused - After Super"); 
    } 
    
    @Override 
    public void onResume() { 
    
        mBindCameraToSurfaceView.ActiveState(mBCTSV_CameraIdentifier); 
    } 
    
    private class SurfaceHolderCallback implements SurfaceHolder.Callback 
    { 
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 
        Log.v(LOG_TAG, "Surface Changed"); 
    
        } 
    
        public void surfaceCreated(SurfaceHolder surfaceHolder) { 
    
         Log.v(LOG_TAG, "Surface Created"); 
         mBindCameraToSurfaceView.ActiveState(mBCTSV_SurfaceViewIdentifier); 
        } 
    
        public void surfaceDestroyed(SurfaceHolder arg0) { 
    
         Log.v(LOG_TAG, "Surface Destoryed"); 
         mBindCameraToSurfaceView.DeactiveState(mBCTSV_SurfaceViewIdentifier); 
        } 
    } 
    
    संबंधित मुद्दे