บริบท Singletons vs. Application บริบทใน Android?


362

การเรียกคืนโพสต์นี้ระบุปัญหาต่าง ๆ ของการใช้ซิงเกิลตัน และได้เห็นตัวอย่างของแอปพลิเคชัน Android ที่ใช้รูปแบบซิงเกิลฉันสงสัยว่าควรใช้ Singletons แทนอินสแตนซ์เดี่ยวที่แชร์ผ่านสถานะแอปพลิเคชันทั่วโลก ผ่าน context.getApplication ())

กลไก / ข้อดีทั้งข้อเสีย / ข้อเสียอะไร?

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

แก้ไข: ฉันต้องการมีความคิดเห็นในหลาย ๆ ด้านที่เกี่ยวข้อง:

  • การประสานข้อมูล
  • สามารถนำมาใช้
  • การทดสอบ

คำตอบ:


295

ฉันไม่เห็นด้วยกับคำตอบของ Dianne Hackborn เราค่อย ๆ ลบซิงเกิลตันทั้งหมดออกจากโครงการของเราเนื่องจากมีน้ำหนักเบาวัตถุที่ถูกกำหนดขอบเขตงานซึ่งสามารถสร้างใหม่ได้ง่ายเมื่อคุณต้องการ

Singletons เป็นฝันร้ายสำหรับการทดสอบและถ้าเริ่มต้นได้อย่างเฉื่อยชาจะแนะนำ"indeterminism รัฐ"กับผลข้างเคียงที่ลึกซึ้ง (ซึ่งจู่ ๆ ก็อาจพื้นผิวเมื่อมีการย้ายการโทรไปgetInstance()จากขอบเขตที่หนึ่งไปยังอีก) ทัศนวิสัยได้รับการกล่าวถึงว่าเป็นปัญหาอื่นและเนื่องจากซิงเกิลตันบ่งบอกถึงการเข้าถึง"global" (= random)เพื่อเข้าถึงสถานะที่ใช้ร่วมกันข้อบกพร่องที่ลึกซึ้งอาจเกิดขึ้นเมื่อไม่ซิงโครไนซ์อย่างเหมาะสมในแอพพลิเคชันที่เกิดขึ้นพร้อมกัน

ฉันคิดว่ามันเป็นรูปแบบการต่อต้านมันเป็นสไตล์เชิงวัตถุที่ไม่ดี

หากต้องการกลับมาที่คำถามของคุณ:

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


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

18
จริงและฉันไม่ได้พูดถึงว่า "บริบทของแอพสามารถพิจารณาได้ว่าเป็นซิงเกิลเอง" ความแตกต่างก็คือเมื่อใช้แอพตัวอย่างการยิงตัวเองที่เท้านั้นหนักกว่ามากเนื่องจากวงจรชีวิตของมันถูกจัดการโดยกรอบ กรอบ DI เช่น Guice, Hivemind หรือ Spring ใช้ประโยชน์จาก singletons ด้วย แต่นั่นเป็นรายละเอียดการใช้งานที่นักพัฒนาไม่ควรสนใจ ฉันคิดว่าโดยทั่วไปจะปลอดภัยกว่าที่จะพึ่งพาซีแมนทิกส์ของเฟรมเวิร์กที่นำไปใช้อย่างถูกต้องมากกว่ารหัสของคุณเอง ใช่ฉันยอมรับว่าฉันทำ! :-)
Matthias

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

30
โอ้สิ่งหนึ่งที่อาจทำให้เกิดความสับสน - Android ได้รับการออกแบบมาเป็นอย่างดีเกี่ยวกับการใช้งานแอพในกระบวนการและการจัดการวงจรชีวิตของกระบวนการเหล่านั้น ดังนั้นใน Android singletons เป็นวิธีที่เป็นธรรมชาติมากที่จะใช้ประโยชน์จากการจัดการกระบวนการ - ถ้าคุณต้องการแคชบางอย่างในกระบวนการของคุณจนกว่าแพลตฟอร์มต้องการเรียกคืนหน่วยความจำของกระบวนการสำหรับสิ่งอื่นการวางสถานะนั้นไว้ในซิงเกิลจะทำเช่นนั้น
hackbod

7
โอเคพอยุติธรรม ฉันสามารถพูดได้ว่าฉันไม่ได้มองย้อนกลับไปตั้งแต่เราก้าวออกจากการจัดการตัวเองเพียงอย่างเดียว ตอนนี้เรากำลังเลือกวิธีการแก้ปัญหา DI-style ที่มีน้ำหนักเบาซึ่งเราเก็บรักษาโรงงานหนึ่งตัน (RootFactory) ซึ่งจะจัดการโดยอินสแตนซ์ของแอป (เป็นตัวแทนถ้าคุณต้องการ) ซิงเกิลนี้จัดการการพึ่งพาทั่วไปส่วนประกอบทั้งหมดของแอพพึ่งพา แต่การสร้างอินสแตนซ์ได้รับการจัดการในที่เดียว - คลาสแอพ ในขณะที่ยังมีวิธีการเดียวเหลืออยู่เพียงหนึ่งเดียวมันจะถูก จำกัด อยู่ที่คลาสแอปพลิเคชันดังนั้นจึงไม่มีโมดูลรหัสอื่นที่รู้เกี่ยวกับ "รายละเอียด" นั้น
แมทเทียส

231

ฉันแนะนำซิงเกิลตันเป็นอย่างมาก หากคุณมีซิงเกิลตันที่ต้องการบริบทให้มี:

MySingleton.getInstance(Context c) {
    //
    // ... needing to create ...
    sInstance = new MySingleton(c.getApplicationContext());
}

ฉันชอบซิงเกิลตันมากกว่า Application เพราะมันช่วยให้แอพมีการจัดระเบียบและแยกส่วนได้มากขึ้นแทนที่จะมีที่เดียวที่ทุกรัฐทั่วโลกของคุณในแอพจะต้องได้รับการดูแลรักษา ความจริงที่ว่า singletons เริ่มต้นอย่างเกียจคร้าน (ตามคำร้องขอ) แทนที่จะนำคุณลงสู่เส้นทางของการเริ่มต้นทั้งหมดล่วงหน้าใน Application.onCreate () เป็นสิ่งที่ดี

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

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

แค่คิดเกี่ยวกับสิ่งที่คุณกำลังทำอยู่ :)


1
หากภายหลังฉันต้องการฟังเหตุการณ์ภายนอกหรือแชร์ใน IBinder (ฉันเดาว่าจะไม่เป็นแอพง่าย ๆ ) ฉันต้องเพิ่มการล็อคซ้ำการซิงโครไนซ์การระเหยได้ใช่ไหม? ขอบคุณสำหรับคำตอบของคุณ :)
mschonaker

2
ไม่ใช่สำหรับเหตุการณ์ภายนอก - BroadcastReceiver.onReceive () ถูกเรียกบนเธรดหลัก
hackbod

2
ตกลง. คุณช่วยชี้ให้ฉันดูเนื้อหาการอ่าน (ฉันต้องการรหัส) ซึ่งฉันสามารถดูกลไกการจัดส่งเธรดหลักได้หรือไม่ ฉันคิดว่าจะอธิบายแนวคิดหลายอย่างพร้อมกันสำหรับฉัน ขอบคุณล่วงหน้า.
mschonaker

2
นี่คือรหัสการ
แจกจ่าย

8
ไม่มีอะไรผิดปกติกับการใช้ซิงเกิลตัน เพียงใช้มันอย่างถูกต้องเมื่อมันสมเหตุสมผล .. แน่นอนว่าพูดได้ดี เฟรมเวิร์ก Android จริง ๆ มีจำนวนมากสำหรับการรักษาแคชของทรัพยากรที่โหลดไว้และกระบวนการอื่น ๆ ตรงตามที่คุณพูด จากเพื่อนของคุณในโลก iOS "ทุกอย่างเป็นซิงเกิลตัน" ใน iOS ไม่มีอะไรที่จะเป็นธรรมชาติมากไปกว่าอุปกรณ์ทางกายภาพมากกว่าแนวคิดของซิงเกิล: จีพีเอส, นาฬิกา, ไจโร, ฯลฯ และอื่น ๆ - ตามแนวคิด เป็นซิงเกิลตัน? ใช่แล้ว
Fattie

22

จาก: นักพัฒนา> การอ้างอิง - แอปพลิเคชัน

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


1
และถ้าคุณเขียนอินเทอร์เฟซสำหรับซิงเกิลปล่อยให้ getInstance ไม่คงที่คุณสามารถสร้างคอนสตรัคค่าเริ่มต้นของคลาสที่ใช้ซิงเกิลตันฉีดอินพุทแบบซิงเกิลผ่านคอนสตรัคเตอร์ที่ไม่ใช่ค่าเริ่มต้นซึ่งก็เป็นคอนสตรัคเตอร์ คลาสที่ใช้ซิงเกิลในการทดสอบหน่วย
android.weasel

11

แอพลิเคชันไม่เหมือนกับซิงเกิลเหตุผลคือ:

  1. เมธอดของแอปพลิเคชัน (เช่น onCreate) ถูกเรียกในเธรด ui
  2. วิธีการของ singleton สามารถเรียกได้ในหัวข้อใด ๆ
  3. ในวิธีการ "onCreate" ของแอปพลิเคชันคุณสามารถสร้างอินสแตนซ์ของ Handler ได้
  4. หากดำเนินการซิงเกิลตันใน none-ui thread คุณไม่สามารถสร้างอินสแตนซ์ Handler ได้
  5. แอปพลิเคชั่นมีความสามารถในการจัดการวงจรชีวิตของกิจกรรมในแอปมันมีวิธีการ "registerActivityLifecycleCallbacks" แต่ singletons ไม่มีความสามารถ

1
หมายเหตุ: คุณสามารถสร้างอินสแตนซ์ของ Handler บนเธรดใดก็ได้ จาก doc: "เมื่อคุณสร้าง Handler ใหม่มันจะถูกผูกไว้กับคิวเธรด / ข้อความของเธรดที่สร้างมัน"
Christ

1
@Christ ขอบคุณมากตอนนี้ฉันได้เรียนรู้ "กลไกของ Looper" ถ้าอินสแตนซ์ตัวจัดการไม่มีเธรด ui โดยไม่มีรหัส 'Looper.prepare ()' ระบบจะรายงานข้อผิดพลาด "java.lang.RuntimeException: Can ไม่สร้างตัวจัดการภายในชุดข้อความที่ไม่ได้เรียกว่า Looper.prepare () "
sunhang

11

ฉันมีปัญหาเดียวกัน: ซิงเกิลหรือทำ subclass android.os.Application?

ครั้งแรกที่ฉันลองใช้ Singleton แต่แอปของฉันในบางจุดโทรไปที่เบราว์เซอร์

Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));

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

วิธีการแก้ไข: ใส่ข้อมูลที่ต้องการภายในคลาสย่อยของ Application class


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

การโหลดแบบซิงเกิลขี้เกียจหลังจากการรีสตาร์ทแอปพลิเคชันไม่ใช่วิธีที่ถูกต้องในการปล่อยให้วัตถุถูกกวาดโดย GC จุดอ่อนคือใช่มั้ย
mschonaker

15
จริงๆ? Dalvik ยกเลิกการโหลดคลาสและสูญเสียสถานะของโปรแกรมหรือไม่ คุณแน่ใจหรือไม่ว่าไม่ใช่การรวบรวมขยะประเภทวัตถุระยะเวลา จำกัด ที่เกี่ยวกับกิจกรรมที่คุณไม่ควรใส่ลงในซิงเกิลตันตั้งแต่แรก คุณต้องยกตัวอย่างให้กับการเรียกร้องพิเศษ!
android.weasel

1
เว้นแต่จะมีการเปลี่ยนแปลงที่ฉันไม่ทราบ Dalvik จะไม่ยกเลิกการโหลดคลาส เคย พฤติกรรมที่พวกเขาเห็นคือกระบวนการของพวกเขาถูกฆ่าในเบื้องหลังเพื่อให้มีพื้นที่สำหรับเบราว์เซอร์ พวกเขาอาจเริ่มต้นตัวแปรในกิจกรรม "หลัก" ของพวกเขาซึ่งอาจไม่ได้ถูกสร้างขึ้นในกระบวนการใหม่เมื่อกลับมาจากเบราว์เซอร์
Groxx

5

พิจารณาทั้งสองอย่างในเวลาเดียวกัน:

  • มีวัตถุ Singleton เป็นกรณีคงที่ภายในชั้นเรียน
  • มีคลาสทั่วไป (บริบท) ที่ส่งคืนอินสแตนซ์ซิงเกิลสำหรับวัตถุ Singelton ทั้งหมดในแอปพลิเคชันของคุณซึ่งมีข้อดีที่ชื่อเมธอดในบริบทจะมีความหมายตัวอย่างเช่น: context.getLoggedinUser () แทน User.getInstance ()

นอกจากนี้ฉันขอแนะนำให้คุณขยายบริบทของคุณเพื่อรวมไม่เพียง แต่การเข้าถึงวัตถุซิงเกิลตันเท่านั้น แต่ยังมีฟังก์ชันบางอย่างที่จำเป็นต้องเข้าถึงทั่วโลกเช่นตัวอย่างเช่น: context.logOffUser (), context.readSavedData () ฯลฯ อาจเปลี่ยนชื่อบริบทเป็น อาคารจะเข้าท่าแล้ว


4

พวกมันเหมือนกันจริงๆ มีความแตกต่างอย่างหนึ่งที่ฉันเห็น ด้วยคลาสแอปพลิเคชันคุณสามารถเริ่มต้นตัวแปรของคุณใน Application.onCreate () และทำลายพวกเขาใน Application.onTerminate () ด้วยซิงเกิลคุณต้องพึ่งพา VM การเริ่มต้นและการทำลายสถิตยศาสตร์


16
เอกสารสำหรับ onTerminate บอกว่ามันถูกเรียกโดยตัวจำลองเท่านั้น บนอุปกรณ์ที่วิธีการอาจจะไม่ถูกเรียก developer.android.com/reference/android/app/…
danb

3

2 เซนต์ของฉัน:

ฉันสังเกตเห็นว่ามีการลบฟิลด์เดี่ยว / คงที่บางส่วนเมื่อกิจกรรมของฉันถูกทำลาย ฉันสังเกตุสิ่งนี้ในอุปกรณ์ 2.3 อันต่ำสุด

กรณีของฉันง่ายมาก: ฉันเพิ่งมีไฟล์ส่วนตัว "init_done" และวิธีแบบคงที่ "init" ที่ฉันเรียกจาก activity.onCreate () ฉันสังเกตว่าเมธอด init กำลังเรียกใช้งานตัวเองอีกครั้งในการสร้างกิจกรรมใหม่

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

ฉันย้ายอินสแตนซ์ของ singleton ของฉันไปที่คลาสย่อยของ Application ฉัน acces พวกเขาจากอินสแตนซ์ของแอปพลิเคชัน และตั้งแต่นั้นไม่ได้สังเกตเห็นปัญหาอีกครั้ง

ฉันหวังว่านี่จะช่วยคนได้


3

จากปากม้าสุภาษิต ...

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

เมื่อเขียนแอพ Android คุณรับประกันได้ว่าจะมีเพียงหนึ่งอินสแตนซ์ของคลาส android.app.Application เท่านั้นดังนั้นจึงปลอดภัย (และแนะนำโดยทีมงาน Google Android) เพื่อรักษามันให้เป็นแบบซิงเกิล นั่นคือคุณสามารถเพิ่มเมธอด getInstance () แบบคงที่ให้กับการใช้งานแอปพลิเคชันของคุณได้อย่างปลอดภัย ชอบมาก

public class AndroidApplication extends Application {

    private static AndroidApplication sInstance;

    public static AndroidApplication getInstance(){
        return sInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
    }
}

2

กิจกรรมของฉันโทรเสร็จสิ้น () (ซึ่งจะไม่ทำให้เสร็จในทันที แต่จะทำในที่สุด) และโทรไปที่ Google Street Viewer เมื่อฉันดีบั๊กมันบน Eclipse การเชื่อมต่อของฉันกับแอพจะหยุดเมื่อมีการเรียก Street Viewer ซึ่งฉันเข้าใจว่าแอปพลิเคชัน (ทั้งหมด) ถูกปิดควรจะเพิ่มหน่วยความจำ (กิจกรรมเดียวที่เสร็จสิ้นไม่ควรทำให้เกิดพฤติกรรมนี้) . อย่างไรก็ตามฉันสามารถบันทึกสถานะใน Bundle ผ่าน onSaveInstanceState () และกู้คืนได้ในเมธอด onCreate () ของกิจกรรมถัดไปในสแต็ก ไม่ว่าโดยการใช้แอพพลิเคชั่นแบบสแตติกตันหรือการทำคลาสย่อยฉันต้องเผชิญกับการปิดแอปพลิเคชันและการสูญเสียสถานะ (เว้นแต่ฉันบันทึกไว้ใน Bundle) ดังนั้นจากประสบการณ์ของฉันพวกเขาก็เหมือนกันกับการดูแลรักษาของรัฐ ฉันสังเกตเห็นว่าการเชื่อมต่อขาดหายไปใน Android 4.1.2 และ 4.2.2 แต่ไม่ได้อยู่ใน 4.0.7 หรือ 3.2.4


"ฉันสังเกตเห็นว่าการเชื่อมต่อขาดหายไปใน Android 4.1.2 และ 4.2.2 แต่ไม่ใช่ใน 4.0.7 หรือ 3.2.4 ซึ่งในความเข้าใจของฉันชี้ให้เห็นว่ากลไกการกู้คืนหน่วยความจำได้เปลี่ยนไปในบางจุด" ..... ฉันคิดว่าอุปกรณ์ของคุณไม่มีจำนวนหน่วยความจำเท่ากันหรือติดตั้งแอพเดียวกัน และข้อสรุปของคุณอาจไม่ถูกต้อง
Christ

@Christ: ใช่คุณจะต้องถูกต้อง มันจะแปลกถ้ากลไกการกู้คืนหน่วยความจำมีการเปลี่ยนแปลงระหว่างรุ่น อาจเป็นการใช้หน่วยความจำที่แตกต่างกันทำให้เกิดพฤติกรรมที่แตกต่าง
Piovezan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.