قم بقياس وقت التحميل وعرض الشاشة باستخدام مراقبة أداء Firebase

1 المقدمة

آخر تحديث: 2021-03-11

لماذا نحتاج إلى قياس أداء المشاهدات؟

تعد طرق العرض جزءًا أساسيًا من تطبيقات Android والتي تؤثر بشكل مباشر على تجربة المستخدم. على سبيل المثال، يحتوي نشاطك أو جزءك على واجهة المستخدم التي تحتوي على مكونات العرض التي يتفاعل معها المستخدمون. لا يتمكن المستخدمون من رؤية محتوى واجهة المستخدم بالكامل حتى يتم رسمه بالكامل على الشاشة. ستؤدي الشاشات البطيئة والمتجمدة إلى إضعاف تفاعل المستخدم مع تطبيقك بشكل مباشر وإنشاء تجربة مستخدم سيئة.

ألا توفر مراقبة أداء Firebase مقاييس الأداء هذه خارج الصندوق؟

تلتقط مراقبة أداء Firebase تلقائيًا بعض بيانات الأداء الجاهزة، مثل وقت بدء تطبيقك (على سبيل المثال، وقت التحميل لنشاطك الأول فقط) وأداء عرض الشاشة (على سبيل المثال، الإطارات البطيئة والمجمدة للأنشطة ولكن ليس للأنشطة فتات). ومع ذلك، لا تحتوي تطبيقات الصناعة عادةً على الكثير من الأنشطة، بل تحتوي على نشاط واحد وأجزاء متعددة. بالإضافة إلى ذلك، تقوم العديد من التطبيقات عادةً بتنفيذ طرق العرض المخصصة الخاصة بها لحالات الاستخدام الأكثر تعقيدًا. لذلك، غالبًا ما يكون من المفيد فهم كيفية قياس وقت التحميل وأداء عرض الشاشة لكل من الأنشطة والأجزاء من خلال استخدام أدوات تتبع التعليمات البرمجية المخصصة في تطبيقك. يمكنك بسهولة توسيع هذا الدرس التطبيقي حول التعليمات البرمجية لقياس أداء مكونات العرض المخصص.

ما ستتعلمه

  • كيفية إضافة مراقبة أداء Firebase إلى تطبيق Android
  • فهم تحميل نشاط أو جزء
  • كيفية استخدام تتبعات التعليمات البرمجية المخصصة لقياس وقت تحميل نشاط أو جزء
  • فهم عرض الشاشة وما هو الإطار البطيء/المجمد
  • كيفية استخدام تتبعات التعليمات البرمجية المخصصة باستخدام المقاييس لتسجيل الشاشات البطيئة/المجمدة
  • كيفية عرض المقاييس المجمعة في وحدة تحكم Firebase

ماذا ستحتاج

  • أندرويد ستوديو 4.0 أو أعلى
  • جهاز/محاكي Android
  • جافا الإصدار 8 أو أعلى

2. الإعداد

احصل على الرمز

قم بتشغيل الأوامر التالية لاستنساخ نموذج التعليمات البرمجية لهذا الدرس التطبيقي حول التعليمات البرمجية. سيؤدي هذا إلى إنشاء مجلد يسمى codelab-measure-android-view-performance على جهازك:

$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance

إذا لم يكن لديك git على جهازك، فيمكنك أيضًا تنزيل الكود مباشرة من GitHub.

قم باستيراد مشروع measure-view-performance-start إلى Android Studio. من المحتمل أن ترى بعض أخطاء الترجمة أو ربما تحذيرًا بشأن فقدان ملف google-services.json . سنقوم بتصحيح هذا في القسم التالي من هذه الخطوة.

في هذا الدرس التطبيقي حول التعليمات البرمجية، سنستخدم المكوّن الإضافي Firebase Assistant لتسجيل تطبيق Android الخاص بنا مع مشروع Firebase وإضافة ملفات تكوين Firebase والمكونات الإضافية والتبعيات الضرورية إلى مشروع Android الخاص بنا - كل ذلك من داخل Android Studio !

قم بتوصيل تطبيقك بـ Firebase

  1. انتقل إلى Android Studio / Help > التحقق من وجود تحديثات للتأكد من أنك تستخدم أحدث إصدارات Android Studio وFirebase Assistant.
  2. حدد أدوات > Firebase لفتح جزء المساعد .

e791bed0999db1e0.png

  1. اختر مراقبة الأداء لإضافتها إلى تطبيقك، ثم انقر على بدء استخدام مراقبة الأداء .
  2. انقر فوق Connect to Firebase لتوصيل مشروع Android الخاص بك بـ Firebase (سيؤدي ذلك إلى فتح وحدة تحكم Firebase في متصفحك) .
  3. في وحدة تحكم Firebase، انقر على إضافة مشروع ، ثم أدخل اسم مشروع Firebase (إذا كان لديك مشروع Firebase بالفعل، فيمكنك تحديد هذا المشروع الحالي بدلاً من ذلك) . انقر على "متابعة" واقبل الشروط لإنشاء مشروع Firebase وتطبيق Firebase جديد.
  4. من المفترض أن ترى بعد ذلك مربع حوار لتوصيل تطبيق Firebase الجديد بمشروع Android Studio الخاص بك.

42c498d28ead2b77.png

  1. بالعودة إلى Android Studio، في جزء المساعد ، من المفترض أن ترى تأكيدًا بأن تطبيقك متصل بـ Firebase.

dda8bdd9488167a0.png

أضف مراقبة الأداء إلى تطبيقك

في جزء "المساعد " في Android Studio، انقر فوق "إضافة مراقبة الأداء إلى تطبيقك ".

من المفترض أن تشاهد مربع حوار لقبول التغييرات وبعد ذلك يجب على Android Studio مزامنة تطبيقك للتأكد من إضافة جميع التبعيات الضرورية.

9b58145acc4be030.png

أخيرًا، من المفترض أن ترى رسالة النجاح في جزء المساعد في Android Studio والتي تفيد بأنه تم إعداد جميع التبعيات بشكل صحيح.

aa0d46fc944e0c0b.png

كخطوة إضافية، قم بتمكين تسجيل التصحيح عن طريق اتباع الإرشادات الواردة في الخطوة "(اختياري) تمكين تسجيل التصحيح". نفس التعليمات متوفرة أيضًا في الوثائق العامة .

3. قم بتشغيل التطبيق

إذا قمت بدمج تطبيقك بنجاح مع SDK لمراقبة الأداء، فيجب الآن تجميع المشروع. في Android Studio، انقر فوق تشغيل > تشغيل "التطبيق" لإنشاء التطبيق وتشغيله على جهاز/محاكي Android المتصل لديك.

يحتوي التطبيق على زرين ينقلانك إلى النشاط والجزء المقابل، مثل هذا:

410d8686b4f45c33.png

في الخطوات التالية من هذا الدرس التطبيقي حول التعليمات البرمجية، ستتعلم كيفية قياس وقت التحميل وأداء عرض الشاشة لنشاطك أو الجزء الخاص بك.

4. فهم تحميل نشاط أو جزء

في هذه الخطوة، سوف نتعرف على ما يفعله النظام أثناء تحميل النشاط أو الجزء.

فهم تحميل النشاط

بالنسبة للنشاط، يتم تعريف وقت التحميل على أنه الوقت الذي يبدأ من وقت إنشاء كائن النشاط حتى يتم رسم الإطار الأول بالكامل على الشاشة ( وهذا هو الوقت الذي سيرى فيه المستخدم واجهة المستخدم الكاملة للنشاط لأول مرة وقت ). لقياس ما إذا كان تطبيقك مرسومًا بالكامل، يمكنك استخدام طريقة reportFullyDrawn() لقياس الوقت المنقضي بين تشغيل التطبيق والعرض الكامل لجميع الموارد وعرض التسلسلات الهرمية.

على مستوى عالٍ، عندما يستدعي تطبيقك startActivity(Intent) ، يقوم النظام تلقائيًا بتنفيذ العمليات التالية. تستغرق كل عملية وقتًا حتى تكتمل، مما يزيد من المدة الزمنية بين إنشاء النشاط والوقت الذي يرى فيه المستخدم واجهة المستخدم الخاصة بالنشاط على شاشته.

c20d14b151549937.png

فهم تحميل جزء

كما هو الحال مع النشاط، يتم تعريف وقت تحميل الجزء على أنه الوقت الذي يبدأ من وقت ربط الجزء بالنشاط المضيف الخاص به حتى يتم رسم الإطار الأول لعرض الجزء بالكامل على الشاشة.

5. قياس وقت تحميل النشاط

يمكن أن تؤدي التأخيرات في الإطار الأول إلى تجربة مستخدم سيئة، لذا من المهم فهم مقدار تأخير التحميل الأولي الذي يواجهه المستخدمون. يمكنك استخدام أداة تتبع تعليمات برمجية مخصصة لقياس وقت التحميل هذا:

  1. ابدأ تتبع التعليمات البرمجية المخصصة (المسمى TestActivity-LoadTime ) في فئة النشاط بمجرد إنشاء كائن النشاط.

TestActivity.java

public class TestActivity extends AppCompatActivity {   
    // TODO (1): Start trace recording as soon as the Activity object is created.
    private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");

    // ...

}
  1. تجاوز رد الاتصال onCreate() ، واحصل على العرض المضاف بواسطة طريقة setContentView() .
@Override     
public void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);          

    // Current Activity's main View (as defined in the layout xml file) is inflated after this            
    setContentView(R.layout.activity_test);          

    // ...

    // TODO (2): Get the View added by Activity's setContentView() method.         
    View mainView = findViewById(android.R.id.content);     

    // ...
}
  1. لقد قمنا بتضمين تطبيق FistDrawListener ، الذي يحتوي على ردي اتصال: onDrawingStart() و onDrawingFinish() (راجع القسم التالي أدناه للحصول على مزيد من التفاصيل حول FirstDrawListener وما يمكن أن يؤثر على أدائه) . قم بتسجيل FirstDrawListener في نهاية رد الاتصال onCreate() الخاص بالنشاط. يجب عليك إيقاف viewLoadTrace في رد الاتصال onDrawingFinish() .

TestActivity.java

    // TODO (3): Register the callback to listen for first frame rendering (see
    //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
    //  finished.
    FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {              
        @Override             
        public void onDrawingStart() {       
          // In practice you can also record this event separately
        }

        @Override             
        public void onDrawingFinish() {
            // This is when the Activity UI is completely drawn on the screen
            viewLoadTrace.stop();             
        }         
    });
  1. أعد تشغيل التطبيق. بعد ذلك، قم بتصفية السجل باستخدام " قياس تتبع التسجيل ". اضغط على زر LOAD ACTIVITY ، وابحث عن السجلات كما يلي:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 تهانينا! لقد قمت بقياس وقت تحميل أحد الأنشطة بنجاح وأبلغت عن هذه البيانات إلى مراقبة أداء Firebase. سنعرض المقياس المسجل في وحدة تحكم Firebase لاحقًا في هذا الدرس التطبيقي حول الترميز.

الغرض من FirstDrawListener

في القسم أعلاه، قمنا بتسجيل FirstDrawListener . الغرض من FirstDrawListener هو قياس وقت بدء الإطار الأول وانتهاء الرسم.

يقوم بتنفيذ ViewTreeObserver.OnDrawListener ويتجاوز رد الاتصال onDraw() الذي يتم استدعاؤه عندما تكون شجرة العرض على وشك الرسم. ثم يقوم بتغليف النتيجة لتوفير ردي اتصال للأداة المساعدة onDrawingStart() و onDrawingFinish() .

يمكن العثور على التعليمات البرمجية الكاملة لـ FirstDrawListener في التعليمات البرمجية المصدر الخاصة ببرنامج Codelab هذا.

6. قياس وقت تحميل جزء ما

يشبه قياس وقت تحميل جزء ما كيفية قياسه لنشاط ما، ولكن مع بعض الاختلافات الطفيفة. مرة أخرى، سنقوم بتتبع التعليمات البرمجية المخصصة :

  1. تجاوز رد الاتصال onAttach() وابدأ في تسجيل fragmentLoadTrace الخاص بك. سنقوم بتسمية هذا التتبع Test-Fragment-LoadTime .

كما هو موضح في خطوة سابقة، يمكن إنشاء كائن Fragment في أي وقت، ولكنه يصبح نشطًا فقط عندما يكون مرتبطًا بالنشاط المضيف الخاص به.

TestFragment.java

public class TestFragment extends Fragment {

   // TODO (1): Declare the Trace variable.
   private Trace fragmentLoadTrace;

   @Override
   public void onAttach(@NonNull Context context) {
       super.onAttach(context);

       // TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
       fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
   }
  1. قم بتسجيل FirstDrawListener في رد الاتصال onViewCreated() . ثم، كما هو الحال في مثال النشاط، قم بإيقاف التتبع في onDrawingFinish() .

TestFragment.java

@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
   super.onViewCreated(mainView, savedInstanceState);

   // ...

   // TODO (3): Register the callback to listen for first frame rendering (see
   //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
   //  finished.
   FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {

       @Override
       public void onDrawingStart() {
           // In practice you can also record this event separately
       }

       @Override
       public void onDrawingFinish() {
           // This is when the Fragment UI is completely drawn on the screen
           fragmentLoadTrace.stop();
       }
   });
  1. أعد تشغيل التطبيق. بعد ذلك، قم بتصفية السجل باستخدام " قياس تتبع التسجيل ". اضغط على زر LOAD FRAGMENT ، وابحث عن السجلات كما هو موضح أدناه:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

🎉 تهانينا! لقد قمت بقياس وقت تحميل أحد الأجزاء بنجاح وأبلغت عن هذه البيانات إلى مراقبة أداء Firebase. سنعرض المقياس المسجل في وحدة تحكم Firebase لاحقًا في هذا الدرس التطبيقي حول الترميز.

7. فهم عرض الشاشة وما هو الإطار البطيء/المجمد

عرض واجهة المستخدم هو عملية إنشاء إطار من تطبيقك وعرضه على الشاشة. للتأكد من أن تفاعل المستخدم مع تطبيقك سلس، يجب أن يعرض تطبيقك الإطارات في أقل من 16 مللي ثانية لتحقيق 60 إطارًا في الثانية ( لماذا 60 إطارًا في الثانية؟ ). إذا كان تطبيقك يعاني من بطء عرض واجهة المستخدم، فسيضطر النظام إلى تخطي الإطارات، وسيلاحظ المستخدم التأتأة في تطبيقك. نحن نسمي هذا خردة .

وبالمثل، فإن الإطارات المجمدة هي إطارات واجهة المستخدم التي يستغرق عرضها وقتًا أطول من 700 مللي ثانية. يمثل هذا التأخير مشكلة لأن تطبيقك يبدو عالقًا ولا يستجيب لإدخال المستخدم لمدة ثانية كاملة تقريبًا أثناء عرض الإطار.

8. قياس الإطارات البطيئة/المجمدة للجزء

تلتقط مراقبة أداء Firebase تلقائيًا الإطارات البطيئة/المجمدة لأحد الأنشطة ( ولكن فقط إذا تم تسريعها بالأجهزة ). ومع ذلك، هذه الميزة غير متوفرة حاليًا للأجزاء. يتم تعريف الإطارات البطيئة/المجمدة للجزء على أنها الإطارات البطيئة/المجمدة للنشاط بأكمله بين ردود الاتصال onFragmentAttached() و onFragmentDetached() في دورة حياة الجزء.

بدافع من فئة AppStateMonitor ( التي تعد جزءًا من SDK لمراقبة الأداء والمسؤولة عن تسجيل آثار الشاشة للنشاط )، قمنا بتطبيق فئة ScreenTrace ( التي تعد جزءًا من مستودع كود مصدر Codelab هذا ). يمكن ربط فئة ScreenTrace باستدعاء دورة حياة FragmentManager الخاص بالنشاط لالتقاط الإطارات البطيئة/المجمدة. توفر هذه الفئة واجهتي برمجة تطبيقات عامة:

  • recordScreenTrace() : يبدأ تسجيل تتبع الشاشة
  • sendScreenTrace() : إيقاف تسجيل تتبع الشاشة وإرفاق مقاييس مخصصة لتسجيل إجمالي عدد الإطارات، والبطيء، والمجمد

من خلال ربط هذه المقاييس المخصصة، يمكن التعامل مع آثار الشاشة للأجزاء بنفس طريقة التعامل مع آثار الشاشة للنشاط ويمكن عرضها مع آثار عرض الشاشة الأخرى في لوحة معلومات الأداء بوحدة تحكم Firebase.

فيما يلي كيفية تسجيل آثار الشاشة للجزء الخاص بك:

  1. قم بتهيئة فئة ScreenTrace في نشاطك الذي يستضيف الجزء.

MainActivity.java

// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();

// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;

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

    // TODO (2): Initialize the ScreenTrace variable.
    screenTrace = new ScreenTrace(this, FRAGMENT_TAG);

    // ...
}
  1. عندما تقوم بتحميل الجزء الخاص بك، قم بالتسجيل في FragmentLifecycleCallbacks وتجاوز عمليات الاسترجاعات onFragmentAttached() و onFragmentDetached() . لقد فعلنا هذا من أجلك. تحتاج إلى بدء تسجيل آثار الشاشة في رد الاتصال onFragmentAttached() وإيقاف التسجيل في رد الاتصال onFragmentDetached() .

MainActivity.java

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
       new FragmentManager.FragmentLifecycleCallbacks() {

           @Override
           public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
               super.onFragmentAttached(fm, f, context);

               // TODO (3): Start recording the screen traces as soon as the Fragment is
               //  attached to its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.recordScreenTrace();
               }
           }

           @Override
           public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
               super.onFragmentDetached(fm, f);

               // TODO (4): Stop recording the screen traces as soon as the Fragment is
               //  detached from its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.sendScreenTrace();
               }

               // Unregister Fragment lifecycle callbacks after the Fragment is detached
               fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
           }
       };
  1. أعد تشغيل التطبيق. ثم اضغط على زر LOAD FRAGMENT . انتظر لبضع ثوان، ثم انقر فوق back button الموجود في شريط التنقل السفلي.

قم بتصفية السجل باستخدام " قياس تتبع التسجيل "، ثم ابحث عن السجلات كما هو موضح أدناه:

I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)

قم بتصفية السجل باستخدام " FireperfViews "، ثم ابحث عن السجلات كما هو موضح أدناه:

D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX

🎉 تهانينا! لقد نجحت في قياس الإطارات البطيئة/المجمدة لأحد الأجزاء وأبلغت عن هذه البيانات إلى مراقبة أداء Firebase. سنعرض المقاييس المسجلة في وحدة تحكم Firebase لاحقًا في هذا الدرس التطبيقي حول الترميز.

9. تحقق من المقاييس في وحدة تحكم Firebase

  1. في السجل، انقر فوق عنوان URL لوحدة تحكم Firebase لزيارة صفحة التفاصيل للتتبع. ceb9d5ba51bb6e89.jpeg

وبدلاً من ذلك، في وحدة تحكم Firebase ، حدد المشروع الذي يحتوي على تطبيقك. في اللوحة اليمنى، حدد موقع قسم الإصدار والمراقبة ، ثم انقر فوق الأداء .

  • في علامة التبويب لوحة المعلومات الرئيسية، قم بالتمرير لأسفل إلى جدول التتبعات، ثم انقر فوق علامة التبويب آثار مخصصة . في هذا الجدول، ستشاهد آثار التعليمات البرمجية المخصصة التي أضفناها سابقًا بالإضافة إلى بعض عمليات التتبع الجاهزة ، مثل _app_start Trace.
  • ابحث عن تتبعي التعليمات البرمجية المخصصة لديك، TestActivity-LoadTime و TestFragment-LoadTime . انقر على المدة لأي منهما لعرض المزيد من التفاصيل حول البيانات التي تم جمعها.

a0d8455c5269a590.png

  1. تعرض لك صفحة التفاصيل الخاصة بتتبع التعليمات البرمجية المخصصة معلومات حول مدة التتبع (أي وقت التحميل المُقاس).

5e92a307b7410d8b.png

  1. يمكنك أيضًا عرض بيانات الأداء لتتبع الشاشة المخصص.
  • ارجع إلى علامة التبويب "لوحة المعلومات" الرئيسية، ثم قم بالتمرير لأسفل إلى جدول التتبعات، ثم انقر فوق علامة التبويب "عرض الشاشة" . في هذا الجدول، سترى آثار الشاشة المخصصة التي أضفناها سابقًا بالإضافة إلى أي آثار شاشة جاهزة ، مثل تتبع MainActivity .
  • ابحث عن تتبع الشاشة المخصص، MainActivity-TestFragment . انقر فوق اسم التتبع لعرض البيانات المجمعة للعرض البطيء والإطارات المجمدة.

ee7890c7e2c28740.png

10. تهانينا

تهانينا! لقد نجحت في قياس وقت التحميل وأداء عرض الشاشة لنشاط وجزء باستخدام مراقبة أداء Firebase!

ما أنجزته

ماذا بعد

يوفر أداء Firebase المزيد من الطرق لقياس أداء تطبيقك بخلاف التتبع المخصص. فهو يقيس تلقائيًا وقت بدء تشغيل التطبيق وبيانات أداء التطبيق في المقدمة والتطبيق في الخلفية . لقد حان الوقت للتحقق من هذه المقاييس في Firebase Console .

كما يوفر Firebase Performance مراقبة تلقائية لطلبات شبكة HTTP/S . وبهذا يمكنك بسهولة إدارة طلبات الشبكة دون كتابة سطر واحد من التعليمات البرمجية. هل يمكنك محاولة إرسال بعض طلبات الشبكة من تطبيقك والعثور على المقاييس في وحدة تحكم Firebase ؟

علاوة

الآن بعد أن عرفت كيفية قياس وقت التحميل وأداء عرض الشاشة لنشاطك/جزءك باستخدام آثار تعليمات برمجية مخصصة، هل يمكنك استكشاف قاعدة التعليمات البرمجية مفتوحة المصدر لدينا لمعرفة ما إذا كان بإمكانك التقاط هذه المقاييس خارج الصندوق لأي نشاط/جزء هذا هو جزء من التطبيق؟ لا تتردد في إرسال العلاقات العامة إذا كنت ترغب في ذلك :-)

11. التعلم الإضافي

سيساعدك فهم ما يحدث أثناء تحميل النشاط على فهم خصائص أداء تطبيقك بشكل أفضل. في خطوة سابقة، وصفنا على مستوى عالٍ ما يحدث أثناء تحميل النشاط، لكن الرسم البياني التالي يصف كل مرحلة بتفاصيل أعلى بكثير.

cd61c1495fad7961.png