مقدمة
يناقش هذا المقال كيفية إخبار شخص لم يكن مطلعًا على برمجة Android بشأن مكوناته الرئيسية. الاهتمام وإظهار أن كل شيء ليس صعباً كما يعتقد الكثير من الناس. في نفس الوقت ، قم بذلك في 15 دقيقة ودون الخوض في شرح لبعض النظريات الأساسية التي يمكن للجميع قراءتها بنفسه والعودة مع توضيح الأسئلة.
عندما حاولت القيام بذلك للمرة الأولى ، فوجئت بنفسي بصورة غير سارة. تحول توضيحي "البسيط والمفهوم" إلى ممل ، وفي إطاره تم تتبع محاولة يائسة لفهم الضخامة وإخبار القليل باختصار. وغني عن القول ، إن هذه القصة لن تهم على الأرجح ، ولكنها تخيف المحاور الخاص بك ، مع تقليل الرغبة في القيام بشيء خاص بك ، حتى لو كان لديك سابقًا آلة حاسبة صغيرة في خططك.
ليس سراً أن عددًا كبيرًا من المقالات حول هذا الموضوع يتم نشرها على الإنترنت ، ولكن في حالتي ، سيكون السرد مختلفًا قليلاً: لن يكون هناك سوى ممارسة بصرية ، دون تعريفات وتفاصيل أخرى. هذا هو ، ونحن ننظر - نرى - نحن نعلق على ما يحدث. يبدو ، في رأيي ، أن كل شيء بسيط للغاية وواضح ، وأجزاء الكود هي أيضًا صغيرة وبسيطة جدًا ، وجاهزة للاستخدام السريع في مشروعك. يبدو لي أن هذا النهج يقدم نظرة عامة واسعة إلى حد ما على أدوات Android الكلاسيكية ، وعند كتابة التطبيق الأول ، بدلاً من الأسئلة "ما الذي يجب أن أستخدمه" ، سيكون هناك المزيد من الأسئلة المحددة "كيف يجب أن أستخدم المكون X بالضبط". وبالفعل ستكون كل التفاصيل حول هذا الشخص قادرة على اكتشاف نفسه - إذا أراد.
لذلك دعونا نذهب!
تعلم المكونات
قم بتثبيت التطبيق ، وإطلاقه ، و ... في الوقت الحالي ، يكفي فتح MainActivity أمامنا. سيتم الرد على السؤال "لماذا هو بالضبط" في وقت لاحق.
أولاً وقبل كل شيء ، دعنا نفكر في مصدره - من main_activity.xml ، حيث يتم الإعلان عن جميع عناصر الواجهة. تم نشرها في LinearLayout ، لذلك من غير المرجح أن تنشأ الأسئلة هنا.
عرض الكود<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Android Demo Application" /> <TextView android:id="@+id/textView3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" 1" /> <Button android:id="@+id/buttonShowToast" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" Toast" /> ... </LinearLayout>
مكونات بسيطة
خبز محمص
الآن دعنا نذهب إلى MainActivity.java والزر الأول من واجهته - "Show Toast" (إشعار منبثق).
ابحث عن معرف الزر في main_activity.xml وانتقل إلى OnClickListener في MainActivity.java.
عرض الكود Button btn = findViewById(R.id.buttonShowToast); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "This is a Toast", Toast.LENGTH_LONG).show(); } });
اتضح أنه لعرض إخطار منبثق ، يكفي سطر واحد من الكود! عظيم ، أليس كذلك؟
التفاعل مع نشاط آخر
عرض الصورةالنشاط الأول

النشاط الثاني

الآن دعونا نحاول الذهاب إلى مكان ما خارج الصفحة الرئيسية للتطبيق. على سبيل المثال ، إلى صفحة أخرى من هذا القبيل! انتقل إلى "التفاعل مع نشاط آخر" - ونحن ندخل في نشاط آخر باستخدام عناصر تحكم أخرى. كيف تقوم الأنشطة المختلفة في تطبيق واحد بنقل البيانات فيما بينها؟ هذا هو الوقت المناسب للحديث عن آليتين مختلفتين: تخزين ثابت للقيم - Shared_prefs ، وكذلك startActivityForResult / onActivityResult (لم أكن أريد إدراج هذا في البداية ، ولكن لا يزال بإيجاز: إذا قمت ببدء نشاط جديد من نشاط مفتوح باستخدام startActivityForResult ، سيتم استدعاء onActivityResult في النشاط الأول . لا تشعر بالقلق إذا كان هذا غير واضح حتى الآن).
وبالطبع ، تثبت في الممارسة!
الدخول في share_prefs:
عرض الكود String valueToSave = "test"; SharedPreferences.Editor editor = getSharedPreferences("demoapp", MODE_PRIVATE).edit(); editor.putString("myValue", valueToSave); editor.apply();
القراءة من Shared_prefs:
عرض الكود SharedPreferences prefs = getSharedPreferences("demoapp", MODE_PRIVATE); String storedValue = prefs.getString("myValue", "NOT_FOUND");
مثال على onActivityResult - انظر في مصدر التطبيق.
حفظ واستعادة الإعدادات
بما أننا ذكرنا Shared_prefs ، فلننتهي معهم. نذهب إلى "حفظ واستعادة الإعدادات" ، حيث يتم تزويدنا ببطاقة نموذجية لحساب نموذجي مع أنواع مختلفة من الحقول (لاحظ أن النوع الخاص بهم تم تعيينه بواسطة متغير واحد فقط - رمز التبديل). سنقوم بحفظ محتويات هذه الحقول في Shared_prefs ، ثم استعادتها. حتى الآن ، زر منفصل ، لا تستأنف - لم نصل إليهم بعد!
توفير:
عرض الكود SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit(); EditText et = findViewById(R.id.editTextName); String name = et.getText().toString(); editor.putString("name", name); editor.apply();
نستعيد:
عرض الكود SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE); EditText et = findViewById(R.id.editTextName); et.setText(prefs.getString("name", "")); ...
قائمة بسيطة
القسم التالي هو قائمة بسيطة. نتعلم منه أنه لا يوجد شيء معقد في ذلك - فقط قم بتعيين onCreateOptionsMenu واملأه بالبنية من my_menu.xml.
عرض الكود @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.my_menu, menu); return true; }
القائمة المنبثقة
بعد ذلك مباشرة - زر القائمة المنبثقة. هو بالفعل أكثر إثارة للاهتمام من السابق حتى مع وجود ذاكرة الإعدادات ووجود قوائم فرعية.
يمكن عرض رمز القائمة المنبثقة في مصدر التطبيق.
مشغل الصوت
باستخدام مثال "مشغل الصوت" ، بالإضافة إلى حقيقة أنه لا يوجد شيء معقد في تشغيل الموسيقى ، يمكنك إثبات ارتباط searchBar بشيء ما ، في هذه الحالة ، إلى الموضع الحالي لمشغل الوسائط. في التحكم في مستوى الصوت - أيضا لا شيء خارق للطبيعة. مكافأة يظهر خطأ. افتح النشاط باستخدام مشغل الوسائط ، وابدأ التشغيل واضغط على زر "رجوع". تستمر الموسيقى في اللعب ، ولم يعد بالإمكان إيقافها ... المشكلة! كيفية حلها - سنكتشفها لاحقًا.
يمكن العثور على رمز مشغل الصوت في مصدر التطبيق.
متصفح الويب
حسنًا ، في ختام الجزء الأول ، نظهر أنه لا يوجد شيء إلهي في متصفح الويب أيضًا.
عرض الكود WebView view = findViewById(R.id.webView); view.setWebViewClient(new WebViewClient()); view.getSettings().setJavaScriptEnabled(true); view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); view.loadUrl("https://google.com");
الجزء التالي أصعب بالفعل.
الخدمات والإشعارات
BroadcastReceiver
أذكر كيف نقلنا النتيجة من نشاط إلى آخر. على نحو ما (لم نعرف حتى الآن كيف) اتضح أنه عندما تم إغلاق النشاط الثاني ، انتقلت النتيجة إلى نشاط أول نتيجة. ولكن هل يمكننا نقل النتيجة في أي مكان داخل طلبنا؟ نعم ، يمكننا إعلان وتسجيل مستمع يسمعنا من أي مكان في البرنامج. هذا المستمع يسمى BroadcastReceiver.
عرض الكود public BroadcastReceiver MyReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(getApplicationContext(), "Broadcast receiver: received!", Toast.LENGTH_LONG).show(); } };
هنا لا يمكنك الاستغناء عن النوايا ، ولكن على المستوى الأكثر بدائية: في الوقت الحالي ، يتم إرسال حقيقة أنها كافية بالنسبة لنا إلى حافلة مشتركة معينة ، وأنه وفقًا لإجراء محدد مسبقًا ، سوف تسمعنا إذاعة BroadcastReceiver أينما كنا.
عرض الكود IntentFilter filter = new IntentFilter(); filter.addAction("MyCustomActionName"); registerReceiver(MyReceiver, filter); sendBroadcast(new Intent("MyCustomActionName"));
في الوقت الحالي ، لدينا فكرة أساسية عن ماهية جهاز الاستقبال ، ولكن سبب الحاجة إليه غير واضح: هذا أمر طبيعي ، والآن أصبح الأمر أسهل.
خدمة بسيطة
انتقل بسلاسة إلى الخدمة ، حيث ستجد BroadcastReceiver تطبيقها الكامل. وبدلاً من محاولة معرفة باختصار ماهية خدمة Android ، أقترح بدء العرض التوضيحي على الفور. قم بتشغيل الخدمة ، والتي تبدأ عملها في الخلفية. جنبا إلى جنب مع هذا ، سنقوم بتسجيل اثنين من أجهزة الاستقبال: واحد في النشاط والآخر في الخدمة.
مطلوب المتلقي في الخدمة لإثبات أنه على الرغم من تنفيذ الخلفية ، يمكننا دائمًا الوصول إليه من النشاط.
عرض الكود public BroadcastReceiver MyServiceReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(getApplicationContext(), "Toast from Service: I hear you!", Toast.LENGTH_LONG).show(); } };
مطلوب المتلقي في النشاط لعرض نتائج الخدمة في TextView.
عرض الكود public BroadcastReceiver MyPrintReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(getApplicationContext(), "pong", Toast.LENGTH_SHORT).show(); TextView tv = findViewById(R.id.textViewSimpleServiceStatus); String msg = intent.getStringExtra("msg"); tv.setText(msg); } };
وفهمًا تامًا ، عند إرسال رسالة من الخدمة إلى Activity ، سنقوم بعرض Toast ("ping"). وعندما نتلقى الرسالة في النشاط ونرسم القيمة في TextView فعليًا ، سنقوم بعرض Toast ("pong").
دع المهمة الرئيسية للخدمة هي إرسال "الأصوات" إلى النشاط ، ومهمة النشاط هي عرضها ببساطة في واجهتها.
عرض الكود Handler handler = new Handler(); runnable = new Runnable() { public void run() { if (running) { printMsg("Service is still running " + running); handler.postDelayed(runnable, 5000); // } else { printMsg("Service exited"); } } }; handler.postDelayed(runnable, 5000);
سيتم النظر في مثال مفصل لعملية المعالجة في وقت لاحق ، في الوقت الحالي ، هي فقط وسيلة لإرسال ping كل 5 ثوانٍ.
والآن ، بعد بدء الخدمة ، نرى Toast ("تم إنشاء الخدمة!") وإرسال إخطارات ping-pong. وفي TextView بدأت الرسائل من الخدمة في الوصول.
عظيم ، تنفيذ الخلفية رأينا. أغلق الآن التطبيق! على أحدث إصدارات Android ، سنرى ما يلي: إعادة تشغيل الخدمة (ستظهر Toast ("تم إنشاء الخدمة!")) وإرسال "الأصوات". في الوقت نفسه ، لا يوجد "بونغس" - بعد كل شيء ، لا يوجد أي نشاط يعالجهم! بعد بضع ثوانٍ ، توقفت الأصوات أيضًا على هاتفي الذكي. يتم تدمير الخدمة. افتح إعدادات الطاقة ، وقم بإيقاف تشغيل التحسين لتطبيقنا وقم بإجراء الإجراء مرة أخرى. الآن لم يتم تدمير الخدمة ، وحتى بعد إغلاق البرنامج ، نرى "الأصوات" الواردة بشكل ثابت. لكن ، بالطبع ، لا يوجد أي ضمان هنا ، ولن تستمر هذه الخدمة لفترة طويلة جدًا. من وجهة نظر المطور ، يمكن أن يكون هذا أمرًا فظيعًا ، ولكن دعونا ننظر إليه من خلال عيون مستخدم بسيط: هل نريد أن يعمل أي تطبيق في الخلفية بحرية وبدون عقاب ، تناول بطارية؟ بالكاد. كيف بعد ذلك للعمل بشكل كامل في الخلفية؟
للقيام بذلك ، ما عليك سوى إخطار المستخدم بهذا. هذا مطلب إلزامي لنظام Android ، والذي يتيح لك ، بوجود قناة إعلام ، ليس فقط تشغيل الخدمة المعتادة ، ولكن خدمة المقدمة ، التي ستعمل بشكل كامل في الخلفية دون التعرض لخطر الزوال.
إضافة إلى onCreate من خدمتنا:
عرض الكود String CHANNEL_ID = "my_channel_01"; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Channel human readable title", NotificationManager.IMPORTANCE_DEFAULT); ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("") .setContentText("").build(); startForeground(1, notification);
وسوف نقوم بتشغيله باستخدام الأمر:
عرض الكود startForegroundService(new Intent(getApplicationContext(), TestServiceForeground.class));
سنعود إلى تطبيقنا التجريبي ، ونعيد إعدادات توفير الطاقة إلى حالتها الأصلية. والآن سنطلق خدمة المقدمة. عند إغلاق التطبيق عند تشغيل الخدمة ، نلاحظ أن الخدمة لم تعد قيد التشغيل ولا يتم إتلافها ، ولكن ببساطة تواصل بثبات العمل في الخلفية ، وإرسال "الأصوات" كل 5 ثوانٍ. في الوقت نفسه ، سيتم تعليق رمز الإعلام في الإشعارات.
سيكون المستخدم قادرًا على إخفاء هذه الإشعارات إذا أراد ، لكن لا يمكنك إخفاؤها برمجيًا في وقت إنشاء قناة الإعلام.
زر عائم (تراكب)
بمعرفة ماهية الخدمة ، يمكنك الانتقال إلى الاستخدام الأبسط والأكثر وضوحًا - العمل باستخدام الأزرار العائمة. على Androids الحاليين ، لا يمكنك فقط إعلان حقوق تقديم Overlays تمامًا مثل ذلك - تحتاج إلى أن تطلب من المستخدم صراحةً الحصول على إذن "فوق كل النوافذ". ثم نقر فوق رسم تراكب ونرى أيقونة عائمة ، يوجد خلفها بالفعل خدمة تستمع للنقرات عليها.
إرسال الإخطارات
ميزة أخرى مهمة من المرجح أن تكون مفيدة في معظم تطبيقات Android هي إرسال إشعارات إلى المستخدم. دعونا نلقي نظرة سريعة على كيفية عمل هذا. لتبدأ (على Androids الحاليين) ، نحتاج إلى إنشاء قناة إعلام - نعم ، نعم ، واحدة من تلك التي تتوفر عادة بأعداد كبيرة في التطبيقات الحديثة.
عرض الكود private void createNotificationChannel() { try { CharSequence channelName = CHANNEL_ID; String channelDesc = "channelDesc"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int importance = NotificationManager.IMPORTANCE_LOW; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, channelName, importance); channel.setDescription(channelDesc); NotificationManager notificationManager = getSystemService(NotificationManager.class); assert notificationManager != null; NotificationChannel currChannel = notificationManager.getNotificationChannel(CHANNEL_ID); if (currChannel == null) { notificationManager.createNotificationChannel(channel); Toast.makeText(getApplicationContext(), "channel created", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "channel exists", Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { } }
حسنًا ، يمكنك إرسال إعلامات إليه دون قيود.
عرض الكود public void setNotify() { try { Intent snoozeIntent = new Intent("ActionFromNotify"); PendingIntent snoozePendingIntent = PendingIntent.getBroadcast(this, 0, snoozeIntent, 0); String title = "Start"; Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_launcher_background, title, snoozePendingIntent).build(); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_launcher_background) .setContentTitle("MyNotification") .setContentText("Text here") .setPriority(NotificationCompat.PRIORITY_LOW) .setContentIntent(null) .setOngoing(true) // .setSound(null) .addAction(action); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); int notificationId = (int) (System.currentTimeMillis() / 4); notificationManager.notify(notificationId, mBuilder.build()); } catch (Exception e) { Log.e("error", e.toString()); } }
الخدمة مع إرسال الإخطارات
إرسال الإشعارات مباشرةً من النشاط ، كما ترى ، ليس مؤثرًا للغاية. ما زلنا معتادون على شيء آخر. ولكن ماذا نعرف عن تنفيذ الخلفية حتى الآن؟ فقط أن هناك خدمات. لذلك دعونا لا نتسرع ونقدم خدمة بسيطة (ولا حتى المقدمة - للبساطة) التي ترسل إشعارًا جديدًا كل 5 ثوانٍ. موافق ، هذا أجمل بالفعل وأكثر إثارة للإعجاب من مجرد إرسال الإشعارات عن طريق زر.
كإيقاف مؤقت لفترة قصيرة بعد الخدمات المعقدة على ما يبدو (إذا لم تقابلها من قبل) ، سننظر في أربعة عناصر تحكم يسهل فهمها. ثم نعود مرة أخرى إلى المواد المعقدة - إلى التدفقات.
مكونات إضافية
جدول البيانات
لنبدأ توقفنا مع جدول البيانات. دراسة الكود المصدري متروك للقارئ.
نافذة كلفه
المحطة التالية هي نافذة مبوبة. بمساعدة TabView بسيط ، يمكنك وضع العديد من الأنشطة على شاشة واحدة.
عرض الكود public class TabsActivity extends TabActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tabs_activity); // TabHost TabHost tabHost = getTabHost(); TabHost.TabSpec tabSpec; tabSpec = tabHost.newTabSpec("tag1"); tabSpec.setIndicator(""); tabSpec.setContent(new Intent(this, SaveRestorePrefsActivity.class)); tabHost.addTab(tabSpec); tabSpec = tabHost.newTabSpec("tag2"); tabSpec.setIndicator(""); tabSpec.setContent(new Intent(this, FloatingMenuActivity.class)); tabHost.addTab(tabSpec); ... } }
عرض كائنات الهيكل: جزء وجدول
يمكن أن يتم إخراج كائنات الهيكل باستخدام الأجزاء والجدول.
هذه هي الطريقة التي يشبه ملء مع شظايا
وهكذا الجدول
دورة حياة النشاط
إنهاء تدريجيا توقفنا ، ننتقل إلى دورة حياة النشاط. هذا هو الوقت المناسب لمعرفة أن هناك عددًا من الطرق الأخرى بجانب onCreate. في البداية ، تساعد قطعة صغيرة من التعليمات البرمجية والإعلامات المنبثقة في فهمها بشكل أفضل من أي تفسير.
عرض الكود @Override protected void onResume() { super.onResume(); Toast.makeText(getApplicationContext(), "onResume - ", Toast.LENGTH_SHORT).show(); } @Override protected void onDestroy() { super.onDestroy(); Toast.makeText(getApplicationContext(), "onDestroy - ", Toast.LENGTH_SHORT).show(); } @Override protected void onPause() { super.onPause(); Toast.makeText(getApplicationContext(), "onPause - ", Toast.LENGTH_SHORT).show(); }
الأمر نفسه ينطبق على OnTouchListener'ov و onTextChanged.
عرض الكود CheckBox cb = findViewById(R.id.checkBoxChangeExample); cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { printMsg("CheckBox - OnCheckedChangeListener: new value is checked = " + isChecked); } }); SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarChangeExample); seekBar.setMax(100); seekBar.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Integer progress = ((SeekBar)v).getProgress(); printMsg("SeekBar - OnTouchListener: new value is = " + progress.toString()); return false; } });
تأخر التنفيذ المتوازي والمنتظم
ننتقل إلى الجزء الأكثر صعوبة في القصة - التنفيذ المؤجل والمتوازي.
تأخر التنفيذ: معالج
لنبدأ الغوص مع معالج. ما هو المعالج في كل التفاصيل - سيقرأه الشخص لاحقًا بنفسه ، ولن نحرمه من هذه المتعة. ولكن نظرًا لأننا ندرسها ، من المهم معرفة الشيء الرئيسي - وهو أنه يسمح لك بأداء مهمة معلقة ، ولا تقوم بذلك بشكل متوازٍ.
تبين ذلك. دعنا ننشئ معالجًا ، أضف المهمة "لإخراج توست في 5 ثوانٍ". نرى أن Toast قد تم سحبه ولم تكن هناك حاجة للنوم (توقف مؤقت في تنفيذ البرنامج بأكمله).
عرض الكود Handler handler = new Handler(); Runnable r = new Runnable() { public void run() { Toast.makeText(getApplicationContext(), "Delayed task executed", Toast.LENGTH_SHORT).show(); } }; handler.postDelayed(r, 5000);
أضف الآن مهمة دورية إلى المعالج:
عرض الكود Runnable r = new Runnable() { public void run() { Toast.makeText(getApplicationContext(), "Delayed task executed", Toast.LENGTH_SHORT).show(); handler.postDelayed(this, 5000); // } }; handler.postDelayed(r, 5000);
بعد التأكد من تشغيله كل 5 ثوانٍ ، امسح المعالج
عرض الكود handler.removeCallbacksAndMessages(null);
يبقى أن نثبت أن تنفيذ المهام من معالج لا يحدث بالتوازي. أسهل طريقة لإظهار ذلك هي تحميله بشيء ثقيل وفي نفس الوقت بسيط. مثل ... بينما (صحيح) دون نوم! عشر ثوان ، حتى لا تقتل التطبيق بالكامل.
عرض الكود Runnable r = new Runnable() { public void run() { long initTime = System.currentTimeMillis(); boolean timeElapsed = false; while(!timeElapsed){ if(System.currentTimeMillis() - initTime > 10000 ){ timeElapsed = true; // , ! ( , ). sleep } } } }; Toast.makeText(getApplicationContext(), "Hard Delayed task started", Toast.LENGTH_SHORT).show(); handler.postDelayed(r, 100);
عند بدء هذه المهمة ، نرى أن التطبيق لا يستجيب لنقراتنا خلال هذه الثواني العشر - إنه مشغول تمامًا وتامًا في معالجة دورتنا المعقدة. الاستنتاج الثاني الذي يلي من هذا المثال هو أنه لا يمكنك تشغيل مهام كثيفة الاستخدام للموارد في نفس مؤشر الترابط مع الواجهة. يجب أن يكون مؤشر ترابط واجهة المستخدم مجانيًا دائمًا ، ويجب أن يتم تشغيل وظائفه في أسرع وقت ممكن. بعض العمليات في دفق واجهة المستخدم محظورة بشكل صريح: على سبيل المثال ، سوف يتعطل Android التطبيق إذا كان يحاول الوصول إلى الإنترنت في دفق واجهة المستخدم.
التنفيذ المتزامن: تيار
السؤال المنطقي الآن هو كيفية إنشاء مؤشرات ترابط جديدة والعمل بشكل متوازٍ؟
نعرض كلاً من إنشاء الدفق وحقيقة تشغيله الموازي. لنقم بإنشاء دفق وتحميله بنفس المهمة ، وبسبب ذلك ، في حالة المتسكعون ، تركنا بواجهة خاملة لمدة 10 ثوانٍ.
عرض الكود Thread thread = new Thread() { @Override public void run() { try { sendMsgUsingBroadcast("Thread started"); long initTime = System.currentTimeMillis(); boolean timeElapsed = false; while(!timeElapsed){ if(System.currentTimeMillis() - initTime > 10000 ){ timeElapsed = true; } } sendMsgUsingBroadcast("Thread stopped"); } catch (Exception e) { sendMsgUsingBroadcast("Thread error " + e.toString()); } } }; thread.start();
تم الإنشاء ، التحميل - وتعمل الواجهة! تنفيذ مؤشر الترابط يحدث بالتوازي.
السؤال التالي هو كيفية التحكم في التدفق. من المهم أن نفهم أن سلسلة الرسائل لن تكتمل إلا عند تنفيذ كل شفرة المصدر. لا يمكنك أن تأخذ وتدفق فقط. حان الوقت للتفكير في Shared_prefs وتطبيق المتغير للمزامنة: دع المتغير = true يتم تعيين المتغير مع بداية الدفق. في كل واحدة من التكرارات الخاصة به ، يتحقق مؤشر الترابط من تشغيل == صحيح ، وإذا لم يكن كذلك ، فإنه يكمل تنفيذه.
عرض الكود Thread thread = new Thread() { @Override public void run() { try { sendMsgUsingBroadcast("Thread started"); SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE); while (true) { if (!prefs.getBoolean("running", false)) { break; } else { try { Thread.sleep(100); } catch (Exception e) {} } } sendMsgUsingBroadcast("Thread stopped"); } catch (Exception e) { sendMsgUsingBroadcast("Thread error " + e.toString()); } } }; thread.start();
لذا ، فقد انتقلنا إلى بضع خطوات أخرى ، والآن لا نعرف فقط عن الخدمة ، ولكن أيضًا عن Handler والدفق. يبدو أن هذه الأدوات كافية لبدء كتابة تطبيق يحتوي على إحدى وظائف إعلام المستخدم العادي. لكن لديهم ميزة واحدة: جميعهم متحدون من خلال حقيقة أننا لا نفقد السيطرة لفترة ثانية ، ونضطر دائمًا لأن نكون في دورة لا نهائية تنام معظم الوقت وتستيقظ أحيانًا للتحقق مما إذا تم استيفاء بعض الشروط من أجل فهم - لاستنتاج إشعار المستخدم أو الذهاب إلى النوم مرة أخرى لفترة طويلة. يضيف هذا صداعًا: ماذا لو قتلت خدمتنا أو تطبيقنا مُحسِّن البطارية ، ولماذا نستهلك الكثير من موارد الهاتف الذكي من أجل صافرة صغيرة غير ضرورية؟ , , , - , ?
: AlarmManager
AlarmManager.
, — sendBroadcast Action, BroadcastReceiver!
BroadcastReceiver, , , :
public class MyAlarmServiceReceiver extends BroadcastReceiver { private String CHANNEL_ID = "MyNotificationsChannel"; @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "MyAlarmServiceReceiver onReceive", Toast.LENGTH_SHORT).show(); setNotify("Notify from AlarmManager", context); } }
AlarmManager Receiver 15 :
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); Intent intent = new Intent(AlarmActivity.this, MyAlarmServiceReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 1); calendar.set(Calendar.MINUTE, 10); //alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, 1 * 60 * 1000, pendingIntent); // not repeating - just one run, if needed alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
: 15 .
( ) .
Logcat Reader , .
( ) , .
String LOG_TAG = "MYDEMOAPP"; Log.i(LOG_TAG, "Test info log"); Log.d(LOG_TAG, "Test debug log"); Log.e(LOG_TAG, "Test error log");
, , MainActivity, , .
استنتاج
, Android. , , , : , . , , - Android-.