วิธีจัดการ AsyncTask ระหว่างการหมุนหน้าจอ


88

ฉันอ่านมากเกี่ยวกับวิธีบันทึกสถานะอินสแตนซ์ของฉันหรือวิธีจัดการกับกิจกรรมของฉันที่ถูกทำลายระหว่างการหมุนหน้าจอ

ดูเหมือนจะมีความเป็นไปได้มากมาย แต่ฉันยังไม่ทราบว่าอันไหนดีที่สุดในการดึงผลลัพธ์ของ AsyncTask

ฉันมี AsyncTasks บางอย่างที่เพิ่งเริ่มต้นอีกครั้งและเรียกใช้isFinishing()เมธอดของกิจกรรมและหากกิจกรรมเสร็จสิ้นพวกเขาจะไม่อัปเดตอะไรเลย

ปัญหาคือฉันมีงานหนึ่งที่ร้องขอไปยังบริการบนเว็บที่อาจล้มเหลวหรือประสบความสำเร็จและการเริ่มงานใหม่จะทำให้ผู้ใช้เสียเงิน

คุณจะแก้ปัญหานี้อย่างไร? ข้อดีหรือข้อเสียของแนวทางแก้ไขที่เป็นไปได้คืออะไร?


1
ดูคำตอบของฉันที่นี่ คุณอาจพบข้อมูลนี้เกี่ยวกับสิ่งที่setRetainInstance(true)เป็นประโยชน์จริง
Timmmm

สิ่งที่ฉันจะทำก็แค่ใช้บริการท้องถิ่นที่ดำเนินการประมวลผล (ในเธรด) ที่ asyncTask ของคุณกำลังทำอยู่ หากต้องการแสดงผลลัพธ์ให้ถ่ายทอดข้อมูลไปยังกิจกรรมของคุณ ตอนนี้กิจกรรมมีหน้าที่แสดงข้อมูลเท่านั้นและการประมวลผลจะไม่หยุดชะงักด้วยการหมุนหน้าจอ
ใครสักคน

แล้วการใช้ AsyncTaskLoader แทน AsyncTask ล่ะ ??
Sourangshu Biswas

คำตอบ:


6

คำแนะนำแรกของฉันคือเพื่อให้แน่ใจว่าคุณจำเป็นต้องรีเซ็ตกิจกรรมของคุณในการหมุนหน้าจอ (พฤติกรรมเริ่มต้น) ทุกครั้งที่ฉันมีปัญหาเกี่ยวกับการหมุนเวียนฉันได้เพิ่มแอตทริบิวต์นี้ลง<activity>ในแท็กของฉันใน AndroidManifest.xml และก็เรียบร้อยดี

android:configChanges="keyboardHidden|orientation"

มันดูแปลก ๆ แต่สิ่งที่ส่งผลต่อonConfigurationChanged()วิธีการของคุณถ้าคุณไม่ได้จัดหามันก็ไม่ได้ทำอะไรเลยนอกจากวัดเค้าโครงใหม่ซึ่งดูเหมือนจะเป็นวิธีที่เหมาะสมอย่างสมบูรณ์ในการจัดการการหมุนเกือบตลอดเวลา .


5
แต่จะป้องกันไม่ให้กิจกรรมเปลี่ยนเค้าโครง ดังนั้นจึงบังคับให้ผู้ใช้ใช้อุปกรณ์ของตนในทิศทางที่กำหนดโดยแอปพลิเคชันของคุณไม่ใช่ตามความต้องการของเขา
Janusz

77
การใช้เทคนิคนี้จะป้องกันไม่ให้คุณใช้ทรัพยากรเฉพาะการกำหนดค่าได้อย่างง่ายดาย ตัวอย่างเช่นหากคุณต้องการให้เค้าโครงหรือสิ่งที่วาดหรือสตริงของคุณหรืออะไรก็ตามที่แตกต่างกันในแนวตั้งและทิวทัศน์คุณจะต้องใช้พฤติกรรมเริ่มต้น การลบล้างการเปลี่ยนแปลงการกำหนดค่าควรทำได้เฉพาะในกรณีที่เฉพาะเจาะจงมากเท่านั้น (เกมเว็บเบราว์เซอร์ ฯลฯ ) และไม่ใช่จากความเกียจคร้านหรือความสะดวกสบายเพราะคุณกำลังบีบบังคับตัวเอง
Romain Guy

38
นั่นเป็นเพียงแค่นั้น Romain "ถ้าคุณต้องการให้เค้าโครงหรือสิ่งที่วาดได้หรือสตริงของคุณหรืออะไรก็ตามที่แตกต่างกันในภาพบุคคลและทิวทัศน์คุณจะต้องการพฤติกรรมเริ่มต้น" ฉันเชื่อว่าเป็นกรณีการใช้งานที่หายากกว่าที่คุณคิดไว้มาก สิ่งที่คุณเรียกว่า "กรณีเฉพาะ" คือนักพัฒนาส่วนใหญ่ที่ฉันเชื่อ การใช้เลย์เอาต์แบบสัมพัทธ์ที่ทำงานได้ในทุกมิติเป็นแนวทางปฏิบัติที่ดีที่สุดและไม่ใช่เรื่องยาก การพูดถึงความขี้เกียจเป็นเรื่องที่เข้าใจผิดอย่างมากเทคนิคเหล่านี้มีไว้เพื่อปรับปรุงประสบการณ์ของผู้ใช้ไม่ลดเวลาในการพัฒนา
Jim Blackler

2
ฉันพบว่ามันใช้งานได้สมบูรณ์แบบสำหรับ LinearLayout แต่เมื่อใช้ RelativeLayout มันจะไม่วาดโครงร่างใหม่อย่างถูกต้องเมื่อเปลี่ยนเป็นโหมดแนวนอน (อย่างน้อยก็ไม่ใช่ใน N1) ดูคำถามนี้: stackoverflow.com/questions/2987049/…
JohnRock

9
ฉันเห็นด้วยกับ Romain (เขารู้ว่าเขากำลังพูดถึงอะไรเขาพัฒนาระบบปฏิบัติการ) จะเกิดอะไรขึ้นเมื่อคุณต้องการพอร์ตแอปพลิเคชันไปยังแท็บเล็ตและ UI ของคุณดูแย่มากเมื่อยืดออก หากคุณใช้แนวทางของคำตอบนี้คุณจะต้องเข้ารหัสโซลูชันทั้งหมดใหม่เพราะคุณใช้แฮ็คขี้เกียจนี้
Austyn Mahoney

46

คุณสามารถตรวจสอบวิธีการที่ฉันจัดการAsyncTaskและการเปลี่ยนแปลงการวางแนวทางที่code.google.com/p/shelves มีหลายวิธีในการทำวิธีที่ฉันเลือกในแอพนี้คือการยกเลิกงานที่กำลังทำงานอยู่บันทึกสถานะและเริ่มงานใหม่ด้วยสถานะที่บันทึกไว้เมื่อActivityสร้างใหม่ มันง่ายที่จะทำมันทำงานได้ดีและเป็นโบนัสที่จะดูแลการหยุดงานของคุณเมื่อผู้ใช้ออกจากแอพ

คุณยังสามารถใช้onRetainNonConfigurationInstance()เพื่อส่งต่อของคุณAsyncTaskไปยังใหม่ได้Activity(ระวังอย่าให้รั่วก่อนหน้าActivityนี้ด้วยวิธีนี้)


1
ฉันลองแล้วหมุนระหว่างการค้นหาหนังสือขัดจังหวะและให้ผลลัพธ์น้อยกว่าตอนที่ไม่หมุนเสียด้วย
max4ever

1
ฉันไม่พบการใช้งาน AsyncTask เพียงครั้งเดียวในรหัสนั้น มี UserTask คลาสที่มีลักษณะคล้ายกัน โครงการนี้เกิดขึ้นก่อน AsyncTask หรือไม่
devconsole

7
AsyncTask มาจาก UserTask เดิมทีฉันเขียน UserTask สำหรับแอปของฉันเองและเปลี่ยนเป็น AsyncTask ในภายหลัง ขอโทษด้วยที่ฉันลืมมันเปลี่ยนชื่อแล้ว
Romain Guy

@RomainGuy สวัสดีหวังว่าคุณจะสบายดี ตามคำขอรหัส 2 ของคุณจะถูกส่งไปยังเซิร์ฟเวอร์แม้ว่าในงานแรกจะถูกยกเลิก แต่ก็ไม่สามารถยกเลิกได้สำเร็จ ฉันไม่รู้ว่าทำไม คุณช่วยบอกฉันได้ไหมว่ามีวิธีใดในการแก้ไขปัญหานี้
iamcrypticcoder

10

นี่เป็นคำถามที่น่าสนใจที่สุดที่ฉันเคยเห็นเกี่ยวกับ Android !!! อันที่จริงฉันกำลังมองหาวิธีแก้ปัญหาในช่วงหลายเดือนที่ผ่านมา ยังไม่ได้แก้ไข.

ระวังเพียงแค่ลบล้างไฟล์

android:configChanges="keyboardHidden|orientation"

ของไม่เพียงพอ

พิจารณากรณีที่ผู้ใช้รับสายในขณะที่ AsyncTask ของคุณกำลังทำงานอยู่ คำขอของคุณอยู่ระหว่างดำเนินการโดยเซิร์ฟเวอร์ดังนั้น AsyncTask กำลังรอการตอบกลับ ในช่วงเวลานี้แอปของคุณทำงานอยู่เบื้องหลังเนื่องจากแอปโทรศัพท์เพิ่งมาอยู่เบื้องหน้า ระบบปฏิบัติการอาจฆ่ากิจกรรมของคุณเนื่องจากอยู่ในพื้นหลัง


6

เหตุใดคุณจึงไม่อ้างอิงถึง AsyncTask ปัจจุบันบน Singleton ที่ Android จัดเตรียมไว้ให้เสมอ

เมื่อใดก็ตามที่งานเริ่มต้นบน PreExecute หรือบนตัวสร้างคุณกำหนด:

((Application) getApplication()).setCurrentTask(asyncTask);

เมื่อใดก็ตามที่เสร็จสิ้นคุณตั้งค่าเป็นโมฆะ

ด้วยวิธีนี้คุณจะมีข้อมูลอ้างอิงที่ช่วยให้คุณทำสิ่งต่างๆเช่น onCreate หรือ onResume ตามที่เหมาะสมกับตรรกะเฉพาะของคุณ

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

หากเป็นโมฆะคุณจะรู้ว่าขณะนี้ไม่มีใครทำงานอยู่!

:-)


จะได้ผลหรือไม่ มีใครทดสอบเรื่องนี้บ้าง? ระบบจะยังคงฆ่างานนี้อยู่หรือไม่หากโทรศัพท์หยุดชะงักหรือหากเราไปที่กิจกรรมใหม่แล้วย้อนกลับไป
Robert

6
Applicationอินสแตนซ์มีวงจรชีวิตของตัวเองซึ่ง OS ก็สามารถฆ่าได้เช่นกันดังนั้นวิธีแก้ปัญหานี้อาจทำให้เกิดบั๊กที่ยากต่อการสร้างซ้ำ
Vit Khudenko

7
ฉันคิดว่า: ถ้าแอปพลิเคชันถูกฆ่าแอปทั้งหมดจะถูกฆ่า (ดังนั้น AsyncTasks ทั้งหมดด้วย)?
manmal

ฉันคิดว่าแอปพลิเคชั่นนั้นสามารถถูกฆ่าได้โดยไม่ต้องมี asynctasks ทั้งหมด (หายากมาก) แต่ @Arhimed ด้วยการยืนยันที่ง่ายต่อการเริ่มต้นและจุดสิ้นสุดของแต่ละ asynctask คุณสามารถหลีกเลี่ยงข้อบกพร่องได้
neteinstein

5

วิธีที่เหมาะสมที่สุดคือการใช้แฟรกเมนต์เพื่อรักษาอินสแตนซ์ของงาน async ไว้เหนือการหมุน

นี่คือลิงค์ไปยังตัวอย่างง่ายๆที่ทำให้ง่ายต่อการติดตามรวมเทคนิคนี้เข้ากับแอพของคุณ

https://gist.github.com/daichan4649/2480065


นี่คือบทช่วยสอนอื่นที่ใช้ประโยชน์จากชิ้นส่วนที่เก็บไว้: blogactivity.wordpress.com/2011/09/01/proper-use-of-asynctask
devconsole


3

ในมุมมองของฉันเป็นการดีกว่าที่จะจัดเก็บ asynctask ผ่านการonRetainNonConfigurationInstanceแยกส่วนจากวัตถุกิจกรรมปัจจุบันและผูกไว้กับวัตถุกิจกรรมใหม่หลังจากเปลี่ยนการวางแนว ที่นี่ฉันพบตัวอย่างที่ดีมากในการทำงานกับ AsyncTask และ ProgressDialog


2

Android: การประมวลผลเบื้องหลัง / Async Opeartion พร้อมการเปลี่ยนแปลงการกำหนดค่า

ในการรักษาสถานะของ async opeartion ระหว่างกระบวนการเบื้องหลัง: คุณสามารถใช้ส่วนย่อยได้

ดูขั้นตอนต่อไปนี้:

ขั้นตอนที่ 1: สร้างส่วนที่ไม่มีส่วนหัวให้พูดงานพื้นหลังและเพิ่มคลาสงาน async ส่วนตัวด้วย

ขั้นตอนที่ 2 (ขั้นตอนเสริม): หากคุณต้องการวางเคอร์เซอร์โหลดไว้ด้านบนของกิจกรรมให้ใช้โค้ดด้านล่าง:

ขั้นตอนที่ 3: ในกิจกรรมหลักของคุณให้ใช้อินเทอร์เฟซ BackgroundTaskCallbacks ที่กำหนดไว้ในขั้นตอนที่ 1

class BackgroundTask extends Fragment {
public BackgroundTask() {

}

// Add a static interface 

static interface BackgroundTaskCallbacks {
    void onPreExecute();

    void onCancelled();

    void onPostExecute();

    void doInBackground();
}

private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();

/**
 * Start the async operation.
 */
public void start() {
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
    if (!isRunning) {
        asyncOperation = new PerformAsyncOpeation();
        asyncOperation.execute();
        isRunning = true;
    }
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}

/**
 * Cancel the background task.
 */
public void cancel() {
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
    if (isRunning) {
        asyncOperation.cancel(false);
        asyncOperation = null;
        isRunning = false;
    }
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}

/**
 * Returns the current state of the background task.
 */
public boolean isRunning() {
    return isRunning;
}

/**
 * Android passes us a reference to the newly created Activity by calling
 * this method after each configuration change.
 */
public void onAttach(Activity activity) {
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
    super.onAttach(activity);
    if (!(activity instanceof BackgroundTaskCallbacks)) {
        throw new IllegalStateException(
                "Activity must implement the LoginCallbacks interface.");
    }

    // Hold a reference to the parent Activity so we can report back the
    // task's
    // current progress and results.
    callbacks = (BackgroundTaskCallbacks) activity;
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}

public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
    super.onCreate(savedInstanceState);
    // Retain this fragment across configuration changes.
    setRetainInstance(true);
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}

public void onDetach() {
    super.onDetach();
    callbacks = null;
}

private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
    protected void onPreExecute() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPreExecute();
        }
        isRunning = true;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
    }

    protected Void doInBackground(Void... params) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
        if (callbacks != null) {
            callbacks.doInBackground();
        }
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
        return null;
    }

    protected void onCancelled() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
        if (callbacks != null) {
            callbacks.onCancelled();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
    }

    protected void onPostExecute(Void ignore) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPostExecute();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
    }
}

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

public void onStart() {
    super.onStart();
}

public void onResume() {
    super.onResume();
}

public void onPause() {
    super.onPause();
}

public void onStop() {
    super.onStop();
}

public class ProgressIndicator extends Dialog {

public ProgressIndicator(Context context, int theme) {
    super(context, theme);
}

private ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.progress_indicator);
    this.setCancelable(false);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}

@Override
public void show() {
    super.show();
}

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

@Override
public void cancel() {
    super.cancel();
}

public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{

private static final String KEY_CURRENT_PROGRESS = "current_progress";

ProgressIndicator progressIndicator = null;

private final static String TAG = MyActivity.class.getSimpleName();

private BackgroundTask task = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(//"set your layout here");
    initialize your views and widget here .............



    FragmentManager fm = getSupportFragmentManager();
    task = (BackgroundTask) fm.findFragmentByTag("login");

    // If the Fragment is non-null, then it is currently being
    // retained across a configuration change.
    if (task == null) {
        task = new BackgroundTask();
        fm.beginTransaction().add(task, "login").commit();
    }

    // Restore saved state
    if (savedInstanceState != null) {
        Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
                + task.isRunning());
        if (task.isRunning()) {
            progressIndicator = new ProgressIndicator(this,
                    R.style.TransparentDialog);
            if (progressIndicator != null) {
                progressIndicator.show();
            }
        }
    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // save the current state of your operation here by saying this 

    super.onSaveInstanceState(outState);
    Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
            + task.isRunning());
    outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}


private void performOperation() {

            if (!task.isRunning() && progressIndicator == null) {
                progressIndicator = new ProgressIndicator(this,
                        R.style.TransparentDialog);
                progressIndicator.show();
            }
            if (task.isRunning()) {
                task.cancel();
            } else {
                task.start();
            }
        }


@Override
protected void onDestroy() {
    super.onDestroy();
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}

@Override
public void onPreExecute() {
    Log.i(TAG, "CALLING ON PRE EXECUTE");
}

@Override
public void onCancelled() {
    Log.i(TAG, "CALLING ON CANCELLED");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();

}

public void onPostExecute() {
    Log.i(TAG, "CALLING ON POST EXECUTE");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
        progressIndicator = null;
    }
}

@Override
public void doInBackground() {
    // put your code here for background operation
}

}


1

สิ่งหนึ่งที่ต้องพิจารณาคือผลลัพธ์ของ AsyncTask ควรมีให้เฉพาะกับกิจกรรมที่เริ่มงานหรือไม่ ถ้าใช่คำตอบของ Romain Guyนั้นดีที่สุด ถ้ามันควรจะมีกิจกรรมอื่น ๆ ของแอพลิเคชันของคุณแล้วในคุณสามารถใช้onPostExecuteLocalBroadcastManager

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

นอกจากนี้คุณจะต้องตรวจสอบให้แน่ใจว่ากิจกรรมนั้นจัดการกับสถานการณ์ได้อย่างถูกต้องเมื่อมีการส่งการออกอากาศในขณะที่กิจกรรมถูกหยุดชั่วคราว


1

ลองดูที่โพสต์นี้ โพสต์นี้เกี่ยวข้องกับ AsyncTask ที่ดำเนินการทำงานเป็นเวลานานและหน่วยความจำรั่วเมื่อการหมุนหน้าจอเกิดขึ้นทั้งในแอปพลิเคชันตัวอย่างเดียว แอปตัวอย่างมีอยู่ในแหล่งปลอมแปลง


0

ทางออกของฉัน

ในกรณีของฉันฉันมีเครือข่าย AsyncTasks ที่มีบริบทเดียวกัน กิจกรรมมีสิทธิ์เข้าถึงเฉพาะรายการแรกเท่านั้น ในการยกเลิกงานที่กำลังทำงานอยู่ฉันได้ทำสิ่งต่อไปนี้:

public final class TaskLoader {

private static AsyncTask task;

     private TaskLoader() {
         throw new UnsupportedOperationException();
     }

     public static void setTask(AsyncTask task) {
         TaskLoader.task = task;
     }

    public static void cancel() {
         TaskLoader.task.cancel(true);
     }
}

งานdoInBackground():

protected Void doInBackground(Params... params) {
    TaskLoader.setTask(this);
    ....
}

กิจกรรมonStop()หรือonPause():

protected void onStop() {
    super.onStop();
    TaskLoader.cancel();
}

0
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final AddTask task = mAddTask;
    if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
        final String bookId = task.getBookId();
        task.cancel(true);

        if (bookId != null) {
            outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
            outState.putString(STATE_ADD_BOOK, bookId);
        }

        mAddTask = null;
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
        final String id = savedInstanceState.getString(STATE_ADD_BOOK);
        if (!BooksManager.bookExists(getContentResolver(), id)) {
            mAddTask = (AddTask) new AddTask().execute(id);
        }
    }
}

0

คุณยังสามารถเพิ่ม android: configChanges = "keyboardHidden | orientation | screenSize"

สำหรับตัวอย่างรายการของคุณฉันหวังว่ามันจะช่วยได้

 <application
    android:name=".AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:theme="@style/AppTheme">
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.