จะประกาศตัวแปรทั่วโลกใน Android ได้อย่างไร?


595

ฉันกำลังสร้างแอปพลิเคชันซึ่งต้องมีการเข้าสู่ระบบ ฉันสร้างกิจกรรมหลักและกิจกรรมเข้าสู่ระบบ

ในonCreateวิธีกิจกรรมหลักฉันเพิ่มเงื่อนไขต่อไปนี้:

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

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

onActivityResultวิธีการที่จะดำเนินการเมื่อฟอร์มการเข้าสู่ระบบยุติลักษณะเช่นนี้

@Override
public void onActivityResult(int requestCode,
                             int resultCode,
                             Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode)
    {
        case(SHOW_SUBACTICITY_LOGIN):
        {
            if(resultCode == Activity.RESULT_OK)
            {

                strSessionString = data.getStringExtra(Login.SESSIONSTRING);
                connectionAvailable = true;
                strUsername = data.getStringExtra(Login.USERNAME);
            }
        }
    }

ปัญหาที่เกิดขึ้นเป็นรูปแบบการเข้าสู่ระบบในบางครั้งจะปรากฏขึ้นเป็นครั้งที่สอง (คนlogin()วิธีการที่เรียกว่าครั้งที่สอง) strSessionStringและเมื่อสไลด์แป้นพิมพ์โทรศัพท์แบบฟอร์มการเข้าสู่ระบบจะปรากฏขึ้นอีกครั้งและผมคิดว่าปัญหาคือตัวแปร

ไม่มีใครรู้วิธีการตั้งค่าตัวแปรทั่วโลกเพื่อหลีกเลี่ยงรูปแบบการเข้าสู่ระบบปรากฏหลังจากที่ผู้ใช้ตรวจสอบแล้วประสบความสำเร็จ?


แบบฝึกหัดที่ดีเกี่ยวกับวิธีจัดการสถานะกิจกรรมโดยใช้บันเดิลสถานะตัวอย่างเช่นquicktips.in/ …
Deepak Swami

คำตอบ:


954

ฉันเขียนคำตอบนี้ในปี '09 เมื่อ Android ค่อนข้างใหม่และมีหลายพื้นที่ที่ไม่ได้รับการยอมรับในการพัฒนา Android ฉันได้เพิ่มภาคผนวกยาวที่ด้านล่างของโพสต์นี้ที่อยู่การวิจารณ์บางส่วนและรายละเอียดความขัดแย้งทางปรัชญาที่ฉันมีกับการใช้งานของ Singletons มากกว่าการประยุกต์ใช้ subclassing อ่านความเสี่ยงของคุณเอง

คำตอบเดิม:

ปัญหาทั่วไปที่คุณพบคือวิธีการบันทึกสถานะในหลาย ๆ กิจกรรมและทุกส่วนของแอปพลิเคชันของคุณ ตัวแปรแบบสแตติก (ตัวอย่างเช่น singleton) เป็นวิธีการใช้จาวาทั่วไปในการบรรลุเป้าหมายนี้ อย่างไรก็ตามฉันพบว่าวิธีที่หรูหรากว่าใน Android คือการเชื่อมโยงสถานะของคุณกับบริบทแอปพลิเคชัน

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

วิธีการทำเช่นนี้คือการสร้างคลาสย่อยของคุณเองandroid.app.Applicationแล้วระบุคลาสนั้นในแท็กแอปพลิเคชันในรายการของคุณ ตอนนี้ Android จะสร้างตัวอย่างของคลาสนั้นโดยอัตโนมัติและทำให้แอปพลิเคชันของคุณพร้อมใช้งาน คุณสามารถเข้าถึงได้จากการcontextใช้Context.getApplicationContext()วิธีการใด ๆ( Activityนอกจากนี้ยังมีวิธีการgetApplication()ที่มีผลเหมือนกันแน่นอน) ต่อไปนี้เป็นตัวอย่างที่ง่ายมากโดยมี caveats ที่จะปฏิบัติตาม:

class MyApp extends Application {

  private String myState;

  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

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

สิ่งที่ควรทราบจากตัวอย่างด้านบน สมมติว่าเราทำสิ่งที่ต้องการแทน:

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

ตอนนี้การเริ่มต้นช้า (เช่นการกดปุ่มดิสก์เครือข่ายการกดปุ่มการบล็อกอะไรเป็นต้น) จะดำเนินการทุกครั้งที่แอปพลิเคชันถูกสร้างขึ้น! คุณอาจคิดว่านี่เป็นเพียงขั้นตอนเดียวสำหรับกระบวนการและฉันจะต้องจ่ายค่าใช้จ่ายใช่มั้ย ตัวอย่างเช่นตามที่ Dianne Hackborn ระบุไว้ด้านล่างเป็นไปได้ทั้งหมดที่กระบวนการของคุณจะถูกสร้างอินสแตนซ์ - เพียงแค่ - เพื่อจัดการกับเหตุการณ์ออกอากาศเบื้องหลัง หากการประมวลผลการออกอากาศของคุณไม่จำเป็นต้องมีสถานะนี้คุณอาจต้องทำการดำเนินการทั้งชุดที่ซับซ้อนและช้าเพื่ออะไร Lazy instantiation เป็นชื่อของเกมที่นี่ ต่อไปนี้เป็นวิธีที่ซับซ้อนกว่าเล็กน้อยในการใช้แอปพลิเคชันซึ่งเหมาะสมกว่าสำหรับทุกสิ่งยกเว้นการใช้งานที่ง่ายที่สุด:

class MyApp extends Application {

  private MyStateManager myStateManager = new MyStateManager();

  public MyStateManager getStateManager(){
    return myStateManager ;
  }
}

class MyStateManager {

  MyStateManager() {
    /* this should be fast */
  }

  String getState() {
    /* if necessary, perform blocking calls here */
    /* make sure to deal with any multithreading/synchronicity issues */

    ...

    return state;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
    String state = stateManager.getState();
    ...
  }
}

ในขณะที่ฉันต้องการ Application subclassing เพื่อใช้ singletons ที่นี่เป็นทางออกที่หรูหรากว่าฉันอยากให้นักพัฒนาใช้ singletons ถ้าจำเป็นจริงๆโดยไม่ต้องคิดถึงเรื่องทั้งหมดผ่านการทำงาน

หมายเหตุ 1:เช่นเดียวกับที่ anticafe แสดงความคิดเห็นเพื่อผูกการแทนที่แอปพลิเคชันของคุณกับแอปพลิเคชันของคุณอย่างถูกต้องจำเป็นต้องมีแท็กในไฟล์รายการ ดูเอกสาร Android อีกครั้งเพื่อดูข้อมูลเพิ่มเติม ตัวอย่าง:

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

หมายเหตุ 2: user608578 ถามด้านล่างว่ามันทำงานอย่างไรกับการจัดการวงจรชีวิตวัตถุพื้นเมือง ฉันไม่ได้ใช้ความเร็วในการใช้รหัสเนทีฟกับ Android ในระดับที่น้อยที่สุดและฉันก็ไม่ได้มีคุณสมบัติที่จะตอบว่าจะโต้ตอบกับโซลูชันของฉันอย่างไร หากใครบางคนมีคำตอบสำหรับเรื่องนี้ฉันยินดีที่จะให้เครดิตพวกเขาและใส่ข้อมูลในโพสต์นี้เพื่อการมองเห็นสูงสุด

ภาคผนวก:

ดังที่คนบางคนตั้งข้อสังเกตว่านี่ไม่ใช่วิธีแก้ปัญหาสำหรับสภาวะถาวรบางสิ่งที่ฉันควรจะเน้นในคำตอบเดิมมากขึ้น เช่นนี้ไม่ได้หมายถึงการแก้ปัญหาสำหรับการบันทึกผู้ใช้หรือข้อมูลอื่น ๆ ที่มีความตั้งใจที่จะยืนยันตลอดอายุการใช้งานของแอปพลิเคชัน ดังนั้นฉันคิดว่าการวิพากษ์วิจารณ์ส่วนใหญ่ด้านล่างที่เกี่ยวข้องกับแอปพลิเคชันที่ถูกฆ่าตายในเวลาใดก็ได้ ฯลฯ ... , moot เป็นสิ่งที่เคยจำเป็นต้องยืนยันกับดิสก์ไม่ควรเก็บไว้ผ่านคลาสย่อยของแอปพลิเคชัน มันจะหมายถึงการจะแก้ปัญหาสำหรับการจัดเก็บชั่วคราวได้อย่างง่ายดายอีกครั้ง creatable สถานะโปรแกรม (ไม่ว่าจะเป็นผู้ใช้ที่ถูกบันทึกไว้ในตัวอย่าง) และส่วนประกอบซึ่งเป็นเช่นเดียว (ผู้จัดการเครือข่ายพลิเคชันสำหรับตัวอย่าง) ( ไม่เดี่ยว!) ในธรรมชาติ

Dayerman ใจดีพอที่จะชี้ให้เห็นการสนทนาที่น่าสนใจกับ Reto Meier และ Dianne Hackbornซึ่งการใช้คลาสย่อยของ Application นั้นไม่สนับสนุนรูปแบบซิงเกิล Somatik ชี้ให้เห็นบางสิ่งบางอย่างในลักษณะนี้ก่อนหน้านี้แม้ว่าฉันจะไม่เห็นมันในเวลานั้น เนื่องจากบทบาทของ Reto และ Dianne ในการบำรุงรักษาแพลตฟอร์ม Android ฉันจึงไม่สามารถแนะนำโดยไม่สนใจคำแนะนำของพวกเขาได้ สิ่งที่พวกเขาพูดไป ฉันต้องการที่จะไม่เห็นด้วยกับความคิดเห็นที่แสดงออกถึงการเลือกซิงเกิลตันมากกว่าคลาสย่อยของแอปพลิเคชัน ในความขัดแย้งของฉันฉันจะใช้ประโยชน์จากแนวคิดที่อธิบายได้ดีที่สุดในคำอธิบาย StackExchange ของรูปแบบการออกแบบซิงเกิลนี้ดังนั้นฉันไม่จำเป็นต้องนิยามคำในคำตอบนี้ ฉันขอแนะนำให้ข้ามลิงก์ก่อนดำเนินการต่อ ทีละจุด:

Dianne กล่าวว่า "ไม่มีเหตุผลใดที่จะซับคลาสจากแอปพลิเคชันไม่แตกต่างจากการสร้างซิงเกิลตัน ... " การอ้างสิทธิ์ครั้งแรกนี้ไม่ถูกต้อง มีสองเหตุผลหลักสำหรับเรื่องนี้ 1) คลาสแอปพลิเคชันให้การรับประกันอายุการใช้งานที่ดีขึ้นสำหรับนักพัฒนาแอปพลิเคชัน รับประกันได้ว่าจะมีอายุการใช้งานของแอปพลิเคชัน ซิงเกิลตันไม่ได้เชื่อมโยงอย่างชัดเจนกับอายุการใช้งานของแอปพลิเคชัน (แม้ว่าจะมีประสิทธิภาพ) นี่อาจไม่ใช่ปัญหาสำหรับนักพัฒนาแอปพลิเคชั่นโดยเฉลี่ยของคุณ แต่ฉันจะเถียงว่านี่เป็นประเภทสัญญาที่ Android API ควรเสนอและมีความยืดหยุ่นมากขึ้นกับระบบ Android เช่นกันโดยลดอายุการใช้งานที่เกี่ยวข้อง ข้อมูล. 2) แอปพลิเคชั่นคลาสให้ผู้พัฒนาแอปพลิเคชันพร้อมตัวยึดอินสแตนซ์เดี่ยวสำหรับสถานะ ซึ่งแตกต่างจากผู้มีอำนาจรัฐเดี่ยวมาก สำหรับรายการความแตกต่างให้ดูลิงก์คำอธิบายซิงเกิลด้านบน

Dianne กล่าวต่อว่า "... น่าจะเป็นสิ่งที่คุณต้องเสียใจในอนาคตเมื่อคุณพบว่าแอปพลิเคชันวัตถุของคุณกลายเป็นเรื่องยุ่งเหยิงที่ยิ่งใหญ่ของสิ่งที่ควรเป็นตรรกะแอปพลิเคชันอิสระ" สิ่งนี้ไม่ถูกต้อง แต่นี่ไม่ใช่เหตุผลสำหรับการเลือกซิงเกิลตันของคลาสย่อยของแอปพลิเคชัน ไม่มีข้อโต้แย้งของไดแอนที่ให้เหตุผลว่าการใช้ซิงเกิลดีกว่าคลาสย่อยของแอปพลิเคชันทั้งหมดที่เธอพยายามสร้างคือการใช้ซิงเกิลตันนั้นไม่เลวร้ายไปกว่าซับคลาสแอปพลิเคชันซึ่งฉันเชื่อว่าเป็นเท็จ

เธอกล่าวต่อ "และสิ่งนี้นำไปสู่การจัดการสิ่งเหล่านี้อย่างเป็นธรรมชาติมากขึ้น สิ่งนี้จะละเว้นความจริงที่ว่าไม่มีเหตุผลที่คุณไม่สามารถเริ่มต้นตามคำขอได้โดยใช้คลาสย่อยของ Application เช่นกัน อีกครั้งไม่มีความแตกต่าง

Dianne ลงท้ายด้วย "กรอบงานนั้นมีตันแบบซิงเกิลตันสำหรับข้อมูลที่แชร์เล็ก ๆ น้อย ๆ ทั้งหมดที่แอปเก็บไว้เช่นแคชทรัพยากรที่มีจำนวนมากกลุ่มของวัตถุ ฯลฯ มันใช้งานได้ดีมาก" ฉันไม่ได้โต้แย้งว่าการใช้ซิงเกิลตันไม่สามารถทำงานได้ดีหรือไม่ใช่ทางเลือกที่ถูกกฎหมาย ฉันโต้เถียงว่า Singletons ไม่ได้ให้สัญญาที่แข็งแกร่งกับระบบ Android เช่นเดียวกับการใช้คลาสย่อยของ Application และยิ่งไปกว่านั้นการใช้ Singletons โดยทั่วไปชี้ไปที่การออกแบบที่ยืดหยุ่นซึ่งไม่ได้แก้ไขได้ง่ายและนำไปสู่ปัญหามากมาย IMHO สัญญาที่แข็งแกร่งที่ Android API เสนอให้กับแอพพลิเคชั่นสำหรับนักพัฒนาเป็นหนึ่งในแง่มุมที่น่าดึงดูดใจและน่าพอใจที่สุดของการเขียนโปรแกรมกับ Android และช่วยนำไปสู่การยอมรับนักพัฒนาในยุคแรกซึ่งผลักดันแพลตฟอร์ม Android ให้ประสบความสำเร็จ

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

ฉันปล่อยให้คุณมีรายการข้อเสียต่อไปนี้เป็น Singletons ซึ่งถูกขโมยจากลิงก์ StackExchange ก่อนหน้านี้:

  • ไม่สามารถใช้คลาส abstract หรืออินเตอร์เฟส
  • ไม่สามารถคลาสย่อย;
  • การมีเพศสัมพันธ์สูงทั่วทั้งแอปพลิเคชัน (ยากที่จะแก้ไข);
  • ทดสอบยาก (ไม่สามารถปลอม / จำลองในการทดสอบหน่วย)
  • ยากต่อการขนานในกรณีที่สถานะไม่แน่นอน (ต้องใช้การล็อคอย่างกว้างขวาง);

และเพิ่มของฉันเอง:

  • สัญญาที่ไม่ชัดเจนและไม่สามารถจัดการได้ตลอดอายุการใช้งานสำหรับการพัฒนา Android (หรืออื่น ๆ ส่วนใหญ่);

93
ขอบคุณ Soonil - คำตอบเหล่านี้เป็นเหตุผลที่ฉันชอบ Stack Overflow มาก งานที่ดี!
JohnnyLambada

5
สำหรับทุกคนที่สงสัยว่าจะ "ระบุคลาสนั้นในแท็กแอปพลิเคชันในไฟล์ Manifest ของคุณได้อย่างไรมีคำตอบอีกสองคำตอบสำหรับคำถามนี้ที่อธิบายถึงวิธีการใช้ (ใช้ android: ชื่อ) โดย ebuprofen และหนึ่ง โดย Mike Brown
Tyler Collier

9
อีกไม่นานคำตอบของคุณถูกต้อง แต่คุณจะสังเกตได้ไหมว่าเราควรเพิ่ม <application android: name = ". MyApp" ... /> ลงในไฟล์ Android Manifest?
anticafe

12
ให้ฉันทำซ้ำอีกครั้งคุณไม่ควรใช้แอพพลิเคชั่นสำหรับ globals มันไม่มีประโยชน์ไม่มีผลประโยชน์ใด ๆ กับซิงเกิลตันและอาจเป็นอันตรายอย่างแข็งขันเช่นการทำร้ายประสิทธิภาพของการเปิดตัวกระบวนการของคุณ ในขณะที่กำลังสร้างแอปพลิเคชันคุณไม่ทราบว่ากระบวนการของคุณกำลังถูกสร้างขึ้นเพื่ออะไร ด้วยการเริ่มต้นซิงเกิลอย่างเกียจคร้านตามต้องการคุณจะต้องทำงานที่จำเป็นเท่านั้น ตัวอย่างเช่นหากกระบวนการของคุณกำลังเปิดตัวเพื่อจัดการการออกอากาศเกี่ยวกับเหตุการณ์พื้นหลังบางอย่างไม่มีเหตุผลที่จะเริ่มต้นสิ่งที่รัฐทั่วโลกต้องการโดย UI ของคุณ
hackbod

14
นอกจากนี้ขอให้ชัดเจนที่นี่ - ทุกข้อโต้แย้งของคุณกับซิงเกิลนั้นใช้ได้อย่างสมบูรณ์แบบเมื่อเราพูดถึงสถานการณ์ที่คุณเลือกระหว่างซิงเกิลตันกับอีกแนวทางที่ไม่ใช่โลก Singletons เป็นรูปกลมมีข้อแม้ทั้งหมดเกี่ยวกับรูปดาวที่ใช้ อย่างไรก็ตามแอพลิเคชันยังเป็นเดี่ยว คุณไม่ได้หลบหนีปัญหาเหล่านั้นด้วยการเปลี่ยนไปใช้ Subclassing Application แอปพลิเคชันนั้นเหมือนกับซิงเกิลตัน (แต่แย่กว่านั้น) มันแค่ปล่อยให้คุณหลอกตัวเองว่าคุณกำลังทำอะไรที่สะอาดกว่า แต่คุณไม่ได้
hackbod

153

สร้างคลาสย่อยนี้

public class MyApp extends Application {
  String foo;
}

ใน AndroidManifest.xml เพิ่ม android: name

ตัวอย่าง

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

1
ขอบคุณสำหรับสิ่งนั้น ฉันสงสัยว่าจะประกาศอย่างไรในที่ประจักษ์
บางคนอยู่ที่ไหนสักแห่ง

3
เพื่อให้ใช้งานได้สำหรับฉันฉันต้องลบ "" ออก ภายใน ".MyApp"
บางคนอยู่ที่ไหนสักแห่ง

3
เพียงแค่ประกาศว่าหลังจากที่กิจกรรมหลักมิฉะนั้นอาจไม่ติดตั้ง / การปรับใช้
sami

11
แค่อยากบอกว่านี่ไปในแท็กแอปพลิเคชันหลักที่มีอยู่แล้ว ... นี่ไม่ใช่อันที่สอง :) ต้องเรียนรู้วิธีที่ยากลำบาก
bwoogie

java.lang.IllegalAccessException: access to class is not allowed
Raptor

142

วิธีที่แนะนำโดย Soonil ในการรักษาสถานะสำหรับแอปพลิเคชันนั้นเป็นสิ่งที่ดี แต่ก็มีจุดอ่อนอยู่จุดหนึ่ง - มีหลายกรณีเมื่อระบบปฏิบัติการฆ่ากระบวนการแอปพลิเคชันทั้งหมด นี่คือเอกสารเกี่ยวกับเรื่องนี้ - กระบวนการและวงจรชีวิต

พิจารณากรณี - แอปของคุณเข้าสู่พื้นหลังเพราะมีใครบางคนกำลังโทรหาคุณ (แอพโทรศัพท์กำลังอยู่ในเบื้องหน้าในขณะนี้) ในกรณีนี้ && ภายใต้เงื่อนไขอื่น ๆ (ตรวจสอบลิงก์ด้านบนเพื่อดูว่ามันเป็นไปได้) ระบบปฏิบัติการอาจฆ่ากระบวนการแอปพลิเคชันของคุณรวมถึงApplicationอินสแตนซ์คลาสย่อย เป็นผลให้รัฐสูญเสีย เมื่อคุณหลังจากนั้นกลับไปที่แอพลิเคชันแล้วระบบปฏิบัติการจะเรียกคืนสแต็คของกิจกรรมและApplicationอินสแตนซ์ subclass แต่ข้อมูลจะเป็นmyStatenull

AFAIK วิธีเดียวที่จะรับประกันความปลอดภัยของรัฐคือการใช้สถานะที่ยังคงอยู่เช่นใช้ไพรเวตสำหรับไฟล์แอปพลิเคชันหรือSharedPrefernces(ในที่สุดจะใช้ไพรเวตสำหรับไฟล์แอปพลิเคชันในระบบไฟล์ภายใน)


10
+1 สำหรับการยืนยันด้วยSharedPreferences; นี่คือสิ่งที่ฉันเห็น ฉันคิดว่ามันแปลกที่จะใช้ระบบการตั้งค่าแบบไม่เหมาะสมสำหรับสถานะที่บันทึกไว้ แต่ก็ใช้งานได้ดีว่าปัญหานี้กลายเป็นเพียงคำถามเกี่ยวกับคำศัพท์
Cheezmeister

1
คุณช่วยกรุณาโพสต์รหัส (หรือให้ลิงค์เชื่อมโยงไปยังคำอธิบาย) เกี่ยวกับวิธีการใช้ SharedPreferences เพื่อแก้ปัญหาที่ Arhimed อธิบาย
ใครสักคนที่

2
การตั้งค่าฐานข้อมูลการทำให้เป็นอนุกรมไฟล์ ฯลฯ แต่ละกิจกรรมสามารถรักษาสถานะหากพวกเขาใช้ onSaveInstanceState แต่มันจะไม่ช่วยถ้าผู้ใช้ backs ออกจากกิจกรรมและที่ลบออกจากสแต็คประวัติศาสตร์บังคับปิดหรือปิดอุปกรณ์ของพวกเขา .
Darren Hinderer

1
พฤติกรรมนี้น่ารำคาญมาก - มันจะไม่เลวร้ายนักหากเรียกใช้เมธอด onTerminate () ของแอปพลิเคชันของคุณเพื่อให้คุณสามารถจัดการกับสถานการณ์ได้อย่างสง่างาม
Dean Wild

2
นี่คือคำตอบที่ถูกต้องในความคิดของฉัน เป็นข้อผิดพลาดที่ต้องพึ่งพาอินสแตนซ์ของแอปพลิเคชันเดียวกันที่มีอยู่ในกิจกรรม จากประสบการณ์ของฉันมันค่อนข้างธรรมดาสำหรับ Android ที่จะฉีกและสร้างกระบวนการทั้งหมดของคุณใหม่ในขณะที่คุณมีพื้นหลัง การถูกแบ็กกราวด์อาจหมายถึงการเปิดกล้องการแสดงเจตนาของเบราว์เซอร์หรือการรับโทรศัพท์
Jared Kells

26

เพียงแค่ทราบ ..

เพิ่ม:

android:name=".Globals"

หรือสิ่งที่คุณตั้งชื่อคลาสย่อยของคุณไปยังแท็กที่มีอยู่ <application>ฉันพยายามเพิ่ม<application>แท็กอื่นในรายการและจะได้รับข้อยกเว้น


สวัสดี Gimbl ผมมีปัญหาเดียวกัน. ฉันมีแท็ก <application> ของตัวเองและเมื่อฉันพยายามเพิ่มแท็ก <application> อีกอันฉันมีปัญหาเช่นเดียวกับคุณ (ข้อความแสดงข้อยกเว้น) แต่ฉันทำสิ่งที่คุณพูดถึง แต่มันก็ไม่ได้ผล ฉันเพิ่ม android: name = ". GlobalClass" ไปยังแท็ก <application> ของฉัน แต่มันใช้งานไม่ได้ คุณช่วยอธิบายได้อย่างเต็มที่ว่าจะแก้ไขได้อย่างไร?
Sonhja

3
ดี <manifest> <application android: name = ". GlobalData"> </application> </manifest> Bad <ประจักษ์> <application> </ application> <ประยุกต์ใช้ Android: name = "GlobalData."> </ application> </ ประจักษ์>
Gimbl

13

ฉันไม่สามารถหาวิธีระบุแท็กแอปพลิเคชันได้ แต่หลังจาก Googling เป็นจำนวนมากมันก็เห็นได้ชัดจากรายการไฟล์เอกสาร: ใช้ android: ชื่อนอกเหนือจากไอคอนเริ่มต้นและป้ายกำกับใน stanza ของแอปพลิเคชัน

android: name ชื่อที่ผ่านการรับรองโดยสมบูรณ์ของคลาสย่อยของแอ็พพลิเคชันที่ถูกใช้สำหรับแอ็พพลิเคชัน เมื่อเริ่มต้นกระบวนการแอปพลิเคชันคลาสนี้จะถูกสร้างอินสแตนซ์ก่อนส่วนประกอบใด ๆ ของแอปพลิเคชัน

คลาสย่อยเป็นทางเลือก แอปพลิเคชั่นส่วนใหญ่ไม่ต้องการ ในกรณีที่ไม่มีคลาสย่อย Android จะใช้อินสแตนซ์ของคลาสแอปพลิเคชันพื้นฐาน


13

สิ่งที่เกี่ยวกับการสร้างความมั่นใจในการรวบรวมหน่วยความจำดั้งเดิมด้วยโครงสร้างระดับโลกเช่นนั้น?

กิจกรรมมีonPause/onDestroy()วิธีการที่เรียกว่าการทำลาย แต่คลาสแอปพลิเคชันไม่มีสิ่งเทียบเท่า กลไกใดที่แนะนำเพื่อให้แน่ใจว่าโครงสร้างโกลบอล (โดยเฉพาะอย่างยิ่งสิ่งที่มีการอ้างอิงถึงหน่วยความจำดั้งเดิม) มีการรวบรวมขยะอย่างเหมาะสมเมื่อแอปพลิเคชันถูกฆ่า


1
วิธีการแก้ปัญหาที่ชัดเจนคือการใช้closeableอินเตอร์เฟซสำหรับวัตถุของคุณรับผิดชอบสำหรับทรัพยากรพื้นเมืองและแน่ใจว่าพวกเขามีการจัดการโดยคำสั่งทรัพยากรลองด้วยหรือสิ่งอื่นใด กรณีที่แย่ที่สุดคุณสามารถใช้ object finalizer ได้เสมอ
เร็ว ๆ นี้

5

เพียงคุณต้องกำหนดชื่อแอปพลิเคชันเช่นด้านล่างซึ่งจะใช้งานได้:

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

4

เช่นเดียวกับที่กล่าวถึงข้างต้นระบบปฏิบัติการสามารถฆ่าแอปพลิเคชันโดยไม่มีการแจ้งเตือนใด ๆ (ไม่มีเหตุการณ์ onDestroy) ดังนั้นจึงไม่มีวิธีบันทึกตัวแปรส่วนกลางเหล่านี้

SharedPreferences อาจเป็นทางออกยกเว้นคุณมีตัวแปร COMPLEX STRUCTURED (ในกรณีของฉันฉันมีอาร์เรย์จำนวนเต็มเพื่อเก็บ ID ที่ผู้ใช้จัดการแล้ว) ปัญหาของ SharedPreferences นั้นยากที่จะจัดเก็บและดึงโครงสร้างเหล่านี้ทุกครั้งที่ต้องการค่า

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


onDestroy () ไม่รับประกันว่าจะสามารถเรียกได้แม้กระทั่งสำหรับบริการ
เรียนรู้ OpenGL ES

ใช่สิ่งนี้อาจเกิดขึ้นได้ แต่ในกรณีที่มีสถานการณ์ร้ายแรงเท่านั้น
Adorjan Princz

4

หากตัวแปรบางตัวถูกเก็บไว้ใน sqlite และคุณต้องใช้มันในกิจกรรมส่วนใหญ่ในแอปของคุณ แอปพลิเคชันอาจเป็นวิธีที่ดีที่สุดในการบรรลุเป้าหมาย ค้นหาตัวแปรจากฐานข้อมูลเมื่อแอปพลิเคชันเริ่มต้นและเก็บไว้ในเขตข้อมูล จากนั้นคุณสามารถใช้ตัวแปรเหล่านี้ในกิจกรรมของคุณ

หาทางที่ถูกต้องและไม่มีทางที่ดีที่สุด


3

คุณสามารถมีฟิลด์สแตติกเพื่อเก็บสถานะประเภทนี้ หรือนำไปไว้ที่บันเดิลของทรัพยากรและกู้คืนจากที่นั่นบน onCreate (บันเดิลที่บันทึกไว้InstanceState) เพียงให้แน่ใจว่าคุณเข้าใจวงจรชีวิตทั้งหมดของแอพ Android ที่มีการจัดการ (เช่นทำไมการเข้าสู่ระบบ () ได้รับเรียกเมื่อมีการเปลี่ยนแปลงการวางแนวคีย์บอร์ด)


2

ไม่ต้อง ใช้<application>แท็กอื่นในไฟล์ Manifest เพียงแค่ทำการเปลี่ยนแปลงใน<application>แท็กที่มีอยู่ให้เพิ่มบรรทัดนี้android:name=".ApplicationName"โดยที่ApplicationNameจะเป็นชื่อของคลาสย่อยของคุณ (ใช้เพื่อจัดเก็บทั่วโลก) ที่คุณกำลังจะสร้าง

ดังนั้นในที่สุดแท็กONE และ ONLY ของคุณ<application>ในไฟล์ Manifest ควรมีลักษณะดังนี้: -

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

1

คุณสามารถใช้ค่ากำหนด Intents, Sqlite หรือ Shared เมื่อพูดถึงที่เก็บข้อมูลสื่อเช่นเอกสารภาพถ่ายและวิดีโอคุณสามารถสร้างไฟล์ใหม่แทน


1

คุณสามารถทำได้โดยใช้สองวิธี:

  1. ใช้คลาสแอปพลิเคชัน
  2. ใช้การตั้งค่าที่ใช้ร่วมกัน

  3. ใช้คลาสแอปพลิเคชัน

ตัวอย่าง:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

คุณสามารถใช้คลาสด้านบนเพื่อดำเนินการเข้าสู่ระบบใน MainActivity ของคุณดังต่อไปนี้ โค้ดจะมีลักษณะดังนี้:

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

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

  1. ใช้การตั้งค่าที่ใช้ร่วมกัน

    String MYPREF="com.your.application.session"
    
    SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
    
    //Insert key as below:
    
    Editot editor= pref.edit();
    
    editor.putString("key","value");
    
    editor.commit();
    
    //Get key as below.
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
    String key= getResources().getString("key");

0

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


0

วิธีการ subclassing ยังถูกใช้โดยกรอบ BARACUS จากมุมมองของฉันsubclassingแอพพลิเคชั่นตั้งใจทำงานกับวงจรชีวิตของ Android นี่คือสิ่งใด ๆการประยุกต์ใช้ตู้คอนเทนเนอร์ไม่ แทนที่จะมีกลมแล้วฉันลงทะเบียนถั่วกับบริบทนี้เพื่อให้พวกเขาได้รับการฉีดเข้าไปในชั้นเรียนใด ๆ ที่บริบทจัดการได้ อินสแตนซ์ของถั่วที่ฉีดแล้วทุกอันเป็นซิงเกิลตัน

ดูตัวอย่างนี้สำหรับรายละเอียด

ทำไมทำงานด้วยตนเองถ้าคุณมีมากขึ้น?


0
class GlobaleVariableDemo extends Application {

    private String myGlobalState;

    public String getGlobalState(){
     return myGlobalState;
    }
    public void setGlobalState(String s){
     myGlobalState = s;
    }
}

class Demo extends Activity {

@Override
public void onCreate(Bundle b){
    ...
    GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
    String state = appState.getGlobalState();
    ...
    }
}

0

คุณสามารถสร้างคลาสที่ขยายApplicationคลาสแล้วประกาศตัวแปรของคุณเป็นฟิลด์ของคลาสนั้นและให้วิธี getter

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

จากนั้นเมื่อต้องการเข้าถึงตัวแปรนั้นในกิจกรรมของคุณให้ใช้สิ่งนี้:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.