অগ্রগতি ডায়ালগ এবং পটভূমি থ্রেড সক্রিয় থাকাকালীন স্ক্রিন ওরিয়েন্টেশন পরিবর্তনটি কীভাবে পরিচালনা করবেন?


524

আমার প্রোগ্রামটি ব্যাকগ্রাউন্ড থ্রেডে কিছু নেটওয়ার্ক কার্যকলাপ করে। শুরু করার আগে, এটি একটি অগ্রগতি ডায়ালগ পপ আপ করে। কথোপকথনটি হ্যান্ডলারে বরখাস্ত করা হয়। ডায়লগটি চলাকালীন স্ক্রিন ওরিয়েন্টেশন পরিবর্তিত হওয়ার পরে (এবং পটভূমির থ্রেড চলমান রয়েছে) ব্যতীত এটি সমস্ত ঠিকঠাক কাজ করে। এই মুহুর্তে অ্যাপটি ক্রাশ হয়, বা ডেডলক হয় বা এমন এক অদ্ভুত পর্যায়ে চলে যায় যেখানে অ্যাপ্লিকেশনটি সমস্ত থ্রেড না মেরে দেওয়া পর্যন্ত কাজ করে না।

আমি কীভাবে স্ক্রিন ওরিয়েন্টেশন পরিবর্তন করুণভাবে পরিচালনা করতে পারি?

নীচের নমুনা কোডটি আমার বাস্তব প্রোগ্রামটি প্রায় মিলে যায়:

public class MyAct extends Activity implements Runnable {
    public ProgressDialog mProgress;

    // UI has a button that when pressed calls send

    public void send() {
         mProgress = ProgressDialog.show(this, "Please wait", 
                      "Please wait", 
                      true, true);
        Thread thread = new Thread(this);
        thread.start();
    }

    public void run() {
        Thread.sleep(10000);
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mProgress.dismiss();
        }
    };
}

স্ট্যাক:

E/WindowManager(  244): Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244): android.view.WindowLeaked: Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244):     at android.view.ViewRoot.<init>(ViewRoot.java:178)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:147)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:90)
E/WindowManager(  244):     at android.view.Window$LocalWindowManager.addView(Window.java:393)
E/WindowManager(  244):     at android.app.Dialog.show(Dialog.java:212)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:103)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:91)
E/WindowManager(  244):     at MyAct.send(MyAct.java:294)
E/WindowManager(  244):     at MyAct$4.onClick(MyAct.java:174)
E/WindowManager(  244):     at android.view.View.performClick(View.java:2129)
E/WindowManager(  244):     at android.view.View.onTouchEvent(View.java:3543)
E/WindowManager(  244):     at android.widget.TextView.onTouchEvent(TextView.java:4664)
E/WindowManager(  244):     at android.view.View.dispatchTouchEvent(View.java:3198)

আমি onSaveInstanceState- এ অগ্রগতি ডায়ালগটি খারিজ করার চেষ্টা করেছি, তবে এটি কেবল তাত্ক্ষণিক ক্রাশকে আটকাচ্ছে। পটভূমি থ্রেড এখনও চলছে, এবং ইউআই আংশিকভাবে টানা অবস্থায় রয়েছে। এটি আবার কাজ শুরু করার আগে পুরো অ্যাপটি মারতে হবে।


1
আপনি যে উত্তরগুলি পেয়েছেন তা বিবেচনা করে, আপনার গ্রহণযোগ্য উত্তরটি সেরাের পক্ষে করা উচিত, তাই না?
আরডিএস

আরও দেখুন সাবেক প্রশ্ন stackoverflow.com/questions/456211/...
RDS

3
সর্বোপরি, এই সমস্যার একটি দুর্দান্ত ব্যাখ্যা এবং সম্ভাব্য সমাধান পেয়েছি। Http://blog.doityoursedaandandroid.com/2010/11/14/handling-progress-dialogs-and-screen-orientation-changes/ এর মাধ্যমে যান লেমমে জানুন এটি সাহায্য করে কিনা।
আরকাম্যাক্স

2
এই ব্লগ পোস্টে স্ক্রিন ওরিয়েন্টেশন জুড়ে কীভাবে অ্যাসিঙ্ক্রোনাস পটভূমি কাজগুলি বজায় রাখা যায় তার একটি সম্পূর্ণ বিবরণ রয়েছে । এটা দেখ!
অ্যাড্রিয়ান সন্ন্যাস

সহজভাবে অ্যান্ড্রয়েড সেট করুন: ম্যানিফেস্টের ক্রিয়াকলাপে কনফিগারেশন = "ওরিয়েন্টেশন | স্ক্রিন সাইজ"। এটি অ্যান্ড্রয়েডটির ক্রিয়াকলাপটি পুনরায় তৈরি করতে বন্ধ করবে
জাওয়াদ জেব

উত্তর:


155

আপনি যখন অরিয়েন্টেশনগুলি স্যুইচ করবেন, অ্যান্ড্রয়েড একটি নতুন ভিউ তৈরি করবে। আপনি সম্ভবত ক্র্যাশ হচ্ছেন কারণ আপনার ব্যাকগ্রাউন্ড থ্রেডটি পুরানোটির উপরে রাষ্ট্র পরিবর্তন করার চেষ্টা করছে। (এটি আপনার সমস্যা হতে পারে কারণ আপনার পটভূমি থ্রেডটি ইউআই থ্রেডে নেই)

আমি সেই এমএইচএন্ডলারটিকে অস্থির করে তোলা এবং ওরিয়েন্টেশন পরিবর্তিত হলে আপডেট করার পরামর্শ দেব।


14
আপনি সম্ভবত ক্র্যাশটির কারণটি স্থির করেছেন। আমি ক্র্যাশ থেকে মুক্তি পেয়েছি, তবে এখনও নির্ভরযোগ্যভাবে বা ওরিয়েন্টেশন পরিবর্তনের আগে ইউআইটিকে যে অবস্থায় ছিল তা কীভাবে পুনরুদ্ধার করা যায় তা আমি এখনও বুঝতে পারি নি। তবে আপনার উত্তর আমাকে এগিয়ে নিয়ে গেছে, তাই উত্তর হিসাবে এটি প্রদান করে।
হাইক্কি টোভোনেন

4
ওরিয়েন্টেশন পরিবর্তিত হলে আপনার ক্রিয়াকলাপে একটি অন স্টার্ট পাওয়া উচিত। মূলত, আপনাকে পুরানো ডেটা ব্যবহার করে ভিউটি পুনরায় কনফিগার করতে হবে। সুতরাং আমি অগ্রগতি বার থেকে সংখ্যাসূচক স্ট্যাটাস udpates অনুরোধ এবং একটি নতুন দৃষ্টিভঙ্গি পুনর্নির্মাণের পরামর্শ দেব যখন আপনি নতুন 'অন স্টার্ট' পেয়ে যান তবে আমি নতুন স্মরণ রাখতে পারি না যদি আপনি একটি নতুন ক্রিয়াকলাপ পান তবে ডকুমেন্টেশনের মাধ্যমে কিছু শিকারের সহায়তা করা উচিত।
হাশেমান

6
এটির সাথে সম্প্রতি খেলে, আমি আপনার অ্যাপটি ওরিয়েন্টেশন পরিবর্তন করার সময় আপনি একটি নতুন ক্রিয়াকলাপটি পেতে পারতে পারেন। (আপনিও একটি নতুন দৃষ্টিভঙ্গি পেয়েছেন) আপনি যদি পুরানো ভিউ আপডেট করার চেষ্টা করেন তবে আপনি একটি ব্যতিক্রম পাবেন কারণ পুরানো ভিউটিতে একটি অবৈধ অ্যাপ্লিকেশন প্রসঙ্গ রয়েছে (আপনার পুরানো ক্রিয়াকলাপ) আপনি আমার অ্যাক্টিভিটি.জেট অ্যাপ্লিকেশন কনটেক্সট () এ পাশ করে এই বিষয়টিকে পেতে পারেন পরিবর্তে ক্রিয়াকলাপের দিকে একটি পয়েন্টার।
হাশমান

1
কেউ কি এই প্রসঙ্গে উদ্বোধনের ব্যবহার / সুবিধা ব্যাখ্যা করতে পারেন
জাওয়াদ জেব

2
@ নেপস্টার হ্যাঁ আমিও সে সম্পর্কে ভাবছিলাম। কেউ যদি অস্থিরতা সম্পর্কে ব্যাখ্যা করে থাকে তবে তা দুর্দান্ত হবে।
রেস্টইনপিস

261

সম্পাদনা করুন: গুগল ইঞ্জিনিয়াররা এই স্ট্যাকওভারফ্লো পোস্টে ডায়ান হ্যাকবর্ন (ওরফে হ্যাকবড ) দ্বারা বর্ণিত হিসাবে এই পদ্ধতির প্রস্তাব দেয় না । পরীক্ষা করে দেখুন এই ব্লগ পোস্টে আরও তথ্যের জন্য।


আপনাকে ম্যানিফেস্টে ক্রিয়াকলাপের ঘোষণায় এটি যুক্ত করতে হবে:

android:configChanges="orientation|screenSize"

সুতরাং এটি মত দেখাচ্ছে

<activity android:label="@string/app_name" 
        android:configChanges="orientation|screenSize|keyboardHidden" 
        android:name=".your.package">

বিষয়টি হ'ল সিস্টেমটি ক্রিয়াকলাপটি ধ্বংস করে যখন কনফিগারেশনের পরিবর্তন ঘটে। কনফিগারেশন পরিবর্তনগুলি দেখুন

সুতরাং এটি কনফিগারেশন ফাইলের মধ্যে রাখলে সিস্টেমটি আপনার ক্রিয়াকলাপ নষ্ট করে না। পরিবর্তে এটি onConfigurationChanged(Configuration)পদ্ধতিটি আহ্বান করে ।


21
এটি অবশ্যই সেরা সমাধান; যেহেতু এটি সহজভাবে বিন্যাসটি ঘোরাচ্ছে (আপনি যে আচরণটি প্রথমে প্রত্যাশা করেন)। কেবল অ্যান্ড্রয়েড স্থাপন করতে ভুলবেন না: কনফিগারেশন = "ওরিয়েন্টেশন | কীবোর্ডহিডেন" (যে ফোনগুলির ল্যান্ডস্কেপ কীবোর্ড রয়েছে তার
কারণেই

24
এটি আমার প্রত্যাশিত আচরণ বলে মনে হচ্ছে। তবে ডকুমেন্টেশনটি নির্দেশ করে যে ক্রিয়াকলাপটি নষ্ট হয়ে গেছে "কারণ বিন্যাসের ফাইলগুলি সহ যে কোনও অ্যাপ্লিকেশন রিসোর্স যে কোনও কনফিগারেশনের মানের উপর ভিত্তি করে পরিবর্তন করতে পারে Thus সুতরাং কনফিগারেশন পরিবর্তন পরিচালনা করার একমাত্র নিরাপদ উপায় হল সমস্ত সংস্থান পুনরুদ্ধার"। আর তাছাড়া orientation: সেখানে পরিবর্তনের কনফিগারেশনের জন্য আরো অনেক কারণ আছে keyboardHidden(আমি উইকি উত্তর ইতিমধ্যে সম্পাদনা করেছি), uiMode;, ইত্যাদি আমি এখন ভাবছি খাসি আসলে এই আছে (পরিবর্তন রাত মোড উদাহরণস্বরূপ, অথবা গাড়ি মোড থেকে বের যাচ্ছে) একটি ভাল উত্তর।
rd

116
এটি কোনও গ্রহণযোগ্য সমাধান নয়। এটি কেবল সত্যিকারের সমস্যাটিকে মুখোশ দেয়।
rf43

18
কাজ করে, তবে গুগল দ্বারা প্রস্তাবিত নয়।
এড বার্নেট

21
এখানে এই পদ্ধতির অনুসরণ করবেন না দয়া করে। ডিডোসএট্যাক পুরোপুরি ঠিক। কল্পনা করুন আপনি একটি ডাউনলোডের জন্য একটি অগ্রগতি সংলাপ তৈরি করছেন বা অন্য কোনও কিছু যা অনেক সময় নেয়। একজন ব্যবহারকারী হিসাবে আপনি সেই ক্রিয়ায় থাকবেন না এবং এটিকে তাকাবেন না। আপনি হোম স্ক্রিনে বা গেমের মতো কোনও অ্যাপ্লিকেশন বা অন্য কোনও অ্যাপ্লিকেশনে স্যুইচ করবেন বা কোনও ফোন কল আসতে পারে বা ক্ষুধার্ত এমন কোনও সংস্থান রয়েছে যা আপনার ক্রিয়াকলাপটি শেষ পর্যন্ত ধ্বংস করে দেবে। আর তাহলে কি? আপনি একই পুরানো সমস্যার মুখোমুখি হচ্ছেন যা সেই ঝরঝরে ছোট্ট কৌশলটির সাথে সমাধান করা হয়নি। ব্যবহারকারী ফিরে এলে ক্রিয়াকলাপটি আবারও তৈরি করা হবে।
টিগুচি

68

আমি এই বিষয়গুলির একটি রক-কঠিন সমাধান নিয়ে এসেছি যা জিনিসগুলির 'অ্যান্ড্রয়েড ওয়ে' এর সাথে সামঞ্জস্য করে। ইনটেন্স সার্ভিস প্যাটার্নটি ব্যবহার করে আমার সমস্ত দীর্ঘ-চলমান অপারেশন রয়েছে।

এটি হ'ল, আমার ক্রিয়াকলাপগুলি ইন্টেন্টগুলি সম্প্রচার করে, ইনট্যান্ট সার্ভিস কাজ করে, ডিবিতে ডেটা সংরক্ষণ করে এবং তারপরে স্টিকি ইন্টেন্টগুলি সম্প্রচার করে । স্টিকি অংশটি গুরুত্বপূর্ণ, যেমন ব্যবহারকারীর কাজ শুরু করার পরে এবং সেই কাজটি ইনটেন্স সার্ভিস থেকে রিয়েল টাইম সম্প্রচারিত সময়ের পরে অ্যাক্টিভিটি বন্ধ হয়ে গেলেও আমরা এখনও প্রতিক্রিয়া জানাতে পারি এবং কলিং ক্রিয়াকলাপ থেকে ডেটা নিতে পারি। ProgressDialogগুলি এই প্যাটার্নটির সাথে বেশ সুন্দরভাবে কাজ করতে পারেonSaveInstanceState()

মূলত, আপনার একটি পতাকা সংরক্ষণ করা দরকার যা আপনি সংরক্ষিত ইভেন্টের বান্ডেলে চলমান একটি অগ্রগতি ডায়ালগ রয়েছে। করো নাঅগ্রগতি ডায়ালগ অবজেক্টটি সংরক্ষণ কারণ এটি পুরো ক্রিয়াকলাপ ফাঁস করবে। অগ্রগতি ডায়ালগটিতে অবিরাম হ্যান্ডেল রাখতে, আমি এটিকে অ্যাপ্লিকেশন অবজেক্টে একটি দুর্বল রেফারেন্স হিসাবে সঞ্চয় করি। ওরিয়েন্টেশন পরিবর্তন বা অন্য কোনও কারণে যা ক্রিয়াকলাপটি থামিয়ে দেয় (ফোন কল, ব্যবহারকারী বাড়ীতে আঘাত করে etc. ইত্যাদি) এবং তারপরে আবার শুরু করুন, আমি পুরানো ডায়লগটি খারিজ করে দিয়েছি এবং সদ্য নির্মিত ক্রিয়াকলাপে একটি নতুন কথোপকথন পুনরায় তৈরি করি।

অনির্দিষ্টকালের অগ্রগতি সংলাপগুলির জন্য এটি সহজ। অগ্রগতি বার শৈলীর জন্য, আপনাকে বান্ডলে সর্বশেষ জ্ঞাত অগ্রগতি এবং আপনি স্থানীয়ভাবে স্থানীয় যে কোনও তথ্য ক্রিয়াকলাপে ব্যবহার করছেন তা অগ্রগতির খোঁজ রাখতে হবে। অগ্রগতি পুনরুদ্ধার করার সময়, আপনি এই তথ্যটি আগের মতো একই অবস্থায় অগ্রগতি বারটিকে পুনরায় স্প্যান করতে ব্যবহার করবেন এবং তারপরে বর্তমান অবস্থার ভিত্তিতে আপডেট করুন update

সুতরাং সংক্ষিপ্তসার হিসাবে, দীর্ঘমেয়াদী কাজগুলিকে ইনটেন্স সার্ভিসটিতে যুক্তিসঙ্গত ব্যবহারের সাথে যুক্তিসঙ্গত ব্যবহারের onSaveInstanceState()সাথে দক্ষতার সাথে কথোপকথনগুলি দক্ষতার সাথে রাখতে এবং ক্রিয়াকলাপের জীবনচক্রের ইভেন্টগুলি জুড়ে পুনরুদ্ধার করতে দেয়। ক্রিয়াকলাপের প্রাসঙ্গিক বিটগুলি নীচে রয়েছে। স্টিকি ইন্টেন্টগুলি যথাযথভাবে পরিচালনা করতে আপনার ব্রডকাস্টার্সিভারে আপনার যুক্তিও লাগবে, তবে এটি এটার পরিধির বাইরে।

public void doSignIn(View view) {
    waiting=true;
    AppClass app=(AppClass) getApplication();
    String logingon=getString(R.string.signon);
    app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    ...
}

@Override
protected void onSaveInstanceState(Bundle saveState) {
    super.onSaveInstanceState(saveState);
    saveState.putBoolean("waiting",waiting);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(savedInstanceState!=null) {
        restoreProgress(savedInstanceState);    
    }
    ...
}

private void restoreProgress(Bundle savedInstanceState) {
    waiting=savedInstanceState.getBoolean("waiting");
    if (waiting) {
        AppClass app=(AppClass) getApplication();
        ProgressDialog refresher=(ProgressDialog) app.Dialog.get();
        refresher.dismiss();
        String logingon=getString(R.string.signon);
        app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    }
}

একটি দুর্দান্ত সমাধান বলে মনে হচ্ছে
ডেরেকি

"ইনটেন্স সার্ভিস প্যাটার্নটি ব্যবহার করে আমার দীর্ঘকাল ধরে চলমান সমস্ত অপারেশন রয়েছে" " এটি একটি নিখুঁত সমাধান নয় কারণ এটি কামান থেকে চড়ুই এবং প্রচুর বয়লারপ্লেট কোডে গুলি চালানোর মতো, আরও বেশি কিছু আপনি দেখতে পারেন youtube.com/watch?v=NJsq0TU0qeg
কামিল নেকানোভিজ

28

আমি একই সমস্যা পূরণ। আমার ক্রিয়াকলাপটি কোনও ইউআরএল থেকে কিছু ডেটা পার্স করা দরকার এবং এটি ধীর। সুতরাং আমি এটি করার জন্য একটি থ্রেড তৈরি করেছি, তারপরে একটি অগ্রগতি ডায়ালগ দেখাব। থ্রেডটি কোনও বার্তা পোস্ট করার পরে ইউআই থ্রেডটি Handlerশেষ হয়ে যায় via ইন Handler.handleMessage, আমি থ্রেড থেকে ডেটা অবজেক্ট (এখন প্রস্তুত) পাই এবং এটি ইউআই-তে পপুলেট করব। সুতরাং এটি আপনার উদাহরণের সাথে খুব মিল।

অনেক পরীক্ষা এবং ত্রুটির পরে দেখে মনে হচ্ছে আমি এর সমাধান পেয়েছি। কমপক্ষে এখন আমি থ্রেডটি সম্পন্ন হওয়ার আগে বা পরে যেকোন মুহুর্তে স্ক্রিনটি ঘোরান। সমস্ত পরীক্ষায়, ডায়ালগটি যথাযথভাবে বন্ধ হয়ে যায় এবং সমস্ত আচরণ প্রত্যাশার মতো হয়।

আমি যা করেছি তা নীচে দেখানো হয়েছে। লক্ষ্যটি হ'ল আমার ডেটা মডেলটি পূরণ করুন ( mDataObject) এবং তারপরে এটি ইউআইতে উন্নীত করুন। অবাক না করে যে কোনও মুহুর্তে স্ক্রিন ঘোরানোর অনুমতি দেওয়া উচিত।

class MyActivity {

    private MyDataObject mDataObject = null;
    private static MyThread mParserThread = null; // static, or make it singleton

    OnCreate() {
        ...
        Object retained = this.getLastNonConfigurationInstance();
        if(retained != null) {
            // data is already completely obtained before config change
            // by my previous self.
            // no need to create thread or show dialog at all
            mDataObject = (MyDataObject) retained;
            populateUI();
        } else if(mParserThread != null && mParserThread.isAlive()){
            // note: mParserThread is a static member or singleton object.
            // config changed during parsing in previous instance. swap handler
            // then wait for it to finish.
            mParserThread.setHandler(new MyHandler());
        } else {
            // no data and no thread. likely initial run
            // create thread, show dialog
            mParserThread = new MyThread(..., new MyHandler());
            mParserThread.start();
            showDialog(DIALOG_PROGRESS);
        }
    }

    // http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html
    public Object onRetainNonConfigurationInstance() {
        // my future self can get this without re-downloading
        // if it's already ready.
        return mDataObject;
    }

    // use Activity.showDialog instead of ProgressDialog.show
    // so the dialog can be automatically managed across config change
    @Override
    protected Dialog onCreateDialog(int id) {
        // show progress dialog here
    }

    // inner class of MyActivity
    private class MyHandler extends Handler {
        public void handleMessage(msg) {
            mDataObject = mParserThread.getDataObject();
            populateUI();
            dismissDialog(DIALOG_PROGRESS);
        }
    }
}

class MyThread extends Thread {
    Handler mHandler;
    MyDataObject mDataObject;

    // constructor with handler param
    public MyHandler(..., Handler h) {
        ...
        mHandler = h;
    }

    public void setHandler(Handler h) { mHandler = h; } // for handler swapping after config change
    public MyDataObject getDataObject() { return mDataObject; } // return data object (completed) to caller

    public void run() {
        mDataObject = new MyDataObject();
        // do the lengthy task to fill mDataObject with data
        lengthyTask(mDataObject);
        // done. notify activity
        mHandler.sendEmptyMessage(0); // tell activity: i'm ready. come pick up the data.
    }
}

এটাই আমার পক্ষে কাজ করে। আমি জানি না অ্যান্ড্রয়েডের নকশাকৃত এটি "সঠিক" পদ্ধতি কিনা - তারা দাবি করে যে এই "পর্দার ঘোরার সময় ক্রিয়াকলাপটি ধ্বংস / পুনরায় তৈরি করা" আসলে জিনিসগুলিকে সহজ করে তোলে, তাই আমার ধারণা এটি খুব জটিল নয়।

আপনি যদি আমার কোডটিতে কোনও সমস্যা দেখেন তবে আমাকে জানান। যেমন উপরে বলা হয়েছে আমি সত্যিই জানি না কোনও পার্শ্ব প্রতিক্রিয়া আছে কিনা।


1
অনেক ধন্যবাদ! ইঙ্গিত onRetainNonConfigurationInstance()এবং getLastNonConfigurationInstance()আমার সমস্যা সমাধানে আমাকে সহায়তা করেছে। থাম্বস আপ!
Sven

15

মূল অনুভূত সমস্যাটি হ'ল কোডটি কোনও স্ক্রিন ওরিয়েন্টেশন পরিবর্তনে বেঁচে থাকবে না। স্পষ্টতই এটি "সমাধান করা হয়েছিল" প্রোগ্রামটি হ্যান্ডেল করে স্ক্রিন ওরিয়েন্টেশনটি নিজেই পরিবর্তিত হয়েছিল, ইউআই কাঠামোটিকে এটি করার পরিবর্তে (অনডাস্ট্রয়ে কল করার মাধ্যমে)।

আমি পেশ করব যে যদি অন্তর্নিহিত সমস্যাটি হ'ল প্রোগ্রামটি ডাস্ট্রয়ে () টিকে থাকবে না, তবে গ্রহণযোগ্য সমাধানটি কেবল একটি কর্মক্ষেত্র যা প্রোগ্রামটিকে গুরুতর অন্যান্য সমস্যা এবং দুর্বলতার সাথে ছেড়ে দেয়। মনে রাখবেন যে অ্যান্ড্রয়েড ফ্রেমওয়ার্কটি সুনির্দিষ্টভাবে বলেছে যে আপনার নিয়ন্ত্রণের বাইরে অবস্থার কারণে আপনার কার্যকলাপটি যে কোনও সময় ধ্বংস হওয়ার ঝুঁকিতে রয়েছে। অতএব, আপনার ক্রিয়াকলাপটি কেবল কোনও স্ক্রিন ওরিয়েন্টেশন পরিবর্তন নয়, কোনও কারণে অনড্রেস্ট্রয় () এবং পরবর্তী অনক্রেট () টিকে থাকতে হবে।

আপনি যদি ওপেনের সমস্যা সমাধানের জন্য স্ক্রিন ওরিয়েন্টেশন হ্যান্ডলিংকে নিজেকে পরিবর্তন করতে চান, তবে আপনাকে যাচাই করতে হবে যে অন্ডেস্ট্রয়ে () এর অন্যান্য কারণগুলির ফলে একই ত্রুটি হয় না। আপনি কি এটি করতে সক্ষম? যদি তা না হয় তবে আমি প্রশ্ন করব যে "গৃহীত" উত্তরটি আসলেই খুব ভাল একটি কিনা।


14

আমার সমাধানটি ছিল ProgressDialogআমার নিজের পেতে ক্লাসটি বাড়ানো MyProgressDialog
আমি পুনরায় সংজ্ঞায়িত করেছি show()এবং dismiss()দেখানোর আগে ওরিয়েন্টেশনটি লক করার পদ্ধতিগুলি Dialogএবং যখন Dialogবরখাস্ত করা হবে তখন এটিকে আনলক করুন । সুতরাং যখন Dialogপ্রদর্শিত হয় এবং ডিভাইসের dismiss()ওরিয়েন্টেশন পরিবর্তিত হয়, তখন পর্দার ওরিয়েন্টেশন বলা না হওয়া অবধি থাকে , তারপরে সেন্সর-মান / ডিভাইস-ওরিয়েন্টেশন অনুযায়ী স্ক্রিন-ওরিয়েন্টেশন পরিবর্তন হয়।

আমার কোডটি এখানে:

public class MyProgressDialog extends ProgressDialog {
private Context mContext;

public MyProgressDialog(Context context) {
    super(context);
    mContext = context;
}

public MyProgressDialog(Context context, int theme) {
    super(context, theme);
    mContext = context;
}

public void show() {
    if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    else
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    super.show();
}

public void dismiss() {
    super.dismiss();
    ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}

}

8

আমি এই একই সমস্যার মুখোমুখি হয়েছি এবং আমি এমন একটি সমাধান নিয়ে এসেছি যা প্রগ্রেস ডায়ালগ ব্যবহার করে না এবং আমি দ্রুত ফলাফল পেয়েছি।

আমি যা করেছি তা হ'ল একটি লেআউট তৈরি করেছিল যাতে এটিতে একটি অগ্রগতি রয়েছে।

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ProgressBar
    android:id="@+id/progressImage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    />
</RelativeLayout>

তারপরে onCreate পদ্ধতিতে নিম্নলিখিতটি করুন

public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.progress);
}

তারপরে একটি থ্রেডে লম্বা টাস্কটি করুন এবং এটি শেষ হয়ে গেলে রান্নেবলের সাথে এই ক্রিয়াকলাপের জন্য আপনি ব্যবহার করতে চান এমন বাস্তব লেআউটে কনটেন্ট ভিউ সেট করুন।

উদাহরণ স্বরূপ:

mHandler.post(new Runnable(){

public void run() {
        setContentView(R.layout.my_layout);
    } 
});

এটি আমি করেছিলাম এবং আমি পেয়েছি যে এটি প্রগ্রেস ডায়ালগ দেখানোর চেয়ে দ্রুত চলে এবং এটি কম অনুপ্রবেশকারী এবং আমার মতে আরও ভাল চেহারা আছে।

তবে আপনি যদি প্রগ্রেস ডায়ালগটি ব্যবহার করতে চান তবে এই উত্তরটি আপনার জন্য নয়।


এই সমাধানটি সাধারণ ব্যবহারের ক্ষেত্রে মার্জিত, তবে ত্রুটি রয়েছে। আপনার সম্পূর্ণ সামগ্রী দর্শনটি পুনর্নির্মাণ করতে হবে। setContentView(R.layout.my_layout);যথেষ্ট নয়; আপনি সব শ্রোতাকে, রিসেট তথ্য, ইত্যাদি সেট করতে হবে
RDS

@ আরডিএস আপনি ঠিক বলেছেন এটি সত্যিই কেবলমাত্র একটি সহজ কেসের সমাধান, বা আপনার ভিউটি প্রদর্শন করার আগে যদি আপনার অনক্রিট পদ্ধতিতে কিছু ভারী উত্তোলন করতে হয়।
পজান্নো

আমি বেশ এটি না। OnCreate () এ শ্রোতাদের সেটিংসের পরিবর্তে, যেমনটি আমরা সাধারণত করি, আমরা তাদের রান () এ সেট করতে পারি। আমি কি এখানে কিছু মিস করছি?
কোড কবি

7

আমি এর একটি সমাধান আবিষ্কার করেছি যা আমি অন্য কোথাও দেখিনি। আপনি এমন একটি কাস্টম অ্যাপ্লিকেশন অবজেক্ট ব্যবহার করতে পারেন যা জানায় যে আপনার পটভূমির কাজ চলছে কিনা, পরিবর্তে ওরিয়েন্টেশন পরিবর্তনে ধ্বংস হয়ে যাওয়া এবং পুনরায় তৈরি করা ক্রিয়াকলাপে এটি করার চেষ্টা করার পরিবর্তে। আমি এখানে এই সম্পর্কে ব্লগ ।


1
একটি কাস্টম তৈরি Applicationকরা সাধারণত বিশ্বব্যাপী প্রয়োগের স্থিতি বজায় রাখতে ব্যবহৃত হয়। আমি এটি বলি না যে এটি কাজ করে না, তবে এটি অতিরিক্ত জটিল বলে মনে হচ্ছে। ডকটি থেকে "সাধারণত সাবক্লাস অ্যাপ্লিকেশন করার প্রয়োজন হয় না।" আমি মূলত সোনসুরক্সোর উত্তর পছন্দ করি।
rd

7

আমি এই ঘূর্ণন সমস্যাটি পরিচালনা করতে আমার পদ্ধতির অবদান রাখতে চলেছি। ওপি ব্যবহার না করায় এটি প্রাসঙ্গিক নাও হতে পারে AsyncTaskতবে অন্যরাও এটি দরকারী বলে মনে করছেন। এটি বেশ সহজ তবে মনে হয় এটি আমার জন্য কাজ করবে:

কলিত নেস্টেড AsyncTaskক্লাসের সাথে আমার একটি লগইন ক্রিয়াকলাপ রয়েছে BackgroundLoginTask

আমার মধ্যে BackgroundLoginTaskআমি সাধারণের বাইরে কিছু করার করি না কল ProgressDialogকরার জন্য বরখাস্ত করার জন্য নাল চেক যোগ করা ছাড়া :

@Override
protected void onPostExecute(Boolean result)
{    
if (pleaseWaitDialog != null)
            pleaseWaitDialog.dismiss();
[...]
}

এটি এমন কেস পরিচালনা করতে হয় যেখানে ব্যাকগ্রাউন্ড টাস্কটি শেষ Activityনা হয়েই শেষ হয় এবং সুতরাং, অগ্রগতি ডায়ালগটি ইতিমধ্যে onPause()পদ্ধতি দ্বারা বাতিল করে দেওয়া হয়েছে ।

এর পরে, আমার প্যারেন্ট Activityক্লাসে, আমি আমার AsyncTaskক্লাসে গ্লোবাল স্ট্যাটিক হ্যান্ডলগুলি তৈরি করি এবং আমার ProgressDialog( AsyncTaskদেরি করা হচ্ছে, এই পরিবর্তনগুলি অ্যাক্সেস করতে পারে):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

এটি দুটি উদ্দেশ্যে পরিবেশন করে: প্রথমত, এটি আমার Activityসর্বদা AsyncTaskএকটি নতুন, পোস্ট-ঘোরানো ক্রিয়াকলাপ থেকে এমনকি অবজেক্টটিতে অ্যাক্সেসের অনুমতি দেয় । দ্বিতীয়ত, এটি আমার ঘুরানোর পরেও BackgroundLoginTaskঅ্যাক্সেস করতে এবং খারিজ করার অনুমতি দেয় ProgressDialog

এরপরে, আমি এটিতে যুক্ত করছি onPause(), যখন Activityঅগ্রভাগ ছেড়ে চলে যাব তখন অগ্রগতি ডায়ালগটি অদৃশ্য হয়ে যাবে (সেই কুৎসিত "ফোর্স ক্লোজ" ক্র্যাশ প্রতিরোধ করা):

    if (pleaseWaitDialog != null)
    pleaseWaitDialog.dismiss();

পরিশেষে, আমার onResume()পদ্ধতিতে আমার নিম্নলিখিত রয়েছে :

if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }

এটি Dialogপুনরায় তৈরি করার পরে পুনরায় প্রদর্শিত হতে দেয় Activity

পুরো ক্লাসটি এখানে:

public class NSFkioskLoginActivity extends NSFkioskBaseActivity {
    private static BackgroundLoginTask backgroundLoginTask;
    private static ProgressDialog pleaseWaitDialog;
    private Controller cont;

    // This is the app entry point.
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (CredentialsAvailableAndValidated())
        {
        //Go to main menu and don't run rest of onCreate method.
            gotoMainMenu();
            return;
        }
        setContentView(R.layout.login);
        populateStoredCredentials();   
    }

    //Save current progress to options when app is leaving foreground
    @Override
    public void onPause()
    {
        super.onPause();
        saveCredentialsToPreferences(false);
        //Get rid of progress dialog in the event of a screen rotation. Prevents a crash.
        if (pleaseWaitDialog != null)
        pleaseWaitDialog.dismiss();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }
    }

    /**
     * Go to main menu, finishing this activity
     */
    private void gotoMainMenu()
    {
        startActivity(new Intent(getApplicationContext(), NSFkioskMainMenuActivity.class));
        finish();
    }

    /**
     * 
     * @param setValidatedBooleanTrue If set true, method will set CREDS_HAVE_BEEN_VALIDATED to true in addition to saving username/password.
     */
    private void saveCredentialsToPreferences(boolean setValidatedBooleanTrue)
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE);
        SharedPreferences.Editor prefEditor = settings.edit();
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
        prefEditor.putString(USERNAME, usernameText.getText().toString());
        prefEditor.putString(PASSWORD, pswText.getText().toString());
        if (setValidatedBooleanTrue)
        prefEditor.putBoolean(CREDS_HAVE_BEEN_VALIDATED, true);
        prefEditor.commit();
    }

    /**
     * Checks if user is already signed in
     */
    private boolean CredentialsAvailableAndValidated() {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
                MODE_PRIVATE);
        if (settings.contains(USERNAME) && settings.contains(PASSWORD) && settings.getBoolean(CREDS_HAVE_BEEN_VALIDATED, false) == true)
         return true;   
        else
        return false;
    }

    //Populate stored credentials, if any available
    private void populateStoredCredentials()
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
            MODE_PRIVATE);
        settings.getString(USERNAME, "");
       EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
       usernameText.setText(settings.getString(USERNAME, ""));
       EditText pswText = (EditText) findViewById(R.id.editTextPassword);
       pswText.setText(settings.getString(PASSWORD, ""));
    }

    /**
     * Validate credentials in a seperate thread, displaying a progress circle in the meantime
     * If successful, save credentials in preferences and proceed to main menu activity
     * If not, display an error message
     */
    public void loginButtonClick(View view)
    {
        if (phoneIsOnline())
        {
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
           //Call background task worker with username and password params
           backgroundLoginTask = new BackgroundLoginTask();
           backgroundLoginTask.execute(usernameText.getText().toString(), pswText.getText().toString());
        }
        else
        {
        //Display toast informing of no internet access
        String notOnlineMessage = getResources().getString(R.string.noNetworkAccessAvailable);
        Toast toast = Toast.makeText(getApplicationContext(), notOnlineMessage, Toast.LENGTH_SHORT);
        toast.show();
        }
    }

    /**
     * 
     * Takes two params: username and password
     *
     */
    public class BackgroundLoginTask extends AsyncTask<Object, String, Boolean>
    {       
       private Exception e = null;

       @Override
       protected void onPreExecute()
       {
           cont = Controller.getInstance();
           //Show progress dialog
           String pleaseWait = getResources().getString(R.string.pleaseWait);
           String commWithServer = getResources().getString(R.string.communicatingWithServer);
            if (pleaseWaitDialog == null)
              pleaseWaitDialog= ProgressDialog.show(NSFkioskLoginActivity.this, pleaseWait, commWithServer, true);

       }

        @Override
        protected Boolean doInBackground(Object... params)
        {
        try {
            //Returns true if credentials were valid. False if not. Exception if server could not be reached.
            return cont.validateCredentials((String)params[0], (String)params[1]);
        } catch (Exception e) {
            this.e=e;
            return false;
        }
        }

        /**
         * result is passed from doInBackground. Indicates whether credentials were validated.
         */
        @Override
        protected void onPostExecute(Boolean result)
        {
        //Hide progress dialog and handle exceptions
        //Progress dialog may be null if rotation has been switched
        if (pleaseWaitDialog != null)
             {
            pleaseWaitDialog.dismiss();
                pleaseWaitDialog = null;
             }

        if (e != null)
        {
         //Show toast with exception text
                String networkError = getResources().getString(R.string.serverErrorException);
                Toast toast = Toast.makeText(getApplicationContext(), networkError, Toast.LENGTH_SHORT);
            toast.show();
        }
        else
        {
            if (result == true)
            {
            saveCredentialsToPreferences(true);
            gotoMainMenu();
            }
            else
            {
            String toastText = getResources().getString(R.string.invalidCredentialsEntered);
                Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT);
            toast.show();
            } 
        }
        }

    }
}

আমি কোনওভাবেই পাকা অ্যান্ড্রয়েড বিকাশকারী নই, তাই বিনা দ্বিধায় মন্তব্য করুন।


1
মজাদার! বিশেষত আমাদের মধ্যে অ্যাসিঙ্কটাস্ক ব্যবহার করা for আপনার সমাধানটি স্রেফ চেষ্টা করে দেখেছেন এবং বেশিরভাগ ক্ষেত্রেই এটি কাজ করে। একটি সমস্যা আছে: প্রগ্রেস ডায়ালগটি একটি ঘূর্ণনের পরে একটু আগে শেষ হবে বলে মনে হচ্ছে যখন অগ্রগতি ডায়ালগ এখনও সক্রিয় রয়েছে। আমি ঠিক কী ঘটছে এবং এর প্রতিকার কী তা দেখতে আমি প্রায় খেলতে যাচ্ছি। তবে আমি আর সেই ক্র্যাশগুলি পাচ্ছি না!
স্কট বিগস

1
একটি সমাধান পাওয়া গেছে। দেখে মনে হচ্ছে যে সমস্যাটি স্থির অগ্রগতি ডায়ালগ। যখন আবর্তনগুলি অগ্রগতি ডায়ালগে বাধা দেয়, তখন এটি কখনও কখনও নতুন ক্রিয়াকলাপে পুনরায় চালু হওয়ার পরে ড। খারিজ () পদ্ধতিটি পেয়ে যায়। প্রতিটি ক্রিয়াকলাপের সাথে তৈরি প্রগ্রেস ডায়ালগ তৈরি করে আমরা নিশ্চিত করি যে পুরানো ক্রিয়াকলাপের সাথে এই নতুন প্রগ্রেসডায়ালগটি নিহত হয় না। আমি এটিও নিশ্চিত করেছিলাম যে প্রগ্রেস ডায়ালগটি যখনই বরখাস্ত করা হবে (আবর্জনা সংগ্রহে সহায়তা করার জন্য) তা বাতিল হয়ে যাবে। সুতরাং আমাদের এখানে একটি সমাধান আছে! অ্যাসিঙ্কটাস্ক ব্যবহারকারীদের চিয়ার্স!
স্কট বিগস

4

দীর্ঘ কার্যকে একটি পৃথক শ্রেণিতে নিয়ে যান। বিষয়-পর্যবেক্ষক নিদর্শন হিসাবে এটি প্রয়োগ করুন। যখনই ক্রিয়াকলাপটি নিবন্ধক তৈরি করা হয় এবং টাস্ক ক্লাসের সাথে নিবন্ধন বন্ধ করার সময়। টাস্ক ক্লাস AsyncTask ব্যবহার করতে পারে।


1
আমি কীভাবে এটি সাহায্য করবে তা দেখছি না। আপনি কীভাবে এটি দেখছেন যে সমস্যাগুলি প্রতিরোধ করে তা আপনি আরও বিশদে ব্যাখ্যা করতে পারেন।
হাইক্কি টোভোনেন

1
যেহেতু হাসেমেন বলেছিলেন যে এটি ব্যাকএন্ডটি ইউআই উপাদানগুলি অ্যাক্সেস করতে বাধা দেয় এবং আমরা ইউআইটিকে ব্যাকএন্ড থেকে পৃথক করতে পারি, ব্যাকএন্ডটি পৃথক থ্রেডে চলে এবং স্ক্রিনটি পুনরায় ওরিয়েন্টেড করার পরেও এটি চলতে থাকে এবং স্থিতি আপডেটের জন্য ব্যাকএন্ড টাস্কের সাথে নিবন্ধিত-নিবন্ধন করুন । এটি ব্যবহার করে আমি যে সমাধান করেছি তার আসল উদাহরণটি হ'ল আমার কাছে একটি ডাউনলোড টাস্ক রয়েছে, আমি এটিকে একটি পৃথক থ্রেডে স্থানান্তরিত করেছি, যখনই থ্রেডটি তৈরি করা হয় তখন আমি এটির সাথে নিবন্ধন-নিবন্ধন করি।
বিনয়

ঠিক আছে, আমি এই সমস্যাটি পুনর্বিবেচনা করছি এবং আমার মনে হয় না আমি এখনও এই উত্তরটি পুরোপুরি বুঝতে পেরেছি। ধরুন আমাদের মূল ক্রিয়াকলাপটি একটি দীর্ঘকালীন চলমান নেটওয়ার্ক ক্রিয়াকলাপ করতে একটি অ্যাসিঙ্কটাস্ক শুরু করে আমরা স্ক্রিন ওরিয়েন্টেশন পরিবর্তনের সময় বাধা দিতে চাই না। পুরানো ক্রিয়াকলাপটি শুরু করে অ্যাসিঙ্কটাস্কে নতুন ক্রিয়াকলাপ কীভাবে বার্তা প্রেরণ করতে পারে তা আমি দেখতে পাচ্ছি না। আপনি একটি কোড উদাহরণ দিতে পারেন?
হাইক্কি টোভোনেন

@ হাইক্কি, আপনি কি বলতে চাইছেন তার নীচে আমার বাস্তবায়ন?
বেটসট্রা

4

কৌশলটি হ'ল যথাযোগ্যভাবে onPreExecute / onPostExecute চলাকালীন AsyncTask এর মধ্যে ডায়ালগটি দেখানো / বরখাস্ত করা, যদিও অরিয়েন্টেশন-পরিবর্তনের ক্ষেত্রে ক্রিয়াকলাপে ডায়ালগটির একটি নতুন উদাহরণ তৈরি / প্রদর্শন করা এবং কার্যটির সাথে তার উল্লেখটি প্রেরণ করা।

public class MainActivity extends Activity {
    private Button mButton;
    private MyTask mTask = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        MyTask task = (MyTask) getLastNonConfigurationInstance();
        if(task != null){
            mTask = task;
            mTask.mContext = this;
            mTask.mDialog = ProgressDialog.show(this, "", "", true);        
        }

        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                mTask = new MyTask(MainActivity.this);
                mTask.execute();
            }
        });
    }


    @Override
    public Object onRetainNonConfigurationInstance() {
        String str = "null";
        if(mTask != null){
            str = mTask.toString();
            mTask.mDialog.dismiss();
        }
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
        return mTask;
    }



    private class MyTask extends AsyncTask<Void, Void, Void>{
        private ProgressDialog mDialog;
        private MainActivity mContext;


        public MyTask(MainActivity context){
            super();
            mContext = context;
        }


        protected void onPreExecute() {
            mDialog = ProgressDialog.show(MainActivity.this, "", "", true);
        }

        protected void onPostExecute(Void result) {
            mContext.mTask = null;
            mDialog.dismiss();
        }


        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(5000);
            return null;
        }       
    }
}

4

আমি এটি এইভাবে সম্পন্ন করেছি:

    package com.palewar;
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

    public class ThreadActivity extends Activity {


        static ProgressDialog dialog;
        private Thread downloadThread;
        final static Handler handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {

                super.handleMessage(msg);

                dialog.dismiss();

            }

        };

        protected void onDestroy() {
    super.onDestroy();
            if (dialog != null && dialog.isShowing()) {
                dialog.dismiss();
                dialog = null;
            }

        }

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            downloadThread = (Thread) getLastNonConfigurationInstance();
            if (downloadThread != null && downloadThread.isAlive()) {
                dialog = ProgressDialog.show(ThreadActivity.this, "",
                        "Signing in...", false);
            }

            dialog = ProgressDialog.show(ThreadActivity.this, "",
                    "Signing in ...", false);

            downloadThread = new MyThread();
            downloadThread.start();
            // processThread();
        }

        // Save the thread
        @Override
        public Object onRetainNonConfigurationInstance() {
            return downloadThread;
        }


        static public class MyThread extends Thread {
            @Override
            public void run() {

                try {
                    // Simulate a slow network
                    try {
                        new Thread().sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    handler.sendEmptyMessage(0);

                } finally {

                }
            }
        }

    }

আপনি চেষ্টাও করতে পারেন এবং আমাকে জানাতে পারেন এটি আপনার পক্ষে কার্যকর হয় বা না


বিকাশকারীর পৃষ্ঠা থেকে অনডাস্ট্রয় কোডটি মোটেও
ইলোমাম্বো

আমি দৃly়ভাবে বিশ্বাস করি যে "#RetainNonCfigrationInstance ()" হল এই জাতীয় ক্ষেত্রে ব্যবহার করা পদ্ধতি ... এনআইসি কাজ
নিতিন বানসাল

আমিও একই সমস্যার মুখোমুখি হচ্ছি, যেমন শচীন গুরানানি আমি আমার সমস্যা সমাধানের জন্য স্থির ঘোষণা ব্যবহার করি using stackoverflow.com/questions/12058774/…
স্টিভেন ডু

2

আপনি যদি একটি পটভূমি তৈরি করা হলে Serviceযে সব ভারী উত্তোলন (TCP অনুরোধ / প্রতিক্রিয়া, unmarshalling) করে, Viewএবং Activityধ্বংস করা যাবে এবং পুনরায় নির্মিত জানালা লিক বা ডেটা হারানো ছাড়া। এটি অ্যান্ড্রয়েডের প্রস্তাবিত আচরণের অনুমতি দেয় যা প্রতিটি কনফিগারেশন পরিবর্তনের কোনও ক্রিয়াকলাপকে ধ্বংস করে দেয় (উদাহরণস্বরূপ প্রতিটি ওরিয়েন্টেশন পরিবর্তনের জন্য)।

এটি কিছুটা জটিল, তবে সার্ভারের অনুরোধ, ডেটা প্রাক / পোস্ট প্রসেসিং ইত্যাদির জন্য এটি সর্বোত্তম উপায় is

এমনকি Serviceআপনি কোনও সার্ভারে প্রতিটি অনুরোধ সারি করতে আপনার ব্যবহার করতে পারেন, সুতরাং এটি সহজভাবে এবং কার্যকরভাবে এই জিনিসগুলি পরিচালনা করতে সক্ষম করে।

দেব গাইডটির একটি সম্পূর্ণ অধ্যায় রয়েছেServices


Serviceএকটি এর চেয়ে বেশি কাজ AsyncTaskতবে কিছু পরিস্থিতিতে এটি আরও ভাল পদ্ধতির হতে পারে। এটি অগত্যা ভাল হয় না, তাই না? বলা হচ্ছে, আমি বুঝতে পারি না ProgressDialogযে এটি কীভাবে মুখ্য থেকে ফাঁস হয়ে গেছে তার সমস্যার সমাধান করে Activity। আপনি কোথায় তিরস্কার করবেন ProgressDialog? আপনি কোথায় তা বরখাস্ত করবেন?
rd

2

আমার একটি বাস্তবায়ন রয়েছে যা স্ক্রিন ওরিয়েন্টেশন পরিবর্তনে ক্রিয়াকলাপটি ধ্বংস হতে দেয় তবে তবুও পুনরায় তৈরি করা ক্রিয়াকলাপে সাফল্যের সাথে ডায়ালগটি ধ্বংস করে দেয়। আমি ...NonConfigurationInstanceব্যাকগ্রাউন্ড টাস্কটিকে পুনরায় তৈরি করা ক্রিয়াকলাপের সাথে সংযুক্ত করতে ব্যবহার করি । সাধারণ অ্যান্ড্রয়েড ফ্রেমওয়ার্কটি ডায়ালগটি পুনরায় তৈরি করা পরিচালনা করে, সেখানে কিছুই পরিবর্তন হয় না।

আমি 'মালিকানাধীন' ক্রিয়াকলাপের জন্য একটি ক্ষেত্র এবং এই মালিককে আপডেট করার একটি পদ্ধতি যুক্ত করে অ্যাসিঙ্কটাসকে সাবক্লাস করেছি।

class MyBackgroundTask extends AsyncTask<...> {
  MyBackgroundTask (Activity a, ...) {
    super();
    this.ownerActivity = a;
  }

  public void attach(Activity a) {
    ownerActivity = a;
  }

  protected void onPostExecute(Integer result) {
    super.onPostExecute(result);
    ownerActivity.dismissDialog(DIALOG_PROGRESS);
  }

  ...
}

আমার ক্রিয়াকলাপ শ্রেণিতে আমি backgroundTask'মালিকানাধীন' ব্যাকগ্রাউন্ড টাস্কের উল্লেখ করে একটি ক্ষেত্র যুক্ত করেছি onRetainNonConfigurationInstanceএবং এবং ব্যবহার করে এই ক্ষেত্রটি আপডেট করি getLastNonConfigurationInstance

class MyActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    ...
    if (getLastNonConfigurationInstance() != null) {
      backgroundTask = (MyBackgroundTask) getLastNonConfigurationInstance();
      backgroundTask.attach(this);
    }
  }

  void startBackgroundTask() {
    backgroundTask = new MyBackgroundTask(this, ...);
    showDialog(DIALOG_PROGRESS);
    backgroundTask.execute(...);
  }

  public Object onRetainNonConfigurationInstance() {
    if (backgroundTask != null && backgroundTask.getStatus() != Status.FINISHED)
      return backgroundTask;
    return null;
  }
  ...
}

আরও উন্নতির জন্য পরামর্শ:

  • কার্যটির backgroundTaskসাথে সম্পর্কিত কোনও স্মৃতি বা অন্যান্য সংস্থান প্রকাশের জন্য কার্য শেষ হওয়ার পরে রেফারেন্সটি সাফ করুন ।
  • সাফ করুন ownerActivityক্রিয়াকলাপটি তত্ক্ষণাত পুনরায় তৈরি করা হবে না যদি ধ্বংস হয়ে যাওয়ার আগে ব্যাকগ্রাউন্ড টাস্কে রেফারেন্সটি ।
  • BackgroundTaskএকই নিজস্ব ক্রিয়াকলাপ থেকে বিভিন্ন ধরণের কাজ চালানোর অনুমতি দেওয়ার জন্য একটি ইন্টারফেস এবং / অথবা সংগ্রহ তৈরি করুন ।

2

আপনি যদি দুটি বিন্যাস বজায় করেন তবে সমস্ত ইউআই থ্রেডটি সমাপ্ত করা উচিত।

আপনি যদি অ্যাসিনটাস্ক ব্যবহার করেন তবে আপনি বর্তমান ক্রিয়াকলাপের .cancel()পদ্ধতির অভ্যন্তরে সহজেই কল করতে পারেন onDestroy()

@Override
protected void onDestroy (){
    removeDialog(DIALOG_LOGIN_ID); // remove loading dialog
    if (loginTask != null){
        if (loginTask.getStatus() != AsyncTask.Status.FINISHED)
            loginTask.cancel(true); //cancel AsyncTask
    }
    super.onDestroy();
}

AsyncTask জন্য, এ অধ্যায় "একটি টাস্ক বাতিল করা হচ্ছে" এ আরও পড়তে এখানে

আপডেট: স্থিতি পরীক্ষা করতে শর্ত যুক্ত হয়েছে, কারণ এটি কেবল চলমান অবস্থায় থাকলে বাতিল করা যেতে পারে। আরও মনে রাখবেন যে অ্যাসিঙ্কটাস্ক কেবলমাত্র একবার কার্যকর করা যেতে পারে।


2

জেফেক্ট্রনের সমাধানটি বাস্তবায়নের চেষ্টা করা হয়েছে কারণ এটি " এই বিষয়গুলির 'অ্যান্ড্রয়েড ওয়ে'র সাথে সামঞ্জস্যপূর্ণ এমন সমস্যাগুলির একটি রক- সলিউড সমাধান ' তবে উল্লিখিত সমস্ত উপাদানগুলিকে সন্ধান করতে এবং এটি কিছুটা সময় নিয়েছে। এটি কিছুটা পৃথক করে শেষ হয়েছে এবং আমি মনে করি আরও মার্জিত, সমাধানটি এখানে সম্পূর্ণ পোস্ট করা হয়েছে।

একটি পৃথক থ্রেডে দীর্ঘকালীন চলমান কার্য সম্পাদন করতে কোনও ক্রিয়াকলাপ থেকে বরখাস্ত একটি ইনট্যান্স সার্ভিস ব্যবহার করে। পরিষেবাটি ডায়ালগ আপডেট করে এমন ক্রিয়াকলাপের স্টিকি ব্রডকাস্ট ব্রাউজগুলিকে ফিরিয়ে দেয়। ক্রিয়াকলাপটি অ্যাপ্লিকেশন অবজেক্টে বা সেভডআইনস্ট্যান্সস্টেট বান্ডিলটিতে অবিচ্ছিন্নভাবে ডেটা পাস করার প্রয়োজনীয়তা নির্মূল করতে শোডায়ালগ (), অনক্রিটডায়ালগ () এবং অনপ্রেরডায়ালগ () ব্যবহার করে। আপনার অ্যাপ্লিকেশন কীভাবে ব্যাহত হচ্ছে তা বিবেচনা করেই এটি কাজ করা উচিত।

ক্রিয়াকলাপ শ্রেণি:

public class TesterActivity extends Activity {
private ProgressDialog mProgressDialog;
private static final int PROGRESS_DIALOG = 0;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button b = (Button) this.findViewById(R.id.test_button);
    b.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            buttonClick();
        }
    });
}

private void buttonClick(){
    clearPriorBroadcast();
    showDialog(PROGRESS_DIALOG);
    Intent svc = new Intent(this, MyService.class);
    startService(svc);
}

protected Dialog onCreateDialog(int id) {
    switch(id) {
    case PROGRESS_DIALOG:
        mProgressDialog = new ProgressDialog(TesterActivity.this);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setMax(MyService.MAX_COUNTER);
        mProgressDialog.setMessage("Processing...");
        return mProgressDialog;
    default:
        return null;
    }
}

@Override
protected void onPrepareDialog(int id, Dialog dialog) {
    switch(id) {
    case PROGRESS_DIALOG:
        // setup a broadcast receiver to receive update events from the long running process
        IntentFilter filter = new IntentFilter();
        filter.addAction(MyService.BG_PROCESS_INTENT);
        registerReceiver(new MyBroadcastReceiver(), filter);
        break;
    }
}

public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.hasExtra(MyService.KEY_COUNTER)){
            int count = intent.getIntExtra(MyService.KEY_COUNTER, 0);
            mProgressDialog.setProgress(count);
            if (count >= MyService.MAX_COUNTER){
                dismissDialog(PROGRESS_DIALOG);
            }
        }
    }
}

/*
 * Sticky broadcasts persist and any prior broadcast will trigger in the 
 * broadcast receiver as soon as it is registered.
 * To clear any prior broadcast this code sends a blank broadcast to clear 
 * the last sticky broadcast.
 * This broadcast has no extras it will be ignored in the broadcast receiver 
 * setup in onPrepareDialog()
 */
private void clearPriorBroadcast(){
    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction(MyService.BG_PROCESS_INTENT);
    sendStickyBroadcast(broadcastIntent);
}}

ইনটেন্স সার্ভিস ক্লাস:

public class MyService extends IntentService {

public static final String BG_PROCESS_INTENT = "com.mindspiker.Tester.MyService.TEST";
public static final String KEY_COUNTER = "counter";
public static final int MAX_COUNTER = 100;

public MyService() {
  super("");
}

@Override
protected void onHandleIntent(Intent intent) {
    for (int i = 0; i <= MAX_COUNTER; i++) {
        Log.e("Service Example", " " + i);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(BG_PROCESS_INTENT);
        broadcastIntent.putExtra(KEY_COUNTER, i);
        sendStickyBroadcast(broadcastIntent);
    }
}}

ম্যানিফেস্ট ফাইল এন্ট্রি:

আবেদন বিভাগের আগে:

uses-permission android:name="com.mindspiker.Tester.MyService.TEST"
uses-permission android:name="android.permission.BROADCAST_STICKY"

অ্যাপ্লিকেশন বিভাগের ভিতরে

service android:name=".MyService"

2

এটি আমার প্রস্তাবিত সমাধান:

  • AsyncTask একটি টুকরা অপরিবর্তিত রাখা করতে লগ সরান অথবা, যেমন ব্যাখ্যা করেছেন এখানে । আমি বিশ্বাস করি যে সমস্ত নেটওয়ার্ক কলকে টুকরো টুকরো করে তোলা ভাল অভ্যাস। আপনি যদি ইতিমধ্যে টুকরোগুলি ব্যবহার করে থাকেন তবে তাদের মধ্যে একটির কল করার জন্য দায়বদ্ধ করা যেতে পারে। অন্যথায়, আপনি লিঙ্কিত নিবন্ধটির প্রস্তাব হিসাবে, অনুরোধটি করার জন্য কেবল একটি খণ্ড তৈরি করতে পারেন।
  • টুকরোটি টাস্ক শেষ / ব্যর্থতার সংকেত দিতে শ্রোতা ইন্টারফেস ব্যবহার করবে। সেখানে ওরিয়েন্টেশন পরিবর্তনের জন্য আপনাকে চিন্তা করতে হবে না। খণ্ডটিতে সর্বদা বর্তমান ক্রিয়াকলাপের সঠিক লিঙ্ক থাকবে এবং অগ্রগতি ডায়ালগটি নিরাপদে আবার শুরু করা যেতে পারে।
  • আপনার অগ্রগতি ডায়ালগটিকে আপনার শ্রেণীর সদস্য করুন। আসলে আপনার সমস্ত সংলাপের জন্য এটি করা উচিত। অনপেজ পদ্ধতিতে আপনার সেগুলি বরখাস্ত করা উচিত, অন্যথায় আপনি কনফিগারেশন পরিবর্তনে একটি উইন্ডো ফাঁস করবেন। ব্যস্ত রাষ্ট্রটি খণ্ড দ্বারা রাখা উচিত। যখন খণ্ডটি ক্রিয়াকলাপের সাথে সংযুক্ত থাকে, আপনি কলটি এখনও চলমান থাকলে, আপনি আবার অগ্রগতি ডায়ালগ আনতে পারেন। void showProgressDialog()এই উদ্দেশ্যে খণ্ড-ক্রিয়াকলাপ শ্রোতা ইন্টারফেসে একটি পদ্ধতি যুক্ত করা যেতে পারে।

নিখুঁত সমাধান, তবে কেন এই উত্তরটি অন্যের থেকে ছায়াবিচ্ছন্ন তা বুঝতে পারছেন না !!!
কালোকারা

2

আমিও একই পরিস্থিতির মুখোমুখি হয়েছি। আমি যা করেছি তা সম্পূর্ণ প্রয়োগে আমার অগ্রগতি ডায়ালগের জন্য কেবল একটি উদাহরণ পেয়েছিল।

প্রথমত, কেবলমাত্র একটি উদাহরণ পেতে আমি একটি ডায়ালগসিংটন ক্লাস তৈরি করেছি (একক প্যাটার্ন)

public class DialogSingleton
{
    private static Dialog dialog;

    private static final Object mLock = new Object();
    private static DialogSingleton instance;

    private DialogSingleton()
    {

    }

    public static DialogSingleton GetInstance()
    {
        synchronized (mLock)
        {
            if(instance == null)
            {
                instance = new DialogSingleton();
            }

            return instance;
        }
    }

    public void DialogShow(Context context, String title)
    {
        if(!((Activity)context).isFinishing())
        {
            dialog = new ProgressDialog(context, 2);

            dialog.setCanceledOnTouchOutside(false);

            dialog.setTitle(title);

            dialog.show();
        }
    }

    public void DialogDismiss(Context context)
    {
        if(!((Activity)context).isFinishing() && dialog.isShowing())
        {
            dialog.dismiss();
        }
    }
}

আমি এই ক্লাসে যেমন দেখি, গুণ হিসাবে আমার অগ্রগতি ডায়ালগ রয়েছে। প্রতিবার যখনই আমি একটি অগ্রগতি কথোপকথন দেখাতে চাই তখন আমি অনন্য উদাহরণ পেয়ে একটি নতুন প্রগ্রেস ডায়ালগ তৈরি করি।

DialogSingleton.GetInstance().DialogShow(this, "My title here!");

যখন আমি ব্যাকগ্রাউন্ড টাস্কটি শেষ করি, আমি আবার অনন্য উদাহরণটি কল করি এবং এর সংলাপটি খারিজ করি।

DialogSingleton.GetInstance().DialogDismiss(this);

আমি আমার ভাগ করা পছন্দগুলিতে ব্যাকগ্রাউন্ড টাস্কের স্থিতি সংরক্ষণ করি। আমি যখন স্ক্রিনটি ঘোরান, তখন আমি জিজ্ঞাসা করি যে এই ক্রিয়াকলাপটির জন্য আমার কোনও কাজ চলছে: (অনক্রিট)

if(Boolean.parseBoolean(preference.GetValue(IS_TASK_NAME_EXECUTED_KEY, "boolean").toString()))
{
    DialogSingleton.GetInstance().DialogShow(this, "Checking credentials!");
} // preference object gets the info from shared preferences (my own implementation to get and put data to shared preferences) and IS_TASK_NAME_EXECUTED_KEY is the key to save this flag (flag to know if this activity has a background task already running).

যখন আমি কোনও পটভূমি টাস্ক চালানো শুরু করি:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, true, "boolean");

DialogSingleton.GetInstance().DialogShow(this, "My title here!");

আমি যখন কোনও পটভূমি টাস্ক চালানো শেষ করি:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, false, "boolean");

DialogSingleton.GetInstance().DialogDismiss(ActivityName.this);

আমি আসা করি এটা সাহায্য করবে.


2

এটি একটি খুব পুরানো প্রশ্ন যা কোনও কারণে সাইডবারে উঠে এসেছিল।

ক্রিয়াকলাপটি পূর্বগ্রাউন্ডে থাকা অবস্থায় যদি ব্যাকগ্রাউন্ড টাস্কটি কেবল টিকে থাকে তবে এই বিকাশকারী গাইড এবং অসংখ্য প্রশ্নোত্তর হিসাবে বর্ণিত হিসাবে "নতুন" সমাধানটি হ'ল পটভূমির থ্রেডটিকে (বা AsyncTaskসম্ভবত:) একটি বজায় রাখা টুকরোয় হোস্ট করা হবে ।

কনফিগারেশনের পরিবর্তনের জন্য ক্রিয়াকলাপটি ধ্বংস হয়ে গেলে একটি রক্ষিত খণ্ডটি টিকে থাকে, তবে যখন ব্যাকগ্রাউন্ডে বা পিছনের স্ট্যাকটিতে ক্রিয়াকলাপটি ধ্বংস হয় তখন তা নয় । সুতরাং, ব্যাকগ্রাউন্ড টাস্কটি যদি isChangingConfigurations()মিথ্যা থাকে তবে বাধা দেওয়া উচিত onPause()


2

আমি অ্যান্ড্রয়েডে এক নবীন এবং আমি এটি চেষ্টা করেছি এবং এটি কাজ করেছে।

public class loadTotalMemberByBranch extends AsyncTask<Void, Void,Void> {
        ProgressDialog progressDialog = new ProgressDialog(Login.this);
        int ranSucess=0;
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            progressDialog.setTitle("");    
            progressDialog.isIndeterminate();
            progressDialog.setCancelable(false);
            progressDialog.show();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            progressDialog.dismiss();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        }
}

1

আমি সব চেষ্টা করেছি। দিন কাটছে পরীক্ষায়। আমি কার্যকলাপটি ঘোরানো থেকে আটকাতে চাইনি। আমার দৃশ্যটি ছিল:

  1. ব্যবহারকারীকে গতিশীল তথ্য দেখানো একটি অগ্রগতি ডায়ালগ। যেমন: "সার্ভারের সাথে সংযুক্ত হচ্ছে ...", "ডেটা ডাউনলোড হচ্ছে ..." ইত্যাদি,
  2. একটি থ্রেড ভারী জিনিসগুলি করছে এবং ডায়ালগটি আপডেট করছে
  3. ফলাফলটি শেষে ইউআই আপডেট করা হচ্ছে।

সমস্যাটি ছিল, স্ক্রিনটি ঘোরানোর সময়, বইয়ের প্রতিটি সমাধান ব্যর্থ হয়েছিল। এমনকি অ্যাসিঙ্কটাস্ক ক্লাসের সাথেও, যা এই পরিস্থিতিগুলি মোকাবেলার সঠিক Android পদ্ধতি। স্ক্রিনটি ঘোরানোর সময়, বর্তমান সূত্রটি যে প্রারম্ভিক থ্রেডটি কাজ করছে, তা শেষ হয়ে গেছে, এবং যে ডায়ালগটি প্রদর্শিত হচ্ছে তাতে গণ্ডগোল। সমস্যাটি সর্বদা ডায়লগ ছিল, আমি কোডটিতে কতগুলি কৌশল যুক্ত করেছি তা বিবেচনা না করেই (থ্রেডগুলি চালানোর ক্ষেত্রে নতুন প্রসঙ্গে প্রবর্তন করা, আবর্তনের মাধ্যমে থ্রেডের রাজ্যগুলি বজায় রাখা ইত্যাদি)। শেষে কোড জটিলতা সর্বদা বিশাল ছিল এবং সর্বদা এমন কিছু ছিল যা ভুল হতে পারে।

আমার পক্ষে একমাত্র সমাধানটি ছিল কার্যকলাপ / ডায়ালগ কৌশল। এটি সহজ এবং প্রতিভা এবং এটি সমস্ত ঘূর্ণন প্রমাণ:

  1. একটি ডায়ালগ তৈরি করে এটি দেখাতে বলার পরিবর্তে, এমন একটি ক্রিয়াকলাপ তৈরি করুন যা ম্যানিফেস্টে অ্যান্ড্রয়েড: থিম = "@ অ্যান্ড্রয়েড: স্টাইল / থিম.ডায়ালগ" দিয়ে সেট করা আছে create সুতরাং, এটি কেবল একটি কথোপকথনের মতো দেখাচ্ছে।

  2. স্টার্টঅ্যাক্টিভিটি ফর রিসাল্ট (আপনারঅ্যাক্টিভিটিডায়ালগ, আপনার কোড) দিয়ে শোডায়ালগ (DIALOG_ID) প্রতিস্থাপন করুন;

  3. এক্সিকিউটিভ থ্রেড (এমনকি ত্রুটিগুলি) থেকে ফলাফল পেতে এবং ইউআই আপডেট করার জন্য কলিং ক্রিয়াকলাপে অনঅ্যাক্টিভিটি রেজাল্ট ব্যবহার করুন।

  4. আপনার 'ক্রিয়াকলাপ ডায়ালগ' এ, দীর্ঘ কর্ম সম্পাদন করতে থ্রেড বা AsyncTask ব্যবহার করুন এবং স্ক্রিনটি ঘোরানোর সময় "ডায়ালগ" স্থিতি সংরক্ষণ করতে "RetainNonConfigrationInstance "ব্যবহার করুন।

এটি দ্রুত এবং সূক্ষ্ম কাজ করে। আমি এখনও অন্য কাজের জন্য ডায়লগ এবং স্ক্রিনে ধ্রুবক ডায়ালগের প্রয়োজন হয় না এমন কিছু জন্য অ্যাসিঙ্কটাস্ক ব্যবহার করি। তবে এই দৃশ্যের সাথে আমি সর্বদা ক্রিয়াকলাপ / ডায়ালগ প্যাটার্নে যাই।

এবং, আমি এটি চেষ্টা করি নি, তবে কলিং ক্রিয়াকলাপটি ঘোরানোর সুযোগ দেওয়ার সাথে থ্রেড চলমান অবস্থায় জিনিসগুলিকে গতি বাড়িয়ে দেওয়ার পরেও সেই ক্রিয়াকলাপ / ডায়ালগটি ঘোরানো থেকে আটকাতে পারা সম্ভব।


সেই প্যারামিটারগুলি বাদ দিয়ে খুব ভাল অবশ্যই একটি দিয়ে যেতে হবে Intent, যা Objectঅনুমোদিতদের চেয়ে বেশি বিধিনিষেধযুক্তAsyncTask
rd

@ রুই আমিও গত বছরের জন্য এই পদ্ধতিটি ব্যবহার করেছি। এটি এখন আমার কাছে হয়েছে যদিও এটিও ভুল। যদি এই সমস্যাটি এই 'সমাধান' করার উপায় হত তবে গুগল কেন একটি ডায়ালগ করবে? আমি যে সমস্যাটি দেখতে পাচ্ছি তা হল আপনি যদি ক্রিয়াকলাপ থেকে অ্যাক্টিভিটিবি (থিম.ডায়ালগ) খুলেন তবে অ্যাক্টিভিটিএকে ক্রিয়াকলাপের স্ট্যাকের উপরে নামানো হয় সুতরাং প্রয়োজনে ওএস দ্বারা হত্যা করার জন্য প্রস্তুত হিসাবে চিহ্নিত করা হয়েছে। অতএব, আপনার যদি দীর্ঘ চলমান প্রক্রিয়া থাকে এবং কোনও ধরণের ত্রুটিযুক্ত অগ্রগতি 'ডায়লগ' দেখানো হয় এবং এটি খুব বেশি সময় নেয় এবং স্মৃতিশক্তি কম চলে ... অ্যাক্টিভিটিএ মারা যায় এবং অগ্রগতি সম্পন্ন হওয়ার পরেও ফিরে আসার মতো কিছুই নেই।
rf43

1

এই দিনগুলিতে এই ধরণের সমস্যাগুলি পরিচালনা করার জন্য আরও একটি স্বতন্ত্র উপায় রয়েছে। সাধারণ পদ্ধতিটি হ'ল:

1. আপনার ডেটা ইউআই থেকে সঠিকভাবে বিচ্ছিন্ন হয়েছে তা নিশ্চিত করুন:

ব্যাকগ্রাউন্ড প্রক্রিয়া যেকোনো কিছু অবশ্যই একটি রক্ষণাবেক্ষণে থাকা উচিত Fragment(এটি সেট করুন Fragment.setRetainInstance()এটি আপনার 'ধ্রুবক ডেটা স্টোরেজ'তে পরিণত হয় যেখানে আপনি যে কোনও ডেটা বজায় রাখতে চান এমন কোনও ডেটা সংরক্ষণ করা হয় kept ওরিয়েন্টেশন পরিবর্তন ইভেন্টের পরে, এটি Fragmentএখনও এর আসলটিতে অ্যাক্সেসযোগ্য হবে) একটি FragmentManager.findFragmentByTag()কলের মাধ্যমে জানিয়ে দিন (আপনি যখন এটি তৈরি করবেন তখন আপনাকে এটিকে একটি আইডি নয় এমন ট্যাগ দেওয়া উচিত কারণ এটি কোনও সংযুক্ত নয় View)।

সঠিকভাবে এটি করা এবং কেন এটি সেরা বিকল্প সে সম্পর্কে তথ্যের জন্য হ্যান্ডলিং রানটাইম পরিবর্তনগুলি বিকাশিত গাইড দেখুন ।

2. নিশ্চিত হয়ে নিন যে আপনি ব্যাকগ্রাউন্ড প্রক্রিয়া এবং আপনার ইউআইয়ের মধ্যে সঠিক এবং নিরাপদে হস্তক্ষেপ করছেন:

আপনাকে অবশ্যই আপনার লিঙ্কিং প্রক্রিয়াটি বিপরীত করতে হবে । এই মুহুর্তে আপনার ব্যাকগ্রাউন্ড প্রক্রিয়া নিজেকে একটি সংযুক্ত করে View- পরিবর্তে আপনার Viewপটভূমি প্রক্রিয়াতে নিজেকে সংযুক্ত করা উচিত। এটা আরও বোধগম্য করে তোলে? Viewযেহেতু পটভূমি প্রক্রিয়া উপর নির্ভরশীল নয় 'র কর্ম, পটভূমি প্রক্রিয়া উপর নির্ভরশীল View.এই মাধ্যম একটি প্রমিত লিংক পরিবর্তন Listenerইন্টারফেস। আপনার প্রক্রিয়াটি বলুন (এটি শ্রেণি যাই হোক না কেন - এটি একটি AsyncTask, Runnableবা যাই হোক না কেন) একটি সংজ্ঞায়িত করে OnProcessFinishedListener, প্রক্রিয়াটি সম্পন্ন হওয়ার পরে এটি উপস্থিত থাকলে শ্রোতাকে কল করা উচিত।

এই উত্তরটি কাস্টম শ্রোতাদের কীভাবে করবেন তার একটি সুন্দর সংক্ষিপ্ত বিবরণ।

৩. যখনই ইউআই তৈরি করা হয় তখনই আপনার ইউআইটিকে ডেটা প্রক্রিয়াতে লিঙ্ক করুন (ওরিয়েন্টেশন পরিবর্তন সহ):

এখন আপনার আবশ্যক Viewকাঠামো যাই হোক না কেন এটি ব্যাকগ্রাউন্ড টাস্ককে ইন্টারফেস করার বিষয়ে অবশ্যই চিন্তিত হওয়া উচিত । আপনি যদি নিজের ওরিয়েন্টেশন পরিবর্তনগুলি সঠিকভাবে পরিচালনা করছেন ( configChangesহ্যাক লোকেরা সবসময়ই সুপারিশ করে না) তবে আপনার Dialogসিস্টেমটি পুনরায় তৈরি করা হবে। এটি গুরুত্বপূর্ণ, এর অর্থ হ'ল ওরিয়েন্টেশন পরিবর্তনের সাথে সাথে আপনার সমস্ত Dialogজীবনচক্রের পদ্ধতিগুলি আবার স্মরণ করা হয়। সুতরাং এই যে কোনও পদ্ধতিতে ( onCreateDialogসাধারণত একটি ভাল জায়গা), আপনি নিম্নলিখিতগুলির মতো কল করতে পারেন:

DataFragment f = getActivity().getFragmentManager().findFragmentByTag("BACKGROUND_TAG");
if (f != null) {
    f.mBackgroundProcess.setOnProcessFinishedListener(new OnProcessFinishedListener() {
        public void onProcessFinished() {
            dismiss();
        }
    });
 }

শ্রোতাদের আপনার পৃথক বাস্তবায়নে সবচেয়ে উপযুক্ত যেখানে সেট করার সিদ্ধান্ত নেওয়ার জন্য টুকরো টুকরো জীবনচক্রটি দেখুন ।

এই প্রশ্নটিতে জিজ্ঞাসা করা জেনেরিক সমস্যার একটি দৃ and় এবং সম্পূর্ণ সমাধান প্রদানের জন্য এটি একটি সাধারণ পদ্ধতি। আপনার পৃথক দৃশ্যের উপর নির্ভর করে এই উত্তরে সম্ভবত কিছু ছোটখাটো টুকরোগুলি রয়েছে, তবে সঠিকভাবে ওরিয়েন্টেশন পরিবর্তন ইভেন্টগুলি পরিচালনা করার জন্য এটি সাধারণত সবচেয়ে সঠিক পন্থা।


1

ওরিয়েন্টেশন পরিবর্তনের সময় থ্রেডগুলি পরিচালনা করার জন্য আমি সহজ সমাধান খুঁজে পেয়েছি। আপনি কেবল আপনার ক্রিয়াকলাপ / খণ্ডটির স্থিতিশীল রেফারেন্স রাখতে পারেন এবং ইউআই-তে অভিনয় করার আগে এটি বাতিল হয়ে যায় কিনা তা যাচাই করতে পারেন। আমি চেষ্টা করে দেখতেও চেষ্টা করি:

 public class DashListFragment extends Fragment {
     private static DashListFragment ACTIVE_INSTANCE;

     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ACTIVE_INSTANCE = this;

        new Handler().postDelayed(new Runnable() {
            public void run() {
                try {
                        if (ACTIVE_INSTANCE != null) {
                            setAdapter(); // this method do something on ui or use context
                        }
                }
                catch (Exception e) {}


            }
        }, 1500l);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        ACTIVE_INSTANCE = null;
    }


}

1

আপনি যদি কোনও সংলাপের সংলাপের পরিবর্তনের ইভেন্টগুলি সনাক্তকরণের সাথে লড়াই করে থাকেন তবে একটি ক্রমাগত রেফারেন্সের প্রস্তাবনাটি এই পদ্ধতিটি উত্তেজনাপূর্ণভাবে ভালভাবে কাজ করে। আমি এটি ব্যবহার করি কারণ আমার নিজস্ব ডায়ালগ ক্লাস রয়েছে যা একাধিক বিভিন্ন ক্রিয়াকলাপে প্রদর্শিত হতে পারে তাই এটি কোন ক্রিয়াকলাপটিতে প্রদর্শিত হচ্ছে তা আমি সর্বদা জানি না this এই পদ্ধতির সাহায্যে আপনার অ্যান্ড্রয়েড ম্যানিফেস্ট পরিবর্তন করতে হবে না, ক্রিয়াকলাপের রেফারেন্সগুলি সম্পর্কে চিন্তা করুন, এবং আপনার কাস্টম সংলাপের দরকার নেই (যেমন আমার আছে)। আপনার প্রয়োজন তবে একটি কাস্টম সামগ্রী দেখুন যাতে আপনি সেই নির্দিষ্ট ভিউটি ব্যবহার করে ওরিয়েন্টেশন পরিবর্তনগুলি সনাক্ত করতে পারেন। এখানে আমার উদাহরণ:

সেটআপ

public class MyContentView extends View{
    public MyContentView(Context context){
        super(context);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig){
        super.onConfigurationChanged(newConfig);

        //DO SOMETHING HERE!! :D
    }
}

বাস্তবায়ন 1 - সংলাপ

Dialog dialog = new Dialog(context);
//set up dialog
dialog.setContentView(new MyContentView(context));
dialog.show();

বাস্তবায়ন 2 - সতর্কতা ডায়ালগ.বিল্ডার

AlertDialog.Builder builder = new AlertDialog.Builder(context);
//set up dialog builder
builder.setView(new MyContentView(context));        //Can use this method
builder.setCustomTitle(new MycontentView(context)); // or this method
builder.build().show();

বাস্তবায়ন 3 - অগ্রগতি ডায়ালগ / সতর্কতা ডায়ালগ

ProgressDialog progress = new ProgressDialog(context);
//set up progress dialog
progress.setView(new MyContentView(context));        //Can use this method
progress.setCustomTitle(new MyContentView(context)); // or this method
progress.show();

1

আমি যখন এটির মুখোমুখি হয়েছি তখন এটি আমার সমাধান: ProgressDialogকোনও Fragmentশিশু নয়, তাই কনফিগারেশনের পরিবর্তনের জন্য ডায়ালগটি দেখানোর পরিবর্তে আমার কাস্টম ক্লাসটি ProgressDialogFragmentপ্রসারিত করতে DialogFragmentপারে।

import androidx.annotation.NonNull;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle; 
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;

 /**
 * Usage:
 * To display the dialog:
 *     >>> ProgressDialogFragment.showProgressDialogFragment(
 *              getSupportFragmentManager(), 
 *              "fragment_tag", 
 *              "my dialog title", 
 *              "my dialog message");
 *              
 * To hide the dialog
 *     >>> ProgressDialogFragment.hideProgressDialogFragment();
 */ 


public class ProgressDialogFragment extends DialogFragment {

    private static String sTitle, sMessage;
    private static ProgressDialogFragment sProgressDialogFragment;

    public ProgressDialogFragment() {
    }

    private ProgressDialogFragment(String title, String message) {
        sTitle = title;
        sMessage = message;
    }


    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return ProgressDialog.show(getActivity(), sTitle, sMessage);
    }

    public static void showProgressDialogFragment(FragmentManager fragmentManager, String fragmentTag, String title, String message) {
        if (sProgressDialogFragment == null) {
            sProgressDialogFragment = new ProgressDialogFragment(title, message);
            sProgressDialogFragment.show(fragmentManager, fragmentTag);

        } else { // case of config change (device rotation)
            sProgressDialogFragment = (ProgressDialogFragment) fragmentManager.findFragmentByTag(fragmentTag); // sProgressDialogFragment will try to survive its state on configuration as much as it can, but when calling .dismiss() it returns NPE, so we have to reset it on each config change
            sTitle = title;
            sMessage = message;
        }

    }

    public static void hideProgressDialogFragment() {
        if (sProgressDialogFragment != null) {
            sProgressDialogFragment.dismiss();
        }
    }
}

চ্যালেঞ্জটি ছিল ডায়ালগটি এখনও প্রদর্শিত থাকলেও, স্ক্রিনের ঘূর্ণনের সময় ডায়লগ শিরোনাম এবং বার্তাটি ধরে রাখা

এটি সমাধানের জন্য 2 টি পন্থা রয়েছে:

প্রথম পদ্ধতির: মেনিফেস্ট ফাইলে কনফিগার পরিবর্তনের সময় স্থিতি ধরে রাখতে ডায়ালগটি ব্যবহার করে এমন কার্যকলাপ করুন:

android:configChanges="orientation|screenSize|keyboardHidden"

এই পদ্ধতির গুগল পছন্দ করে না।

দ্বিতীয় পদ্ধতির: ক্রিয়াকলাপের onCreate()পদ্ধতিতে, আপনাকে শিরোনাম এবং বার্তাটি দিয়ে DialogFragmentপুনরায় বিল্ডিং করে আপনার ধরে রাখতে হবে ProgressDialogFragmentনীচেরটি যদি নিখরচায় থাকে savedInstanceStateনা:

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

 if (savedInstanceState != null) {
      ProgressDialogFragment saveProgressDialog = (ProgressDialogFragment) getSupportFragmentManager()
              .findFragmentByTag("fragment_tag");
      if (saveProgressDialog != null) {
          showProgressDialogFragment(getSupportFragmentManager(), "fragment_tag", "my dialog title", "my dialog message");
      }
  }
}

0

অনেক 'দ্রুত এবং নোংরা' সত্য বলে মনে হচ্ছে তাই দয়া করে ত্রুটিগুলি চিহ্নিত করুন তবে আমি যা দেখেছি তা হ'ল ...

আমার অ্যাসিঙ্কটাস্কের অনপস্টএক্সেকিউট পদ্ধতির মধ্যে, আমি কেবল প্রগতি ডায়ালগের জন্য একটি চেষ্টা / ক্যাচ ব্লকে (একটি খালি ক্যাচ দিয়ে) 'মুছে ফেলা' এবং তারপরে উত্থাপিত ব্যতিক্রমটিকে কেবল উপেক্ষা করেছি। এটি করতে ভুল বলে মনে হচ্ছে তবে কোনও খারাপ প্রভাব নেই বলে মনে হচ্ছে (কমপক্ষে আমি যা করছি তার জন্য যা অন্তত আমার দীর্ঘকালীন ক্যোয়ারির ফলাফল হিসাবে অতিরিক্ত হিসাবে পাস করা অন্য ক্রিয়াকলাপটি শুরু করার জন্য)


আপনি কি বলছেন যে অ্যান্ড্রয়েড প্ল্যাটফর্মগুলি জানালার ফাঁস হওয়ার সময় আসলে কোনও মেমরি ফাঁস হয় না?
rd

আমার অগ্রগতি ডায়ালগটি ক্রিয়াকলাপের শ্রেণীর সদস্য পরিবর্তনশীল তাই আমি ধরে নিয়েছিলাম যে কার্যকলাপটি ধ্বংস হয়ে গেলে এবং পুনরায় তৈরি করা হলে তা আবর্জনা সংগ্রহ করা হবে এবং কোনও ফাঁস হবে না। আমি কি ভূল?
সাইমন

হ্যাঁ, আমি মনে করি এটি ভুল। আপনি যেমনটি বলেন, এর Activityকাছে একটি উল্লেখ রয়েছে Dialog। যখন কনফিগারেশন পরিবর্তন করা হয়, প্রথমটি Activityধ্বংস হয়, যার অর্থ সমস্ত ক্ষেত্র সেট করা আছে null। তবে নিম্ন-স্তরেরও WindowManagerএকটি রেফারেন্স রয়েছে Dialog(যেহেতু এটি এখনও বাতিল হয়নি)। নতুন Activityএকটি নতুন Dialog(ইন preExecute()) তৈরি করার চেষ্টা করে এবং উইন্ডো ম্যানেজার আপনাকে মারাত্মক ব্যতিক্রম ঘটায় যাতে আপনি এটি করতে বাধা দেন। প্রকৃতপক্ষে, যদি এটি হয় Dialogতবে প্রাথমিকভাবে কোনও রেফারেন্স রেখে পরিষ্কারভাবে ধ্বংস করার কোনও উপায় থাকবে না Activity। আমি কি সঠিক?
rd

0

সবচেয়ে সহজ এবং সবচেয়ে নমনীয় সমাধান হ'ল প্রগ্রেসবারের স্থিতিশীল রেফারেন্স সহ অ্যাসিঙ্কটাস্ক ব্যবহার করা । এটি প্রাচীর পরিবর্তন সমস্যাগুলির জন্য একটি এনক্যাপসুলেটেড এবং এভাবে পুনরায় ব্যবহারযোগ্য সমাধান সরবরাহ করে। এই সমাধানটি ইন্টারনেট ডাউনলোড, সার্ভিসের সাথে যোগাযোগ , এবং ফাইল সিস্টেম স্ক্যান সহ বিভিন্ন ধরণের অ্যাসক্রোনাস কাজের জন্য আমাকে ভালভাবে সরবরাহ করেছে । সমাধানটি একাধিক অ্যান্ড্রয়েড সংস্করণ এবং ফোন মডেলগুলিতে ভালভাবে পরীক্ষা করা হয়েছে। DownloadFile.java তে নির্দিষ্ট আগ্রহের সাথে একটি সম্পূর্ণ ডেমো এখানে পাওয়া যাবে

আমি নিম্নলিখিতটি একটি ধারণার উদাহরণ হিসাবে উপস্থাপন করি

public class SimpleAsync extends AsyncTask<String, Integer, String> {
    private static ProgressDialog mProgressDialog = null;
    private final Context mContext;

    public SimpleAsync(Context context) {
        mContext = context;
        if ( mProgressDialog != null ) {
            onPreExecute();
        }
    }

    @Override
    protected void onPreExecute() {
        mProgressDialog = new ProgressDialog( mContext );
        mProgressDialog.show();
    }

    @Override
    protected void onPostExecute(String result) {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress( progress[0] );
    }

    @Override
    protected String doInBackground(String... sUrl) {
        // Do some work here
        publishProgress(1);
        return null;
    }

    public void dismiss() {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
        }
    }
}

অ্যান্ড্রয়েড ক্রিয়াকলাপের ব্যবহার সহজ

public class MainActivity extends Activity {
    DemoServiceClient mClient = null;
    DownloadFile mDownloadFile = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        mDownloadFile = new DownloadFile( this );

        Button downloadButton = (Button) findViewById( R.id.download_file_button );
        downloadButton.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDownloadFile.execute( "http://www.textfiles.com/food/bakebred.txt");
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        mDownloadFile.dismiss();
    }
}

0

আপনি যখন অরিয়েন্টেশন পরিবর্তন করেন, অ্যান্ড্রয়েড সেই ক্রিয়াকলাপটিকে হত্যা করে এবং নতুন ক্রিয়াকলাপ তৈরি করে। আমি আরএক্স জাভা দিয়ে রেট্রোফিট ব্যবহার করার পরামর্শ দিই। যা স্বয়ংক্রিয়ভাবে ক্র্যাশ হ্যান্ডেল করে।

রিট্রোফিট কল করার সময় এই পদ্ধতিটি ব্যবহার করুন।

। সাবস্ক্রাইবঅন (শিডিউলার.ও ())

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.