การใช้งานซอร์สโค้ดขนมปังปิ้ง
ทางเข้าขนมปังปิ้ง
เมื่อเราใช้พรอมต์ขนมปังในแอปพลิเคชันเรามักจะโทรรหัสง่าย ๆ ดังที่แสดงด้านล่าง:
[Java] ดู PlainCopyPrint?
toast.maketext (บริบท, ผงชูรส, toast.length_short). show ();
MakeText เป็นทางเข้าของ Toast ซอร์สโค้ดมีดังนี้ (Frameworks/base/core/java/Android/widget/toast.java):
Public Toast Maketext (บริบทบริบท, ข้อความ Charsequence, ระยะเวลา int) {toast result = toast ใหม่ (บริบท); internet.layout.transient_notification, null); ระยะเวลา;จากซอร์สโค้ดของ makeText เราจะเห็นว่าไฟล์เลย์เอาต์ของขนมปังปิ้งคือ transient_notification.xml อยู่ในเฟรมเวิร์ก/ฐาน/แกน/res/layout/transition_notification.xml:
<? XML เวอร์ชัน = "1.0" การเข้ารหัส = "UTF-8" match_parent "Android: Orientation =" Vertical "Android: background ="? "WRAP_CONTENT" Android: layout_weight = "1" Android: layout_gravity = "center_horizontal" Android: textapperance = "@style/textaperance.toast" "Android: textcolor ="@color/bright_foreground_dark shadowradius = "2.75" /> < /linearlayout>
ไฟล์เลย์เอาต์ของขนมปังปิ้งนั้นง่ายมากนั่นคือทิวทัศน์จะถูกวางไว้ใน linearlayout ของเค้าโครงแนวตั้ง ต่อไปเราจะติดตามวิธีการแสดง () เพื่อศึกษาการใช้รหัสการแสดงผลหลังจากการจัดวาง:
โมฆะสาธารณะแสดง () {ถ้า (mnextview == null) {โยน runtimeException ใหม่ ("SetView ต้องถูกเรียกว่า"); ; ลอง {service.enqueuetoast (pkg, tn, mduration); มีสองจุดในวิธีการแสดงที่เราต้องให้ความสนใจ (1) TN คืออะไร? (2) บทบาทของบริการ InotificationManager ด้วยปัญหาทั้งสองนี้ให้ดำเนินการซอร์สโค้ดขนมปังปิ้งของเรา
ซอร์สโค้ด TN
คำถามมากมายสามารถค้นหาคำตอบผ่านซอร์สโค้ด การใช้งาน MTN ในคอนสตรัคเตอร์ของขนมปังปิ้งซอร์สโค้ดมีดังนี้:
Toast Public (บริบท) {McOnText = บริบท; () .getInteger (com.android.internal.r.iinteger.config_toastdefaultgravity);}ต่อไปเราเริ่มจากซอร์สโค้ดของคลาส TN เพื่อสำรวจบทบาทของ TN ซอร์สโค้ด TN มีดังนี้:
คลาสสแตติกส่วนตัว TN ขยาย itransitenotification.stub {สุดท้าย runnable mshow = new runnable () {@Override โมฆะสาธารณะเรียกใช้ () {Handyless ();}; (); mx int, My; อย่างเหมาะสม WindoManager.layoutparams.type_toast; params.privateflags = windowmanager.layoutparams.private_flag_for_all_users; .post (mshow);} / ** * กำหนดการจับ handlehide insto เธรดขวา * / @Override โมฆะสาธารณะซ่อน () {ถ้า (locallogv) log.v (แท็ก (แท็ก "ซ่อน:" + สิ่งนี้); mhandler.post ( mhide);} โมฆะสาธารณะ handleshow () {ถ้า (locallogv) log.v (แท็ก, "handle show:" + this + "mview =" + mview + "xtview =" + mnextview); {// ลบมุมมองเก่าหากจำเป็นต้องใช้ handlehide (); (WindowManager) บริบท GetSystemService (Context.window_service); แรงโน้มถ่วง int = gravity.getabsolutegravity (mgravity, config.getlayoutdirection ()); (Gravity & Gravity.vertical_gravity_mask) == Gravity.fill_vertical) {mparams.verticalweight = 1.0 f;} mparams.x = mx; getParent ()! เพิ่ม! " + mView +" ใน " + this); mwm.addview (mView, mParams); trysendac senseEvent ();}}}}} daccessibilityEvent () {accessibilityManager AccessibilityManager = AccessibilityManager. (! accessiblemanager.isenabled ()) {return;} // treat toasts เป็นการแจ้งเตือนตั้งแต่การใช้ // ชิ้นส่วนของข้อมูลชั่วคราวไปยังเหตุการณ์ผู้ใช้ EcessibleEvent = AccessibleEvent.obtain.obtain; DispatchPopultAccessibilityEvent (เหตุการณ์); = null) {// ไม่: ตรวจสอบสี () เพียงแค่ mak พยายามอย่าชน ผ่านซอร์สโค้ดเราสามารถเห็นความสัมพันธ์ในการสืบทอดได้อย่างชัดเจน สันนิษฐานว่าผู้อ่านมีพื้นฐานของการสื่อสารระหว่างกระบวนการ Android (ไม่เป็นที่รู้จักกันดีในการเรียนรู้ชุดของบล็อกของการสื่อสารของ Luo Shengyang เกี่ยวกับการสื่อสารกระบวนการสารยึดเกาะ) เนื่องจาก TN ใช้สำหรับการสื่อสารระหว่างกระบวนการมันเป็นเรื่องง่ายสำหรับเราที่จะคิดว่าบทบาทเฉพาะของคลาส TN ควรเป็นวัตถุการโทรกลับของคลาสขนมปังปิ้ง คลาส TN
คลาส TN นั้นสืบทอดมาจาก itransitenotification.stub, itransientnotification.aidl ตั้งอยู่ในเฟรมเวิร์ก/ฐาน/แกน/java/app/itransientnotification.aidl, ซอร์สโค้ดมีดังนี้: ดังต่อไปนี้:
แพ็คเกจ Android.app;
ItransientNotification กำหนดสองวิธีแสดง () และซ่อน () และการใช้งานเฉพาะของพวกเขาอยู่ในคลาส TN การใช้งานคลาส TN คือ:
/ ***กำหนดเวลา handleshow ลงในเธรดที่ถูกต้อง* / @Override public void show () {ถ้า (locallogv) log.v (แท็ก, "แสดง:" + สิ่งนี้);;} / *** */ @Override โมฆะสาธารณะซ่อน () {ถ้า (locallogv) log.v (แท็ก, "ซ่อน:" + สิ่งนี้);ที่นี่เราสามารถรู้ได้ว่าการแสดงและซ่อนวิธีการของ Toast นั้นขึ้นอยู่กับกลไกของตัวจัดการ การใช้งานตัวจัดการในคลาส TN คือ:
handl สุดท้าย mhandler = new handler ();
ยิ่งกว่านั้นเราไม่พบวิธี looper.perpare () และ looper.loop () ในคลาส TN มันแสดงให้เห็นว่า mhandler เรียกวัตถุ looper ของเธรดปัจจุบัน ดังนั้นเมื่อเราอยู่ในเธรดหลัก (นั่นคือในเธรด UI) เราสามารถเรียกวิธี Toast.maketext ได้ตามความประสงค์เนื่องจากระบบ Android ช่วยให้เราตระหนักถึงการเริ่มต้นลูโฟนของเธรดหลัก但是, 如果你想在子线程中调用 toast.maketext 方法, 就必须先进行 looper 初始化了, 不然就会报出 java.lang.runtimeException: ไม่สามารถสร้างตัวจัดการภายในเธรดที่ไม่ได้เรียกว่า looper.prepare () แก่นแท้ การเรียนรู้กลไกการจัดการสามารถอ้างถึงบล็อกที่ฉันเขียนไว้ก่อนหน้านี้: http://blog.csdn.net/wzy_1988/article/details/38346637
ถัดไปดำเนินการต่อไปเพื่อติดตามการดำเนินการของ MSHOW และ MHIDE ซึ่งทั้งสองอย่างนี้สามารถวิ่งได้
mshow ที่รันได้สุดท้าย = new runnable () {@Override public void run () {handleshow ();}}; HADLESHOW () MNEXTVIEW = NULL;}}; จะเห็นได้ว่าการใช้งานจริงของการแสดงและซ่อนคือการเรียกใช้วิธีการ handleshow () และ handlehide () ตามลำดับ มาดูการใช้งานเฉพาะของ HADLESHOW ():
โมฆะสาธารณะ handleshow () {ถ้า (mView! = mnextview) {// ลบมุมมองเก่าหากจำเป็นต้องใช้ handlehide (); ) {context = mView.getContext ();} mwm = (windowmanager) context.getSystemService (context.window_service); .getContext () .getResources () .fill_horizontal) {mparams.horizontalweight = 1.0f;} ถ้า (แรงโน้มถ่วงและแรงโน้มถ่วง VERTICAL_GRAVITY_MASK) == GRAVITY.FILL_VERTICAL) = mverticalmargin; จากซอร์สโค้ดเรารู้ว่าขนมปังจะถูกโหลดใน AddView ผ่าน WindowManager ดังนั้นวิธีการซ่อนจึงเป็น WindowManager ตามธรรมชาติเพื่อเรียกใช้วิธีการลบเพื่อลบมุมมองขนมปังปิ้ง
เพื่อสรุปโดยการวิเคราะห์ซอร์สโค้ดของคลาส TN เรารู้ว่าคลาส TN เป็นวัตถุการโทรกลับและกระบวนการอื่น ๆ เรียกวิธีการแสดงและซ่อนวิธีการของคลาส TN เพื่อควบคุมการแสดงผลและการหายตัวไปของขนมปังปิ้งนี้
NotificationManagerservice
กลับไปที่วิธีการแสดงของคลาสขนมปังปิ้งเราจะเห็นว่า GetService ถูกเรียกที่นี่เพื่อรับบริการ InotificationManager
เอกชน inotificationManager SSERVICE; InotificationManager ส่วนตัว GetService () {ถ้า (sservice! = null) {return sservice;} sservice = ใน otifinager.stub.asinterface (servicemanager.getService หลังจากได้รับบริการ InotificationManager วิธีการ enqueuetoast ถูกเรียกให้ใส่ขนมปังปัจจุบันลงในคิวขนมปังปิ้งของระบบ พารามิเตอร์ของ PASS คือ PKG, TN และ MDURATION กล่าวอีกนัยหนึ่งเราใช้ toast.maketext (บริบท, ผงชูรส, toast.length_show). show () เพื่อนำเสนอขนมปังปิ้ง ระบบเรียกวิธีการแสดงและซ่อนวิธีการโทรกลับ TN เพื่อแสดงและซ่อนขนมปังปิ้ง
คลาสการใช้งานเฉพาะของอินเทอร์เฟซ InofiticAnager ที่นี่คือคลาส NotificationManagerservice ซึ่งอยู่ในเฟรมเวิร์ก/ฐาน/บริการ/Java/com/server/notificationManagervice.java
ก่อนอื่นมาวิเคราะห์ฟังก์ชั่นของการเข้าร่วมของ Toast ใน Enqueuetoast
โมฆะสาธารณะ enqeuetoast (สตริง pkg, itransientnotification callback, ระยะเวลา int) {// packagename เป็นคลาส null หรือ tn เป็น null ) || ("Android" .Equals (PKG)); (pkg, binder.getcallinguid ()) &&! // (2) ดูว่า Toast มีดัชนี int อยู่แล้ว = i ndexoftoastlocked (pkg, callback); รับ (ดัชนี); record.update (ระยะเวลา);} else {// non -system toast แต่ละ pkg อยู่ในปัจจุบันจำนวนทั้งหมดของขนมปังปิ้งใน mtoastqueue ไม่เกิน max_package_notifications ถ้า (! issystemtoast) {int count = 0; int สุดท้าย n = mtoastqueue.size (); ++; ถ้า (count> = max_package_notifications) {slog.e (แท็ก, "แพ็คเกจมีการโพสต์" + count + "toasts ไม่แสดงเพิ่มเติมแพ็คเกจ =" + pkg); Toastrecord วัตถุและวางไว้ใน mtoastqueue ) หากดัชนีคือ 0 หมายความว่าทีมงานปัจจุบันอยู่ที่ทีม }}} จะเห็นได้ว่าฉันได้แสดงความคิดเห็นสั้น ๆ เกี่ยวกับรหัสด้านบน รหัสค่อนข้างง่าย แต่มีรหัสโน้ต 4 คะแนนที่ต้องการให้เราหารือเพิ่มเติม
(1) พิจารณาว่าเป็นขนมปังปิ้งหรือไม่ หากชื่อแพ็คเกจของขนมปังปัจจุบันคือ "Android" มันเป็นระบบขนมปังปิ้งมิฉะนั้นคุณสามารถเรียกใช้วิธีการ ISCallersystem () เพื่อตัดสิน ซอร์สโค้ดการใช้งานของวิธีนี้คือ:
บูลีน isuidsystem (int uid) {สุดท้าย int appid = userhandle.getAppid (uid); ());} ซอร์สโค้ดของ ISCallersystem นั้นค่อนข้างง่ายนั่นคือเพื่อตรวจสอบว่า UID ของกระบวนการขนมปังปิ้งเป็นหนึ่งใน system_uid, 0, phone_uid ถ้าเป็นมันเป็นระบบขนมปัง ขนมปังปิ้ง.
ไม่ว่าจะเป็นขนมปังปิ้งอ่านรหัสแหล่งที่มาด้านล่างจะเห็นได้ว่ามีข้อได้เปรียบหลักสองประการ:
Toast System สามารถเข้าสู่คิวขนมปังปิ้งได้อย่างแน่นอนและจะไม่หยุดโดยบัญชีดำ
Toast System ไม่มีขีด จำกัด ปริมาณในคิวขนมปังปิ้งในขณะที่ขนมปังปิ้งที่ส่งโดย PKG ทั่วไปมีขีด จำกัด ปริมาณในคิวขนมปังปิ้ง
(2) ดูว่าขนมปังปิ้งที่จะได้รับความไว้วางใจให้กับทีมอยู่ในคิวขนมปังปิ้งอยู่แล้วหรือไม่ สิ่งนี้ทำได้โดยการเปรียบเทียบ PKG และการโทรกลับ
INTEDING INDECTOASTLOCKED (String PKG, ItransientNotification Callback) {iBinder cbak = callback.asbinder (); ++) {toastrecord r = list.get (i); ถ้า (r.pkg.equals (pkg) && r.callback.asbinder () == cbak) {return i;}} return -1;} ผ่านรหัสข้างต้นเราสามารถสรุปได้ว่าตราบใดที่ชื่อ PKG ของขนมปังปิ้งนั้นสอดคล้องกับวัตถุ TN ระบบจะพิจารณาขนมปังปิ้งเหล่านี้เหมือนขนมปังปิ้งเดียวกัน
(3) ตั้งค่ากระบวนการขนมปังปัจจุบันเป็นการประมวลผลแผนกต้อนรับส่วนหน้า ซอร์สโค้ดแสดงด้านล่าง:
โมฆะส่วนตัว keepprocessalivelocked (int pid) {int toastcount = 0; list.get (i); ถ้า (r.pid == pid) {toastcount ++;}} ลอง {mam.setprocessforeground (mforegrounttoken, pid, toastcount> 0);} cat 'ไม่เกิดขึ้น}} MAM = ActivityManagernative.getDefault () ที่นี่เรียกวิธีการ setProcessForeground เพื่อวางกระบวนการ PID ปัจจุบันลงในการประมวลผลแผนกต้อนรับเพื่อให้แน่ใจว่าจะไม่ถูกฆ่าอย่างเป็นระบบ นอกจากนี้ยังอธิบายว่าทำไมเมื่อเราทำกิจกรรมเสร็จในปัจจุบันขนมปังปิ้งสามารถแสดงได้เนื่องจากกระบวนการปัจจุบันยังคงดำเนินการอยู่
(4) เมื่อดัชนีเป็น 0 ขนมปังปิ้งของหัวคิวจะปรากฏขึ้น ซอร์สโค้ดมีดังนี้:
โมฆะส่วนตัวแสดงให้เห็นถึงการบันทึก () {// รับ toastrecord toastrecord Record = mtoastqueue.get (0); (); ScheduleTimeOutlocked (บันทึก); รายการและปล่อยให้ procese int index = mtoastqueue.indexof (บันทึก); = mtoastqueue.get (0);} else {record = null;}}}}}วัตถุการโทรกลับของขนมปังปิ้งคือวัตถุ TN ถัดไปลองมาดูกันว่าทำไมเวลาแสดงของ System Toast จึงเป็นเพียง 2s หรือ 3.5s คีย์คือการใช้วิธี Scheduletimeoutlocked หลักการคือหลังจากเรียกใช้วิธีการแสดงของ TN เพื่อแสดงขนมปังคุณต้องโทรหาวิธี ScheduletMelocklocked เพื่อหายไปขนมปังปิ้ง (หากคุณมีคำถามใด ๆ : ไม่ต้องบอกว่าวิธีซ่อนของวัตถุ TN จะหายไปขนมปัง และเรามักจะมีสำนักงานของเรา使用的ขนมปังปิ้ง都会在当前กิจกรรม 停留几秒。如何实现停留几秒呢?原理就是 ScheduleTimeoutlocked 发送 message_timeout 消息去调用 tn 对象的ซ่อน方法, 但是这个消息会有一个ล่าช้า延迟, 这里这里也是用了 handler 消息机制)) 。
INT LONG_DELAY ครั้งสุดท้ายส่วนตัว = 3500; Long Delay = R.Duration == Toast.length_long?
ก่อนอื่นเราเห็นว่าสิ่งนี้ที่นี่ไม่ได้ส่งข้อความ Message_timeOut โดยตรง แต่มีความล่าช้าในการล่าช้า เวลาของความล่าช้าจาก "long delay = r.duration == toast.length_long? long_dlay: short_dlay;" 2S หรือ 3.5S มันไม่มีประโยชน์ที่จะผ่านระยะเวลาในวิธี toast.maketext ตามต้องการ
ถัดไปลองมาดูกันว่าข้อความ Message_timeOut ถูกประมวลผลใน WorkerHandler อย่างไร ประเภทของวัตถุ mhandler คือ WorkerHandler ซอร์สโค้ดมีดังนี้:
WorkerHandler ชั้นเรียนสุดท้ายขยายตัว handler {@Override โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {switch (msg.what) {case message_timeout: handleti meout (toastrecord) msg.obj;จะเห็นได้ว่าการประมวลผลข้อความของ WorkRhandler ไปยังประเภท message_timeout คือการเรียกใช้วิธี handlertimeout
โมฆะส่วนตัว handletimeout (บันทึก toastrecord) {ซิงโครไนซ์ (mtoastqueue) {int index = indexoftoastlocked (record.pkg, record.callback);ในรหัส Handletimeout ก่อนอื่นให้พิจารณาว่าวัตถุ toastrecord ที่ต้องหายไปนั้นอยู่ในคิวหรือไม่ ความจริงกำลังจะปรากฏต่อหน้าเราดำเนินการติดตามซอร์สโค้ดต่อไป:
โมฆะส่วนตัว CanceltoAstlocked (INT ดัชนี) {toAstrecord Record = MTOASTQUEUE.GET (ดัชนี); // รายการต่อไป} mtoastqueue.remove (ดัชนี); รายการดังนั้น Donforms ว่ารายการจะไม่เปลี่ยนแปลง // หลังจากจุดนี้ ฮ่าฮ่าดูที่นี่วิธีซ่อนของวัตถุการโทรกลับของเราได้ถูกเรียกและมันก็ถูกลบออกจาก mtoastqueue จาก mtoastqueue ณ จุดนี้การแสดงผลที่สมบูรณ์และการหายตัวไปของขนมปังจะสิ้นสุดลง