3zcs

Members
  • عدد منشوراتي

    9
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

8 Neutral

عن العضو 3zcs

  • الرتبه
    مبدع جديد

اخر الزوار

832 زياره للملف الشخصي
  1. بسم الله الرحمن الرحيم سنتحدث اليوم بإذن الله عن الـ Geofence, وهو من المواضيع الشيقة في التطوير لأندرويد, جميل جدا, لنبدأ. لنفترض جدلا أن لدينا محل تجاري يقدم عروض خاصة عن طريق التطبيق الخاص به, فقط لزبائنه الذين هم جلوس داخل المحل أو قريبين منه بمسافة 50 م, عن طريق إظهار notification مكتوب فيها خصم 10%, وعند الضغط على notification تتجه لصحفة لا يمكن الوصول إليها إلا عن طريق هذا notification, سيكون هذا المثال الذي نطبق عليه. بداية ما هو الـ Geofence, هو عبارة عن API يتيح لك معرفة ما إذا كان المستخدم قريب من أماكن تود أنت كمبرمج أن تقدم له تنبيه أو Action معين. قبل البدأ سنقوم بتجهير ملفي gradle و manifest. بالنسبة لملف gradle سنحتاج لأن نضيف google play services لتطبيق, عن طريق إضافة هذا السطر في dependencies - هذه المكتبة بأحدث إصدار وقت كتابة هذه المقالة - compile 'com.google.android.gms:play-services:10.0.1' أما في ملف manifest سنضيف رقم النسخة كـ meta-data, ورقم النسخة يضاف مباشرة في string.xml بعد عمل sync إذا تم تحديث gradle <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> ولا ننسى أن نطلب صلاحية الوصول لـ Location <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> الاّن التطبيق جاهز من ناحية المكتبات لنبدأ في كتابة الكود سيكون لدينا intent service تقوم بإضافة المواقع التي نرغب أن ننبه المستخدم إذا كان بمقربة منها, و service أخرى لإظهار notification سنبدأ بـ intent service ونسميها AddLocationService, أولا نقوم بتعريفها في ملف manifest <service android:name=".AddLocationService" /> نبدأ بعمل implements لثلاث Interfaces وهي ConnectionCallbacks وهي تطلب منك عمل override لـ onConnected OnConnectionFailedListener وتطلب من عمل override لـ onConnectionSuspended و onConnectionFailed ResultCallback<T> وتطلب من عمل override لـ onResult والتي تخبرك عن حالة الطلب الخاص بك من حيث النجاح أو الفشل ثم نحتاج لـ object من نوع GoogleApiClient حيث سيكون الاتصال عن طريقه ويقوم بشكل ذاتي بإدارة الإتصال, ونسند له API الذي نريد استخدامه ألا وهو LocationServices لنلقي نظرة على شكل الكود حتى هذه اللحظة public class AddLocationService extends IntentService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status>{ GoogleApiClient mGoogleApiClient; /** * Creates an IntentService. Invoked by your subclass's constructor. * * String AddLocationService important only for debugging. */ public AddLocationService() { super("AddLocationService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { mGoogleApiClient = new GoogleApiClient.Builder(this) .addOnConnectionFailedListener(this) .addConnectionCallbacks(this) .addApi(LocationServices.API) .build(); mGoogleApiClient.connect(); } @Override public void onConnected(@Nullable Bundle bundle) {} @Override public void onConnectionSuspended(int i) {} @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {} @Override public void onResult(@NonNull Status status) {} جميل جدا, في حالة كان هناك مشكلة في الاتصال أو تعطل الاتصال لفترة بسيطة سنضع log في onConnectionSuspended, onConnectionFailed كنوع من debug لمعرفة نوع الخطأ . أما في حالة onConnected سنبدأ في إنشاء geofence الخاص بنا كالتالي @Override public void onConnected(@Nullable Bundle bundle) { try { LocationServices.GeofencingApi.addGeofences( mGoogleApiClient, // The GeofenceRequest object. getGeofencingRequest(), getGeofencePendingIntent() ).setResultCallback(this); // Result processed in onResult(). } catch (SecurityException securityException) { // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission. Log.i(getClass().getSimpleName(),securityException.getMessage()); } } كما تلاحظ استدعينا دالة addGeofences التي نبني فيها Geofence الخاص بنا, وتستقبل ثلاث متغيرات وهي google api client وهو الذي قمنا ببناءه سلفاً, وقلنا أنه مسؤول عن إدارة الاتصال, و geofencingRequest سنتحدث عنها بعد قليل, وPendingIntent سنتحدث عنها لاحقا في هذه المقالة هي الأخرى أيضا. فيكون شكل الدالة كالتالي PendingResult<Status> addGeofences(GoogleApiClient var1, GeofencingRequest var2, PendingIntent var3) وسيرجع لنا status التي ستبين لنا حالة الـ request من حيث النجاح أو الفشل. نلاحظ أن لدينا two function وهي getGeofencingRequest و getGeofencePendingIntent لنبدأ بطريقة عمل GeofencingRequest وهي المسؤلة عن إضافة geofence list التي نريد متابعتها وإظهار التنبيه بشكل مناسب, وهي كالتالي private GeofencingRequest getGeofencingRequest() { GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL); builder.addGeofences(getGeofecne()); return builder.build(); } طبعا هو يستخدم builder pattern ونلاحظ أنه يطلب setInitialTrigger و addGeofences لدينا ثلاث أنوع من Triggers INITIAL_TRIGGER_DWELL : نستخدمه عندما يكون المستخدم داخل المنطقة التي حددناها لبعض الوقت, ويكون لدينا GEOFENCE_TRANSITION_DWELL. INITIAL_TRIGGER_ENTER : نستخدمه عندما يكون المستخدم داخل المنطقة التي حددناها, ويكون لدينا GEOFENCE_TRANSITION_ENTER INITIAL_TRIGGER_EXIT : نستخدمه عندما يكون المستخدم خارج المنطقة التي حددناها, ويكون لدينا GEOFENCE_TRANSITION_EXIT جميل جداً تعرفنا على أنواع Triggers التي قد نضعها في setInitialTrigger, وفي حالتنا سنستخدم INITIAL_TRIGGER_DWELL و INITIAL_TRIGGER_ENTER في الحقيقة نحن نريد GEOFENCE_TRANSITION_DWELL فقط لكن لاستخدامه تكون دائما مضطر لاستخدام INITIAL_TRIGGER_ENTER معه, لانه يمكننا من معرفه لحظة دخول الشخص إلى المنطقة المحدده والبدء بحساب الوقت الذي نقدره, قبل إظهار التنبيه. بالنسبة لـ addGeofences فهي list of geofence وهو من أهم الجزئيات في geofencing getGeofecne تبنى كالتالي private List<Geofence> getGeofecne(){ List<Geofence> mGeofenceList = new ArrayList<>(); //add one object mGeofenceList.add(new Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId("key") // Set the circular region of this geofence. .setCircularRegion( 25.768466, //lat 47.567625, //long 50) // radios // Set the expiration duration of the geofence. This geofence gets automatically // removed after this period of time. //1000 millis * 60 sec * 5 min .setExpirationDuration(1000 * 60 * 5) // Set the transition types of interest. Alerts are only generated for these // transition. We track entry and exit transitions in this sample. .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_DWELL) //Time before fire notification .setLoiteringDelay(3000) // Create the geofence. .build()); return mGeofenceList; } قمنا بإنشاء list of geofence وأضفنا Object واحد ولإنشاء object ستحتاج إلى عدة أمور Id نعرف به geofence ويكون من نوع string تحديد المحيط الخاص بالمنطقة عن طريق ثلاث أمور setCircularRegion( LATITUDE, LONGITUDE, RADIUS) تحديد الوقت الذي بعده يتم حذف geofence من list والوقت يحدد بالجزء من الثانية في setExpirationDuration(1000 * 60 * 5) حالة وضعنا NEVER_EXPIRE سيتم إشعار اليوزر في كل مرة حسب نوع الحالة التي نضعها, عند كل دخول, أو عند بقائه فترة محدد, أو عند خروجه. تحديد متى يتم تنبيه المستخدم, حال الدخول, أو الخروج أو عندما يكون داخل المنطقة لمدة من الزمن عن طريق setTransitionTypes() وتستخدم أحد هذه الثوابت أو جميعها GEOFENCE_TRANSITION_ENTER تحدثنا عنها في getGeofencingRequest أنها تستخدم مع INITIAL_TRIGGER_ENTER GEOFENCE_TRANSITION_DWELL فتستخدم مع INITIAL_TRIGGER_ENTER و INITIAL_TRIGGER_DWELL GEOFENCE_TRANSITION_EXIT تستخدم مع INITIAL_TRIGGER_EXIT لذلك يجب التنبه لوضع trigger في request مع transition الصحيح في geofecne. أضفت هنا object واحد لتبسيط المثال لكن بإمكانك إضافة الكثير وبطريقة dynmic أيضا الاّن أكلمنا GeofenceRequest لننتقل لـ PendingIntent private PendingIntent getGeofencePendingIntent() { // Reuse the PendingIntent if we already have it. if (mGeofencePendingIntent != null) { return mGeofencePendingIntent; } Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); return PendingIntent.getService(this, 0, intent, PendingIntent. FLAG_UPDATE_CURRENT); } قمنا ببناء pending intent بسيطة جدا الغرض منها إخراج notification للمستخدم, عن طريق استدعاء GeofenceTransitionsIntentService class وهو class الثاني الذي تحدثنا عنه في بداية المقالة جميل جدا public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override protected void onHandleIntent(Intent intent) { } الشكل الأولي لـ service, سنضيف logic الخاص بنا في onHandleIntnet, وهو قراءة البيانات القادمة في intent عن طريق دالة GeofencingEvent.fromIntent(intent) وسنحفظها في geofencingEvent ثم نتأكد من أنها سليمة عن طريق code التالي if (geofencingEvent.hasError()) { Log.e(TAG, getErrorString(geofencingEvent.getErrorCode())); return; } public String getErrorString(int errorCode) { switch (errorCode) { case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: return "not Available"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES: return "Too many Geofences"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS: return "Too many Pending Intents"; default: return "unknown geofence error"; } } لكل خطأ في geofence رقم معين وفي حال حدوثه سيظهر هنا في هذا log, فأحد الأخطاء مثلا, هو أن تضيف أكثر من 100 geofecne في geofence list, فسيظهر لك خطأ GEOFENCE_TOO_MANY_GEOFENCES, وبقية الأخطاء على نفس النسق بعد ذلك نتعرف على نوع Transition القادم والذي تم اسناده سابقا عن طريق دالة setTransitionTypes وعلى ضوءه نحدد notification التي نخرجها فيكون شكل class النهائي, كالتالي public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override protected void onHandleIntent(Intent intent) { GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); if (geofencingEvent.hasError()) { Log.e(TAG, getErrorString(geofencingEvent.getErrorCode())); return; } // Get the transition type. int geofenceTransition = geofencingEvent.getGeofenceTransition(); // Test that the reported transition was of interest. if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) { // Get the transition details as a String. String geofenceTransitionDetails = "Discount 10% for you"; // Send notification and log the transition details. sendNotification(geofenceTransitionDetails); Log.i(TAG, geofenceTransitionDetails); } else { // Log the error. Log.e(TAG, getString(R.string.geofence_transition_invalid_type + geofenceTransition)); } } public String getErrorString(int errorCode) { switch (errorCode) { case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: return "not Available"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES: return "Too many Geofences"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS: return "Too many Pending Intents"; default: return "unknown geofence error"; } } /** * Posts a notification in the notification bar when a transition is detected. * If the user clicks the notification, control goes to the MainActivity. */ private void sendNotification(String notificationDetails) { // Create an explicit content Intent that starts the main Activity. Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); // Construct a task stack. TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Add the main Activity to the task stack as the parent. stackBuilder.addParentStack(MainActivity.class); // Push the content Intent onto the stack. stackBuilder.addNextIntent(notificationIntent); // Get a PendingIntent containing the entire back stack. PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Get a notification builder that's compatible with platform versions >= 4 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // Define the notification settings. builder.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark_normal) // In a real app, you may want to use a library like Volley // to decode the Bitmap. .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.cast_abc_scrubber_primary_mtrl_alpha)) .setColor(Color.RED) .setContentTitle(notificationDetails) .setContentText(getString(R.string.geofence_transition_notification_text)) .setContentIntent(notificationPendingIntent); // Dismiss notification once the user touches it. builder.setAutoCancel(true); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } } وقد أضفت فيه جزئية إظهار notifiction ولن أتطرق لها بالشرح في هذه المقالة لكيلا تطول فيمل القارئ, في الـ Main Activity أضفت سطر واحد يشغل service @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService(new Intent(this,AddLocationService.class)); } طيب بكذا يكون تطبيقنا أكتمل وقابل لتجربة لكن بعطيك معلومة سريعة بما إننا استخدمنا IntentService, فهي راح تستقبل الأوامر عن طريق onHandleIntent حتى تنهيها وبعد كذا راح تسوي stop وبعدها destroy, طبعا قريب حلول كثيرة أشهرها استخدام broadcast receiver, اختبر نفسك وحاول تطبق geofence معاه, ورفعت هنا على github الكود الخاص بالمثال هذا مع broadcast receiver. تأكد من أن تكون قد قمت بإعطاء التطبيق الصلاحيات المطلوبة قبل عمل اختبار لتطبيق, عن طريق الإعدادات -> التطبيقات -> الصلاحيات هذا وصلى الله وسلم وبارك المراجع: https://developer.android.com/training/location/geofencing.html https://code.tutsplus.com/tutorials/how-to-work-with-geofences-on-android--cms-26639 Android programming big nerd ranch guide Android Application Development Cookbook واستفدت كثيرا من الأسئلة المطروحة في stack overflow
  2. بسم الله الرحمن الرحيم سنتحدث اليوم بإذن الله عن الـ Geofence, وهو من المواضيع الشيقة في التطوير لأندرويد, جميل جدا, لنبدأ. لنفترض جدلا أن لدينا محل تجاري يقدم عروض خاصة عن طريق التطبيق الخاص به, فقط لزبائنه الذين هم جلوس داخل المحل أو قريبين منه بمسافة 50 م, عن طريق إظهار notification مكتوب فيها خصم 10%, وعند الضغط على notification تتجه لصحفة لا يمكن الوصول إليها إلا عن طريق هذا notification, سيكون هذا المثال الذي نطبق عليه. بداية ما هو الـ Geofence, هو عبارة عن API يتيح لك معرفة ما إذا كان المستخدم قريب من أماكن تود أنت كمبرمج أن تقدم له تنبيه أو Action معين. قبل البدأ سنقوم بتجهير ملفي gradle و manifest. بالنسبة لملف gradle سنحتاج لأن نضيف google play services لتطبيق, عن طريق إضافة هذا السطر في dependencies - هذه المكتبة بأحدث إصدار وقت كتابة هذه المقالة - compile 'com.google.android.gms:play-services:10.0.1' أما في ملف manifest سنضيف رقم النسخة كـ meta-data, ورقم النسخة يضاف مباشرة في string.xml بعد عمل sync إذا تم تحديث gradle <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> ولا ننسى أن نطلب صلاحية الوصول لـ Location <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> الاّن التطبيق جاهز من ناحية المكتبات لنبدأ في كتابة الكود سيكون لدينا intent service تقوم بإضافة المواقع التي نرغب أن ننبه المستخدم إذا كان بمقربة منها, و service أخرى لإظهار notification سنبدأ بـ intent service ونسميها AddLocationService, أولا نقوم بتعريفها في ملف manifest <service android:name=".AddLocationService" /> نبدأ بعمل implements لثلاث Interfaces وهي ConnectionCallbacks وهي تطلب منك عمل override لـ onConnected OnConnectionFailedListener وتطلب من عمل override لـ onConnectionSuspended و onConnectionFailed ResultCallback<T> وتطلب من عمل override لـ onResult والتي تخبرك عن حالة الطلب الخاص بك من حيث النجاح أو الفشل ثم نحتاج لـ object من نوع GoogleApiClient حيث سيكون الاتصال عن طريقه ويقوم بشكل ذاتي بإدارة الإتصال, ونسند له API الذي نريد استخدامه ألا وهو LocationServices لنلقي نظرة على شكل الكود حتى هذه اللحظة public class AddLocationService extends IntentService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status>{ GoogleApiClient mGoogleApiClient; /** * Creates an IntentService. Invoked by your subclass's constructor. * * String AddLocationService important only for debugging. */ public AddLocationService() { super("AddLocationService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { mGoogleApiClient = new GoogleApiClient.Builder(this) .addOnConnectionFailedListener(this) .addConnectionCallbacks(this) .addApi(LocationServices.API) .build(); mGoogleApiClient.connect(); } @Override public void onConnected(@Nullable Bundle bundle) {} @Override public void onConnectionSuspended(int i) {} @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {} @Override public void onResult(@NonNull Status status) {} جميل جدا, في حالة كان هناك مشكلة في الاتصال أو تعطل الاتصال لفترة بسيطة سنضع log في onConnectionSuspended, onConnectionFailed كنوع من debug لمعرفة نوع الخطأ . أما في حالة onConnected سنبدأ في إنشاء geofence الخاص بنا كالتالي @Override public void onConnected(@Nullable Bundle bundle) { try { LocationServices.GeofencingApi.addGeofences( mGoogleApiClient, // The GeofenceRequest object. getGeofencingRequest(), getGeofencePendingIntent() ).setResultCallback(this); // Result processed in onResult(). } catch (SecurityException securityException) { // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission. Log.i(getClass().getSimpleName(),securityException.getMessage()); } } كما تلاحظ استدعينا دالة addGeofences التي نبني فيها Geofence الخاص بنا, وتستقبل ثلاث متغيرات وهي google api client وهو الذي قمنا ببناءه سلفاً, وقلنا أنه مسؤول عن إدارة الاتصال, و geofencingRequest سنتحدث عنها بعد قليل, وPendingIntent سنتحدث عنها لاحقا في هذه المقالة هي الأخرى أيضا. فيكون شكل الدالة كالتالي PendingResult<Status> addGeofences(GoogleApiClient var1, GeofencingRequest var2, PendingIntent var3) وسيرجع لنا status التي ستبين لنا حالة الـ request من حيث النجاح أو الفشل. نلاحظ أن لدينا two function وهي getGeofencingRequest و getGeofencePendingIntent لنبدأ بطريقة عمل GeofencingRequest وهي المسؤلة عن إضافة geofence list التي نريد متابعتها وإظهار التنبيه بشكل مناسب, وهي كالتالي private GeofencingRequest getGeofencingRequest() { GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL); builder.addGeofences(getGeofecne()); return builder.build(); } طبعا هو يستخدم builder pattern ونلاحظ أنه يطلب setInitialTrigger و addGeofences لدينا ثلاث أنوع من Triggers INITIAL_TRIGGER_DWELL : نستخدمه عندما يكون المستخدم داخل المنطقة التي حددناها لبعض الوقت, ويكون لدينا GEOFENCE_TRANSITION_DWELL. INITIAL_TRIGGER_ENTER : نستخدمه عندما يكون المستخدم داخل المنطقة التي حددناها, ويكون لدينا GEOFENCE_TRANSITION_ENTER INITIAL_TRIGGER_EXIT : نستخدمه عندما يكون المستخدم خارج المنطقة التي حددناها, ويكون لدينا GEOFENCE_TRANSITION_EXIT جميل جداً تعرفنا على أنواع Triggers التي قد نضعها في setInitialTrigger, وفي حالتنا سنستخدم INITIAL_TRIGGER_DWELL و INITIAL_TRIGGER_ENTER في الحقيقة نحن نريد GEOFENCE_TRANSITION_DWELL فقط لكن لاستخدامه تكون دائما مضطر لاستخدام INITIAL_TRIGGER_ENTER معه, لانه يمكننا من معرفه لحظة دخول الشخص إلى المنطقة المحدده والبدء بحساب الوقت الذي نقدره, قبل إظهار التنبيه. بالنسبة لـ addGeofences فهي list of geofence وهو من أهم الجزئيات في geofencing getGeofecne تبنى كالتالي private List<Geofence> getGeofecne(){ List<Geofence> mGeofenceList = new ArrayList<>(); //add one object mGeofenceList.add(new Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId("key") // Set the circular region of this geofence. .setCircularRegion( 25.768466, //lat 47.567625, //long 50) // radios // Set the expiration duration of the geofence. This geofence gets automatically // removed after this period of time. //1000 millis * 60 sec * 5 min .setExpirationDuration(1000 * 60 * 5) // Set the transition types of interest. Alerts are only generated for these // transition. We track entry and exit transitions in this sample. .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_DWELL) //Time before fire notification .setLoiteringDelay(3000) // Create the geofence. .build()); return mGeofenceList; } قمنا بإنشاء list of geofence وأضفنا Object واحد ولإنشاء object ستحتاج إلى عدة أمور Id نعرف به geofence ويكون من نوع string تحديد المحيط الخاص بالمنطقة عن طريق ثلاث أمور setCircularRegion( LATITUDE, LONGITUDE, RADIUS) تحديد الوقت الذي بعده يتم حذف geofence من list والوقت يحدد بالجزء من الثانية في setExpirationDuration(1000 * 60 * 5) حالة وضعنا NEVER_EXPIRE سيتم إشعار اليوزر في كل مرة حسب نوع الحالة التي نضعها, عند كل دخول, أو عند بقائه فترة محدد, أو عند خروجه. تحديد متى يتم تنبيه المستخدم, حال الدخول, أو الخروج أو عندما يكون داخل المنطقة لمدة من الزمن عن طريق setTransitionTypes() وتستخدم أحد هذه الثوابت أو جميعها GEOFENCE_TRANSITION_ENTER تحدثنا عنها في getGeofencingRequest أنها تستخدم مع INITIAL_TRIGGER_ENTER GEOFENCE_TRANSITION_DWELL فتستخدم مع INITIAL_TRIGGER_ENTER و INITIAL_TRIGGER_DWELL GEOFENCE_TRANSITION_EXIT تستخدم مع INITIAL_TRIGGER_EXIT لذلك يجب التنبه لوضع trigger في request مع transition الصحيح في geofecne. أضفت هنا object واحد لتبسيط المثال لكن بإمكانك إضافة الكثير وبطريقة dynmic أيضا الاّن أكلمنا GeofenceRequest لننتقل لـ PendingIntent private PendingIntent getGeofencePendingIntent() { // Reuse the PendingIntent if we already have it. if (mGeofencePendingIntent != null) { return mGeofencePendingIntent; } Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); return PendingIntent.getService(this, 0, intent, PendingIntent. FLAG_UPDATE_CURRENT); } قمنا ببناء pending intent بسيطة جدا الغرض منها إخراج notification للمستخدم, عن طريق استدعاء GeofenceTransitionsIntentService class وهو class الثاني الذي تحدثنا عنه في بداية المقالة جميل جدا public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override protected void onHandleIntent(Intent intent) { } الشكل الأولي لـ service, سنضيف logic الخاص بنا في onHandleIntnet, وهو قراءة البيانات القادمة في intent عن طريق دالة GeofencingEvent.fromIntent(intent) وسنحفظها في geofencingEvent ثم نتأكد من أنها سليمة عن طريق code التالي if (geofencingEvent.hasError()) { Log.e(TAG, getErrorString(geofencingEvent.getErrorCode())); return; } public String getErrorString(int errorCode) { switch (errorCode) { case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: return "not Available"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES: return "Too many Geofences"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS: return "Too many Pending Intents"; default: return "unknown geofence error"; } } لكل خطأ في geofence رقم معين وفي حال حدوثه سيظهر هنا في هذا log, فأحد الأخطاء مثلا, هو أن تضيف أكثر من 100 geofecne في geofence list, فسيظهر لك خطأ GEOFENCE_TOO_MANY_GEOFENCES, وبقية الأخطاء على نفس النسق بعد ذلك نتعرف على نوع Transition القادم والذي تم اسناده سابقا عن طريق دالة setTransitionTypes وعلى ضوءه نحدد notification التي نخرجها فيكون شكل class النهائي, كالتالي public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override protected void onHandleIntent(Intent intent) { GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); if (geofencingEvent.hasError()) { Log.e(TAG, getErrorString(geofencingEvent.getErrorCode())); return; } // Get the transition type. int geofenceTransition = geofencingEvent.getGeofenceTransition(); // Test that the reported transition was of interest. if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) { // Get the transition details as a String. String geofenceTransitionDetails = "Discount 10% for you"; // Send notification and log the transition details. sendNotification(geofenceTransitionDetails); Log.i(TAG, geofenceTransitionDetails); } else { // Log the error. Log.e(TAG, getString(R.string.geofence_transition_invalid_type + geofenceTransition)); } } public String getErrorString(int errorCode) { switch (errorCode) { case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: return "not Available"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES: return "Too many Geofences"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS: return "Too many Pending Intents"; default: return "unknown geofence error"; } } /** * Posts a notification in the notification bar when a transition is detected. * If the user clicks the notification, control goes to the MainActivity. */ private void sendNotification(String notificationDetails) { // Create an explicit content Intent that starts the main Activity. Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); // Construct a task stack. TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Add the main Activity to the task stack as the parent. stackBuilder.addParentStack(MainActivity.class); // Push the content Intent onto the stack. stackBuilder.addNextIntent(notificationIntent); // Get a PendingIntent containing the entire back stack. PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Get a notification builder that's compatible with platform versions >= 4 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // Define the notification settings. builder.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark_normal) // In a real app, you may want to use a library like Volley // to decode the Bitmap. .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.cast_abc_scrubber_primary_mtrl_alpha)) .setColor(Color.RED) .setContentTitle(notificationDetails) .setContentText(getString(R.string.geofence_transition_notification_text)) .setContentIntent(notificationPendingIntent); // Dismiss notification once the user touches it. builder.setAutoCancel(true); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } } وقد أضفت فيه جزئية إظهار notifiction ولن أتطرق لها بالشرح في هذه المقالة لكيلا تطول فيمل القارئ, في الـ Main Activity أضفت سطر واحد يشغل service @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService(new Intent(this,AddLocationService.class)); } طيب بكذا يكون تطبيقنا أكتمل وقابل لتجربة لكن بعطيك معلومة سريعة بما إننا استخدمنا IntentService, فهي راح تستقبل الأوامر عن طريق onHandleIntent حتى تنهيها وبعد كذا راح تسوي stop وبعدها destroy, طبعا قريب حلول كثيرة أشهرها استخدام broadcast receiver, اختبر نفسك وحاول تطبق geofence معاه, ورفعت هنا على github الكود الخاص بالمثال هذا مع broadcast receiver. تأكد من أن تكون قد قمت بإعطاء التطبيق الصلاحيات المطلوبة قبل عمل اختبار لتطبيق, عن طريق الإعدادات -> التطبيقات -> الصلاحيات هذا وصلى الله وسلم وبارك المراجع: https://developer.android.com/training/location/geofencing.html https://code.tutsplus.com/tutorials/how-to-work-with-geofences-on-android--cms-26639 Android programming big nerd ranch guide Android Application Development Cookbook واستفدت كثيرا من الأسئلة المطروحة في stack overflow تم ترقية هذا الطرح المميز الى صفحة المقالات
  3. بسم الله الرحمن الرحيم استكمالا للحديث عن أكثر Design pattern استخداما في بيئة Android, وبعد أن كنا قد بدأنا في الحديث عن أنواع Structural pattern, اليوم سيكون حديثنا عن Facade Pattern, فنبدأ بالسؤال المعتاد ماهو Facade Pattern؟ لنبدأ بتعريفه: إذا فهو Pattern يستخدم لتبسيط interfacees لأجزاء النظام, الصورة التالية ستكون شارحة جدا لوظيفة واّلية عمل هذا Pattern. جميل جدا, أعتقد أن المفهوم بات متضحاً جدا, فلندلف إلى تطبيقه بالمثال التالي. لتعلم أنه Pattern بسيط وواضح جدا, فلو فرضنا أن لدينا interface اسمه line, ولديه three claases قاموا بعمل implementation لهذا interface. وهم straight و curve و snaky. public interface Line { void draw(); } class straight public class Straight implements Line { @Override public void draw() { System.out.println("straight"); } } class curve public class Curve implements Line { @Override public void draw() { System.out.println("curve"); } } snaky class public class Snaky implements Line { @Override public void draw() { System.out.println("snaky"); } } أصبح لدينا مجموعة من classes, لو كان النظام الذي سنقوم ببنائه أكبر من ذلك سيكون من الصعب جدا تذكر هذه classes, لذلك سنقوم ببناء Facade class يحل هذه المشكلة ويبسط التعامل معها. public class LineMaker { private Line straight; private Line curve; private Line snaky; public LineMaker() { straight = new Straight(); curve = new Curve(); snaky = new Snaky(); } public void drawStraight(){ straight.draw(); } public void drawCurve(){ curve.draw(); } public void drawSnaky(){ snaky.draw(); } } أصبح التعامل هذه المجموعة من classes غاية في البساطة ويمكن التعامل معها في main كالتالي public static void main(String[] args) { LineMaker lineMaker = new LineMaker(); lineMaker.drawStraight(); lineMaker.drawCurve(); lineMaker.drawSnaky(); } } بينما لو لم نقم باستخدام facade class سيكون شكل main كالتالي: public static void main(String[] args) { private Line straight = new Straight(); private Line curve = new Curve(); private Line snaky = new Snaky(); straight.drawStraight(); curve.drawCurve(); snaky.drawSnaky(); } } طبعا هذا المثال يبسط المشكلة لكن في حال كان حجم النظام كبير ستواجه مشكلة في التعامل مع هذه الكمية من classes. وصلى الله وسلم وبارك على نبينا محمد وعلى اّله وصحبه أجمعين. المراجع: - sourcemaking.com/design_patterns - java design pattern , rohit joshi - head first design pattern - tutorialspoint.com
  4. بسم الله الرحمن الرحيم كما ما بدأناه بالحديث عن أكثر Design pattern استخداما في بيئة Android واليوم سنتحدث عن أحد أنواع Structural pattern , ألا وهو Adapter pattern ,قبل أن نبدأ, وبما أننا في أول pattern في هذه السلسة من نوع Structural فماذا نعني ب Structural pattern ؟ Structural pattern : هو تصميم للكود بطرق تسهل فهم العلاقة بين classes و objects والربط بينها. جميل جدا, إذا ماهو Adapter pattern ؟ من كتاب GoF ولتبسيط الأمور, هذا pattern يستخدم لربط بين two classes لا يمكن الربط بينهما لعدم التوافق. كثيراً ما تكون التعريفات غير مفيدة, لنبدأ بكتابة بعض الأكواد تشرح هذا pattern. بداية دعنا نعرف المشكلة, لو فرضنا أن لديك class قديم قمت ببنائه, ومن ثم اردت الربط بينه وبين class جديد فلن تقوم بإعادة كتابته, وإنما تقوم ببناء interface يقوم بالربط بينهم كما سنقوم بذالك في المثال التالي. لنفترض أن لدينا نظام إدارة فصول إلكتروني, وأردنا ربطه مع نظام حضور وانصراف جديد,فكان شكل interface الخاص بنا كالتالي. public interface Xattendance { public String getStuedntName(); public long getStuedntId() ; public String getTime(); public void setStuedntName(String stuedntName); public void setStuedntId(long stuedntId); public void setTime(String time); } ثم قمنا بعمل implementation له داخل هذا class public class XattendanceImpl implements Xattendance{ String stuedntName; long stuedntId; String time; public String getStuedntName() { return stuedntName; } public void setStuedntName(String stuedntName) { this.stuedntName = stuedntName; } public long getStuedntId() { return stuedntId; } public void setStuedntId(long stuedntId) { this.stuedntId = stuedntId; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } } ولكن وقع الإشكال عندما أردنا ربط هذا class الخاص بنا نظام لتحضير والذي كان شكل interface الخاص به كالتالي: public interface XnewAttendaceSystem { public String getName() ; public void setName(String name); public String getId(); public void setId(String id); public String getMinute(); public void setMinute(String minute); public String getHour(); public void setHour(String hour); } وclass الذي سيقوم بعمل implementation له بهذا الشكل public class XnewAttendaceSystemImpl implements XnewAttendaceSystem{ String name; String id; String minute; String hour; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMinute() { return minute; } public void setMinute(String minute) { this.minute = minute; } public String getHour() { return hour; } public void setHour(String hour) { this.hour = hour; } } فكما ترى لدينا two classes من نوعين مختلفين, فيلزمنا إنشاء class لنسميه المحول, حيث يمكننا عن طريقه ربط two classes مع بعضهما. وهو يقوم بعمل implementation ل XnewAttendaceSystem وفي constructor يستقبل object من نوع Xattendance ويقوم بالتحويل public class fromOldClassToNewSystem implements XnewAttendaceSystem{ String name; String id; String minute; String hour; final Xattendance xattendance; public fromOldClassToNewSystem(Xattendance xattendance) { this.xattendance = xattendance; adapt(); } @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name = name; } @Override public String getId() { return this.id; } @Override public void setId(String id) { this.id = id; } @Override public String getMinute() { return this.minute; } @Override public void setMinute(String minute) { this.minute = minute; } @Override public String getHour() { return this.hour; } @Override public void setHour(String hour) { this.hour = hour; } public void adapt(){ setName(xattendance.getStuedntName()); setId(xattendance.getStuedntName()); setHour(xattendance.getTime().substring(0, 1)); setMinute(xattendance.getTime().substring(3, 4)); } } فكما ترى تم تحويل object من نوع إلى أخر, فدعنا نلقي نظرة عن طريقة استخدم هذا pattern. سنقوم بإنشاء بيانات نقوم نحن بكتابتها- من الممكن أن تكون البيانات قادمة من API أو من Database, ولكن هنا لتبسيط المقالة ستكون مكتوبة يدوياً- ومن ثم نحولها من النظام الخاص فينا, إلى النظام الذي قمنا بالربط معه. public static void main(String[] args) { Xattendance x = new XattendanceImpl(); x.setStuedntId(10110101); x.setStuedntName("ameen"); x.setTime("12:30"); XnewAttendaceSystem obj = new fromOldClassToNewSystem(x); //test System.out.println(obj.getName()); System.out.println(obj.getHour()); } وبهذا نلاحظ أنه تم إرسال البيانات من نظام إلى أخر باستخدام adapter pattern. ختاما لتعلم أن لدينا نوعان من adapter الأول هو object adapter وهو الذي تعاملنا معه في المثال اّنف الذكر, والذي استخدمنا في بنائه مبدأ composition, والأخر هو adapter class والذي يعتمد في بنائه على multiple inheritance وهو غير مدعوم في java تجنباً لمشكلة DDD (deadly diamond of death), ولكن يمكنك تطبيقها باستخدام لغات تدعم multiple inheritance كلغة C++ ويكون شكل Digram كالتالي: إلى هنا, هذا ما كان في الجعبة ووفق الله الجميع وصلى الله وسلم وبارك المراجع: - sourcemaking.com/design_patterns - java design pattern , rohit joshi - head first design pattern
  5. بسم الله الرحمن الرحيم استكمالا للحديث عن أكثر Design pattern استخداما في بيئة Android, وبعد أن كنا قد بدأنا في الحديث عن أنواع Structural pattern, اليوم سيكون حديثنا عن Facade Pattern, فنبدأ بالسؤال المعتاد ماهو Facade Pattern؟ لنبدأ بتعريفه: إذا فهو Pattern يستخدم لتبسيط interfacees لأجزاء النظام, الصورة التالية ستكون شارحة جدا لوظيفة واّلية عمل هذا Pattern. جميل جدا, أعتقد أن المفهوم بات متضحاً جدا, فلندلف إلى تطبيقه بالمثال التالي. لتعلم أنه Pattern بسيط وواضح جدا, فلو فرضنا أن لدينا interface اسمه line, ولديه three claases قاموا بعمل implementation لهذا interface. وهم straight و curve و snaky. public interface Line { void draw(); } class straight public class Straight implements Line { @Override public void draw() { System.out.println("straight"); } } class curve public class Curve implements Line { @Override public void draw() { System.out.println("curve"); } } snaky class public class Snaky implements Line { @Override public void draw() { System.out.println("snaky"); } } أصبح لدينا مجموعة من classes, لو كان النظام الذي سنقوم ببنائه أكبر من ذلك سيكون من الصعب جدا تذكر هذه classes, لذلك سنقوم ببناء Facade class يحل هذه المشكلة ويبسط التعامل معها. public class LineMaker { private Line straight; private Line curve; private Line snaky; public LineMaker() { straight = new Straight(); curve = new Curve(); snaky = new Snaky(); } public void drawStraight(){ straight.draw(); } public void drawCurve(){ curve.draw(); } public void drawSnaky(){ snaky.draw(); } } أصبح التعامل هذه المجموعة من classes غاية في البساطة ويمكن التعامل معها في main كالتالي public static void main(String[] args) { LineMaker lineMaker = new LineMaker(); lineMaker.drawStraight(); lineMaker.drawCurve(); lineMaker.drawSnaky(); } } بينما لو لم نقم باستخدام facade class سيكون شكل main كالتالي: public static void main(String[] args) { private Line straight = new Straight(); private Line curve = new Curve(); private Line snaky = new Snaky(); straight.drawStraight(); curve.drawCurve(); snaky.drawSnaky(); } } طبعا هذا المثال يبسط المشكلة لكن في حال كان حجم النظام كبير ستواجه مشكلة في التعامل مع هذه الكمية من classes. وصلى الله وسلم وبارك على نبينا محمد وعلى اّله وصحبه أجمعين. المراجع: - sourcemaking.com/design_patterns - java design pattern , rohit joshi - head first design pattern - tutorialspoint.com تم ترقية هذا الطرح المميز الى صفحة المقالات
  6. بسم الله الرحمن الرحيم كما ما بدأناه بالحديث عن أكثر Design pattern استخداما في بيئة Android واليوم سنتحدث عن أحد أنواع Structural pattern , ألا وهو Adapter pattern ,قبل أن نبدأ, وبما أننا في أول pattern في هذه السلسة من نوع Structural فماذا نعني ب Structural pattern ؟ Structural pattern : هو تصميم للكود بطرق تسهل فهم العلاقة بين classes و objects والربط بينها. جميل جدا, إذا ماهو Adapter pattern ؟ من كتاب GoF ولتبسيط الأمور, هذا pattern يستخدم لربط بين two classes لا يمكن الربط بينهما لعدم التوافق. كثيراً ما تكون التعريفات غير مفيدة, لنبدأ بكتابة بعض الأكواد تشرح هذا pattern. بداية دعنا نعرف المشكلة, لو فرضنا أن لديك class قديم قمت ببنائه, ومن ثم اردت الربط بينه وبين class جديد فلن تقوم بإعادة كتابته, وإنما تقوم ببناء interface يقوم بالربط بينهم كما سنقوم بذالك في المثال التالي. لنفترض أن لدينا نظام إدارة فصول إلكتروني, وأردنا ربطه مع نظام حضور وانصراف جديد,فكان شكل interface الخاص بنا كالتالي. public interface Xattendance { public String getStuedntName(); public long getStuedntId() ; public String getTime(); public void setStuedntName(String stuedntName); public void setStuedntId(long stuedntId); public void setTime(String time); } ثم قمنا بعمل implementation له داخل هذا class public class XattendanceImpl implements Xattendance{ String stuedntName; long stuedntId; String time; public String getStuedntName() { return stuedntName; } public void setStuedntName(String stuedntName) { this.stuedntName = stuedntName; } public long getStuedntId() { return stuedntId; } public void setStuedntId(long stuedntId) { this.stuedntId = stuedntId; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } } ولكن وقع الإشكال عندما أردنا ربط هذا class الخاص بنا نظام لتحضير والذي كان شكل interface الخاص به كالتالي: public interface XnewAttendaceSystem { public String getName() ; public void setName(String name); public String getId(); public void setId(String id); public String getMinute(); public void setMinute(String minute); public String getHour(); public void setHour(String hour); } وclass الذي سيقوم بعمل implementation له بهذا الشكل public class XnewAttendaceSystemImpl implements XnewAttendaceSystem{ String name; String id; String minute; String hour; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMinute() { return minute; } public void setMinute(String minute) { this.minute = minute; } public String getHour() { return hour; } public void setHour(String hour) { this.hour = hour; } } فكما ترى لدينا two classes من نوعين مختلفين, فيلزمنا إنشاء class لنسميه المحول, حيث يمكننا عن طريقه ربط two classes مع بعضهما. وهو يقوم بعمل implementation ل XnewAttendaceSystem وفي constructor يستقبل object من نوع Xattendance ويقوم بالتحويل public class fromOldClassToNewSystem implements XnewAttendaceSystem{ String name; String id; String minute; String hour; final Xattendance xattendance; public fromOldClassToNewSystem(Xattendance xattendance) { this.xattendance = xattendance; adapt(); } @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name = name; } @Override public String getId() { return this.id; } @Override public void setId(String id) { this.id = id; } @Override public String getMinute() { return this.minute; } @Override public void setMinute(String minute) { this.minute = minute; } @Override public String getHour() { return this.hour; } @Override public void setHour(String hour) { this.hour = hour; } public void adapt(){ setName(xattendance.getStuedntName()); setId(xattendance.getStuedntName()); setHour(xattendance.getTime().substring(0, 1)); setMinute(xattendance.getTime().substring(3, 4)); } } فكما ترى تم تحويل object من نوع إلى أخر, فدعنا نلقي نظرة عن طريقة استخدم هذا pattern. سنقوم بإنشاء بيانات نقوم نحن بكتابتها- من الممكن أن تكون البيانات قادمة من API أو من Database, ولكن هنا لتبسيط المقالة ستكون مكتوبة يدوياً- ومن ثم نحولها من النظام الخاص فينا, إلى النظام الذي قمنا بالربط معه. public static void main(String[] args) { Xattendance x = new XattendanceImpl(); x.setStuedntId(10110101); x.setStuedntName("ameen"); x.setTime("12:30"); XnewAttendaceSystem obj = new fromOldClassToNewSystem(x); //test System.out.println(obj.getName()); System.out.println(obj.getHour()); } وبهذا نلاحظ أنه تم إرسال البيانات من نظام إلى أخر باستخدام adapter pattern. ختاما لتعلم أن لدينا نوعان من adapter الأول هو object adapter وهو الذي تعاملنا معه في المثال اّنف الذكر, والذي استخدمنا في بنائه مبدأ composition, والأخر هو adapter class والذي يعتمد في بنائه على multiple inheritance وهو غير مدعوم في java تجنباً لمشكلة DDD (deadly diamond of death), ولكن يمكنك تطبيقها باستخدام لغات تدعم multiple inheritance كلغة C++ ويكون شكل Digram كالتالي: إلى هنا, هذا ما كان في الجعبة ووفق الله الجميع وصلى الله وسلم وبارك المراجع: - sourcemaking.com/design_patterns - java design pattern , rohit joshi - head first design pattern تم ترقية هذا الطرح المميز الى صفحة المقالات
  7. بسم الله الرحمن الرحيم نكمل ما بدأناه بالحديث عن أكثر Design pattern استخداما في بيئة Android واليوم نتم الحديث عن Creational patterns بعد تكلمنا عن Singleton و dependency injection اليوم سكون حديثنا عن Builder Design patterns نقتبس التعريف من كتاب GoF جميل جداً كما ذكر في التعريف إذ أننا سنقوم بتقسيم بناء Object إلى عدة method نبدء بالأساسية التي لا يمكن التعامل مع Object بدونها, ثم ننتقل إلى الاختيارية حتى نتم بناء Object على الوجه الذي نريد. لنبدأ بمثال يوضح المشكلة . لنفترض أننا نريد إنشاء class لفريق معين كالتالي : public class Player { private String name ; private String team ; private double height ; private int salary ; private String phone ; private String twitterAccount ; public Player(String name, String team, double height, int salary, String phone, String twitterAccount) { this.name = name; this.team = team; this.height = height; this.salary = salary; this.phone = phone; this.twitterAccount = twitterAccount; } } نحن هنا نتعامل مع six class data member فقط فلو فرضنا أن لدينا 10 فسيكون شكل constructor رهيب جدا وفي هذا عدة مشاكل منها أنك لن تستطيع تذكر أماكن data member في constructor مما يسبب في وضع البيانات في غير مكانها وستحصل على نتائج غريبة قد لا يصنفها compiler ك Error ولتلافي هذه المشكلة سنستعرض أحد الحلول وهو Telescopin Design Pattern حيث سنقوم بكتابة شكل constructor فقط public Player(String name, String team) { this(name, team,0.0); } public Player(String name, String team, double height) { this(name, team,height,0); } public Player(String name, String team, double height, int salary) { this(name, team,height,salary,""); } public Player(String name, String team, double height, int salary, String phone) { this(name, team,height,salary,phone,""); } public Player(String name, String team, double height, int salary, String phone, String twitterAccount) { this.name = name; this.team = team; this.height = height; this.salary = salary; this.phone = phone; this.twitterAccount = twitterAccount; } كما تلاحظ نبدأ بالمعلومات الأساسية التي لا يمكن الاستمرار بدونها ثم بعد ذلك نبدأ بتوفير البيانات في حال كانت لدينا أو نسند قيم افتراضية. ولهذه الطريقة عيوبها أولا صعوبة قراءة code وأيضا صعوبة في كتابة client code (إنشاء object ), بالإضافة إلى أنك قد تجبر على كتابة parameters التي لا تريد كتابتها. لهذه قد يكون استخدام Telescopin ليس الحل المناسب, لكن يوجد حل أخر لنأخذ فرصة في تجربته ألا وهو javaBeans Pattern , لنطبقه على نفس المثال السابق. public Player() {} public void setName(String name) { this.name = name; } public void setTeam(String team) { this.team = team; } public void setHeight(double height) { this.height = height; } public void setSalary(int salary) { this.salary = salary; } public void setPhone(String phone) { this.phone = phone; } public void setTwitterAccount(String twitterAccount) { this.twitterAccount = twitterAccount; } فكما ترى نبدأ ب constructor وبعد ذلك setters بعدد data members , وهذا الحل يحل بعض الإشكالات السابقة ويخلق إشكالات جديدة, فأولا سيكون إنشاء Object مرتبط بعدد كبير من method التي يجب استدعاؤها وتذكرها بالإضافة إلى أنه لا يمكنك جعل class immutable, وليس thread safe, فتجازونا بعض إشكالات Telescopin ووقعنا في إشكالات أخرى ,فسنجمع بينهم في Patterm وهو Builder Design Pattern. لنعيد كتابة constructor بإستخدام نفس المثال public class Player { private String name ; private String team ; private double height ; private int salary ; private String phone ; private String twitterAccount ; public static class Builder { private final String name ; private final String team ; private double height = 0; private int salary = 0; private String phone = ""; private String twitterAccount = ""; public Builder(String name, String team) { this.name = name; this.team = team; } public Builder height(double height) { this.height = height; return this ; } public Builder salary(int salary) { this.salary = salary; return this ; } public Builder phone(String phone) { this.phone = phone; return this ; } public Builder twitterAccount(String twitterAccount) { this.twitterAccount = twitterAccount; return this ; } public Player build(){ return new Player(this); } } public Player(Builder builder) { name = builder.name; team = builder.team; height = builder.height; salary = builder.salary;; phone = builder.phone;; twitterAccount = builder.twitterAccount; } } في البداية قمنا ببناء inner class سميناه Builder حيث سنقوم بداخله ببناء Object الخاص بنا وبعد ذلك نرجعه عن طريق دالة build التي تقوم بعمل return ل object الذي قمنا ببناءه, نبدأ بالمتغيرات الأساسية في Object وهي التي كانت final ولا تأخذ قيم افتراضية, ثم الاختيارية التي قد نستخدمها فنستدعي method الخاصة بها, أو لا نستخدمها فنسند فيه القيم الافتراضية, فيكون شكل client كالتالي Player player = new Player.Builder("Aziz", "Android") .height(10.2) .phone("0555555555") .salary(100) .build(); كما نلاحظ قمنا بكتابة client سهل القراءة وحتى الكتابة, ومرن وبسيط جدا. اذا ف Builder Pattern خيار جيد جداً حينما يكون عدد Parameters ليس بالقليل ويكون فيه عدد من Parameters اختيارية المراجع ومصادر : Effective Java (2nd Edition) https://sourcemaking.com/design_patterns/builder http://www.blackwasp.co.uk/gofpatterns.aspx
  8. بسم الله الرحمن الرحيم نكمل ما بدأناه بالحديث عن أكثر Design Batterns استخداما في بيئة Android واليوم نتم الحديث عن Creational patterns بعد تكلمنا عن Singleton و dependency injection اليوم سكون حديثنا عن Builder Design Battern نقتبس التعريف من كتاب GoF جميل جداً كما ذكر في التعريف إذ أننا سنقوم بتقسيم بناء Object إلى عدة method نبدء بالأساسية التي لا يمكن التعامل مع Object بدونها, ثم ننتقل إلى الاختيارية حتى نتم بناء Object على الوجه الذي نريد. لنبدأ بمثال يوضح المشكلة . لنفترض أننا نريد إنشاء class لفريق معين كالتالي : public class Player { private String name ; private String team ; private double height ; private int salary ; private String phone ; private String twitterAccount ; public Player(String name, String team, double height, int salary, String phone, String twitterAccount) { this.name = name; this.team = team; this.height = height; this.salary = salary; this.phone = phone; this.twitterAccount = twitterAccount; } } نحن هنا نتعامل مع six class data member فقط فلو فرضنا أن لدينا 10 فسيكون شكل constructor رهيب جدا وفي هذا عدة مشاكل منها أنك لن تستطيع تذكر أماكن data member في constructor مما يسبب في وضع البيانات في غير مكانها وستحصل على نتائج غريبة قد لا يصنفها compiler ك Error ولتلافي هذه المشكلة سنستعرض أحد الحلول وهو Telescopin Design Pattern حيث سنقوم بكتابة شكل constructor فقط public Player(String name, String team) { this(name, team,0.0); } public Player(String name, String team, double height) { this(name, team,height,0); } public Player(String name, String team, double height, int salary) { this(name, team,height,salary,""); } public Player(String name, String team, double height, int salary, String phone) { this(name, team,height,salary,phone,""); } public Player(String name, String team, double height, int salary, String phone, String twitterAccount) { this.name = name; this.team = team; this.height = height; this.salary = salary; this.phone = phone; this.twitterAccount = twitterAccount; } كما تلاحظ نبدأ بالمعلومات الأساسية التي لا يمكن الاستمرار بدونها ثم بعد ذلك نبدأ بتوفير البيانات في حال كانت لدينا أو نسند قيم افتراضية. ولهذه الطريقة عيوبها أولا صعوبة قراءة code وأيضا صعوبة في كتابة client code (إنشاء object ), بالإضافة إلى أنك قد تجبر على كتابة parameters التي لا تريد كتابتها. لهذه قد يكون استخدام Telescopin ليس الحل المناسب, لكن يوجد حل أخر لنأخذ فرصة في تجربته ألا وهو javaBeans Pattern , لنطبقه على نفس المثال السابق. public Player() {} public void setName(String name) { this.name = name; } public void setTeam(String team) { this.team = team; } public void setHeight(double height) { this.height = height; } public void setSalary(int salary) { this.salary = salary; } public void setPhone(String phone) { this.phone = phone; } public void setTwitterAccount(String twitterAccount) { this.twitterAccount = twitterAccount; } فكما ترى نبدأ ب constructor وبعد ذلك setters بعدد data members , وهذا الحل يحل بعض الإشكالات السابقة ويخلق إشكالات جديدة, فأولا سيكون إنشاء Object مرتبط بعدد كبير من method التي يجب استدعاؤها وتذكرها بالإضافة إلى أنه لا يمكنك جعل class immutable, وليس thread safe, فتجازونا بعض إشكالات Telescopin ووقعنا في إشكالات أخرى ,فسنجمع بينهم في Patterm وهو Builder Design Pattern. لنعيد كتابة constructor بإستخدام نفس المثال public class Player { private String name ; private String team ; private double height ; private int salary ; private String phone ; private String twitterAccount ; public static class Builder { private final String name ; private final String team ; private double height = 0; private int salary = 0; private String phone = ""; private String twitterAccount = ""; public Builder(String name, String team) { this.name = name; this.team = team; } public Builder height(double height) { this.height = height; return this ; } public Builder salary(int salary) { this.salary = salary; return this ; } public Builder phone(String phone) { this.phone = phone; return this ; } public Builder twitterAccount(String twitterAccount) { this.twitterAccount = twitterAccount; return this ; } public Player build(){ return new Player(this); } } public Player(Builder builder) { name = builder.name; team = builder.team; height = builder.height; salary = builder.salary;; phone = builder.phone;; twitterAccount = builder.twitterAccount; } } في البداية قمنا ببناء inner class سميناه Builder حيث سنقوم بداخله ببناء Object الخاص بنا وبعد ذلك نرجعه عن طريق دالة build التي تقوم بعمل return ل object الذي قمنا ببناءه, نبدأ بالمتغيرات الأساسية في Object وهي التي كانت final ولا تأخذ قيم افتراضية, ثم الاختيارية التي قد نستخدمها فنستدعي method الخاصة بها, أو لا نستخدمها فنسند فيه القيم الافتراضية, فيكون شكل client كالتالي Player player = new Player.Builder("Aziz", "Android") .height(10.2) .phone("0555555555") .salary(100) .build(); كما نلاحظ قمنا بكتابة client سهل القراءة وحتى الكتابة, ومرن وبسيط جدا. اذا ف Builder Pattern خيار جيد جداً حينما يكون عدد Parameters ليس بالقليل ويكون فيه عدد من Parameters اختيارية المراجع ومصادر : Effective Java (2nd Edition) https://sourcemaking.com/design_patterns/builder http://www.blackwasp.co.uk/gofpatterns.aspx تم ترقية هذا الطرح المميز الى صفحة المقالات
  9. بسم الله الرحمن الرحيم نكمل ما بدأنا به في شرح design pattern الأكثر استخداما في بيئة Android اليوم بإذن الله سيكون حديثنا عن مفهوم Dependency Injection. تعريف wikipedia فما هو Inversion of Control ؟ التعريف من wikipedia أيضا قد تكون عملية الترجمة غير مجدية أو أن التعريفات ليست واضحة عموما بإذن الله سنفصل الموضوع تفصيلا بسيطاً يجعله واضحا بإذن الله. ببساطة مفهوما Dependency Injection و Inversion of Control (IoC) بدون الخوض في التفاصيل تعنى بحذف dependencies من code ماذا نعني ب dependencies (الاعتمادية) ؟ خذ هذا المثال public class MSWord { private ErrorCorrection corrector; public MSWord() { this.corrector = new ErrorCorrection(); } } نلاحظ أنه لكي أقوم بإنشاء Object من class MSWrod فأنا مضطر لإنشاء Object من class ErrorCorrection ونقطة الاعتمادية هنا هي إنشاء Object لكي تقوم بتنفيذ Dependency Injection لابد أن تعمل Inject (حقن) object بحيث يأتي إلى class ك parameter مبني, فتتم عملية الإسناد فقط بالمثال يتضح المقال, فهذه مثال ننفذ فيه Dependency Injection على نفس code السابق public class MSWord { private ErrorCorrection corrector; public MSWord(ErrorCorrection corrector) { this.corrector = corrector; } } ففي Main أو مكان تنفيذ code ستقوم بالتالي public static void main(String[] args) { ErrorCorrection corrector = new ErrorCorrection() MSWord word = new MSWord(corrector); } جميل جدا, الأمر نفسه بتمرير dependencies ك parameter سواء كان في constructor أو method, يأتي السؤال الأهم لماذا أستخدم هذا pattern ؟ لنفترض أن لدينا class A ولديه subclass B ويحتاج object من class C public class C { String s ; C(String s){ this.s = s ; } } public class A { private C c ; A(String s){ this.c = new C(s); } } public class B extends A{ B(String s){ super(s); } } جميل, في حال تم تغيير constructor في class C فأنت مضطر لتغيير constructor في class A وجميع من يرث منه, فلو قلنا فرضاً أننا قررنا إضافة Strimg أخر لدى C public class C { String s ,s1; C(String s ,String s1 ){ this.s = s ; this.s1 = s1 ; } } public class A { private C c ; A(String s,String s1){ this.c = new C(s,s1); } } public class B extends A{ B(String s,String s1){ super(s,s1); } } فكما ترى اضطررنا لتغيير جميع classes نظرا لأننا غيرنا في class يعتمدون عليه, لكن ماذا لو طبقنا Dependency Injection على هؤلاء classes public class C { String s ; C(String s){ this.s = s ; } } public class A { private C c ; A(C c){ this.c = c ; } } public class B extends A{ B(C c){ super(c); } } لنغير في C و constructor الخاص بها ونرى ماذا يحدث. public class C { String s ,s1; C(String s,String s1){ this.s = s ; this.s1 = s1 ; } } public class A { private C c ; A(C c){ this.c = c ; } } public class B extends A{ B(C c){ super(c); } } نلاحظ الفائدة Dependency Injection حيث أن التغيير تم في مكان واحد فقط. من فوائد Dependency Injection له فوائد جدا كثيرة ستلاحظها أثناء الكتابة أو حين تقرأ عنه ومنها - قابلية التعديل - maintainability - قابلية الاختبار - testability - قابلية الاستفادة من نفس code عوضاً عن إعادة كتابته - reusability - بالإضافة إلى أنه يقلل من coupling في code للاستزادة هنا بعض المراجع التي قرأت منها وفيها الكثير من المعلومات التي فضلت عدم إيرادها لجعل الموضوع أكثر بساطة https://en.wikipedia.org/wiki/Dependency_injection http://frogermcs.github.io/dependency-injection-with-dagger-2-introdution-to-di/ http://stackoverflow.com/questions/3058/what-is-inversion-of-control
  10. بسم الله الرحمن الرحيم نكمل ما بدأنا به في شرح design pattern الأكثر استخداما في بيئة Android اليوم بإذن الله سيكون حديثنا عن مفهوم Dependency Injection. تعريف wikipedia فما هو Inversion of Control ؟ التعريف من wikipedia أيضا قد تكون عملية الترجمة غير مجدية أو أن التعريفات ليست واضحة عموما بإذن الله سنفصل الموضوع تفصيلا بسيطاً يجعله واضحا بإذن الله. ببساطة مفهوما Dependency Injection و Inversion of Control (IoC) بدون الخوض في التفاصيل تعنى بحذف dependencies من code ماذا نعني ب dependencies (الاعتمادية) ؟ خذ هذا المثال public class MSWord { private ErrorCorrection corrector; public MSWord() { this.corrector = new ErrorCorrection(); } } نلاحظ أنه لكي أقوم بإنشاء Object من class MSWrod فأنا مضطر لإنشاء Object من class ErrorCorrection ونقطة الاعتمادية هنا هي إنشاء Object لكي تقوم بتنفيذ Dependency Injection لابد أن تعمل Inject (حقن) object بحيث يأتي إلى class ك parameter مبني, فتتم عملية الإسناد فقط بالمثال يتضح المقال, فهذه مثال ننفذ فيه Dependency Injection على نفس code السابق public class MSWord { private ErrorCorrection corrector; public MSWord(ErrorCorrection corrector) { this.corrector = corrector; } } ففي Main أو مكان تنفيذ code ستقوم بالتالي public static void main(String[] args) { ErrorCorrection corrector = new ErrorCorrection() MSWord word = new MSWord(corrector); } جميل جدا, الأمر نفسه بتمرير dependencies ك parameter سواء كان في constructor أو method, يأتي السؤال الأهم لماذا أستخدم هذا pattern ؟ لنفترض أن لدينا class A ولديه subclass B ويحتاج object من class C public class C { String s ; C(String s){ this.s = s ; } } public class A { private C c ; A(String s){ this.c = new C(s); } } public class B extends A{ B(String s){ super(s); } } جميل, في حال تم تغيير constructor في class C فأنت مضطر لتغيير constructor في class A وجميع من يرث منه, فلو قلنا فرضاً أننا قررنا إضافة Strimg أخر لدى C public class C { String s ,s1; C(String s ,String s1 ){ this.s = s ; this.s1 = s1 ; } } public class A { private C c ; A(String s,String s1){ this.c = new C(s,s1); } } public class B extends A{ B(String s,String s1){ super(s,s1); } } فكما ترى اضطررنا لتغيير جميع classes نظرا لأننا غيرنا في class يعتمدون عليه, لكن ماذا لو طبقنا Dependency Injection على هؤلاء classes public class C { String s ; C(String s){ this.s = s ; } } public class A { private C c ; A(C c){ this.c = c ; } } public class B extends A{ B(C c){ super(c); } } لنغير في C و constructor الخاص بها ونرى ماذا يحدث. public class C { String s ,s1; C(String s,String s1){ this.s = s ; this.s1 = s1 ; } } public class A { private C c ; A(C c){ this.c = c ; } } public class B extends A{ B(C c){ super(c); } } نلاحظ الفائدة Dependency Injection حيث أن التغيير تم في مكان واحد فقط. من فوائد Dependency Injection له فوائد جدا كثيرة ستلاحظها أثناء الكتابة أو حين تقرأ عنه ومنها - قابلية التعديل - maintainability - قابلية الاختبار - testability - قابلية الاستفادة من نفس code عوضاً عن إعادة كتابته - reusability - بالإضافة إلى أنه يقلل من coupling في code للاستزادة هنا بعض المراجع التي قرأت منها وفيها الكثير من المعلومات التي فضلت عدم إيرادها لجعل الموضوع أكثر بساطة https://en.wikipedia.org/wiki/Dependency_injection http://frogermcs.github.io/dependency-injection-with-dagger-2-introdution-to-di/ http://stackoverflow.com/questions/3058/what-is-inversion-of-control تم ترقية هذا الطرح المميز الى صفحة المقالات
  11. بسم الله الرحمن الرحيم بإذن الله ستكون هذه المقالة بداية لسلسلة من المقالات المتعلقة ب Design Patterns الأكثر أستخداما في بيئة Android. ونستهل هذه السلسلة ب Observer Design Pattern أو ما يسمى ب Publish-Subscribe (Pub-Sub) Pattern. في تعريف هذا Pattern في كتاب GoF : ولتبسيط التعريف نقول : عندما تريد تنبيه أو تحديث مجموعة objects عن حدوث تغيير في object أخر جميل جداً, دعنا نضرب مثالاً قبل أن نبدأ. - لدينا مدونة تنشر مقالات. - قمت أنت بالأشتراك في قائمة تقوم بتنبيهك في حال صدرت أي مقالة. - لم تعد مهتما بالمجال الذي تكتب هذه المدونة عنه, قمت بإلغاء الإشتراك منها. - قام مجموعة من أصدقائك بالإشتراك وإلغاء الإشتراك. المثال السابق هو ملخص لما للكود الذي سنقوم بكتابته في حال فهمك إياه فأنت قطعت شوط كبير في فهم هذا Design Pattern . بداية هذا هو UML الخاص بالكود الذي سنكتبه الان, سيتضح أكثر بعد كتابه الكود. جميل الان لنبدأ في كتابة الكود في البداية لدينا two interfaces الأول هو subjuct أو يمكنك أن تسميه puplisher والثاني observer أو يمكنك أن تسميه subscriber - إذا لم تكن تستخدم intrtfaces أثناء كتابتك لبرامجك فقد وضعت مرجع قد يفيدك في نهاية المقالة لمعرفة أهمية وأليه استخدام interfaces- public interface Subject { void addObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); public void setUpdate(String article); public String getUpdate(); } public interface Observer { void Update (); } ليس هناك الكثير من التفاصيل في إنشاء هذين interfaces لكن سنبدأ في التعليق عند مرحلة implementation دعنا نبدأ ب subjuct ونسوي له implementation في class Publisher public class Publisher implements Subject{ private List <Observer> observers ; String article; public Publisher() { observers = new ArrayList<>(); } @Override public void addObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers() { for(Observer ob : observers) ob.Update(); } @Override public String getUpdate() { return article; } @Override public void setUpdate(String article) { this.article = article ; notifyObservers(); } } في البداية قمنا بإنشاء List نستطيع أن نسميها قائمة المشتركين في هذا class الذي أفترضنا أنه مدونة لنشر المقالات, ومتغير String والذي هو عبارة عن المقالة التي نريد تنبيه متابعينا حال صدورها. لدينا ثلاث functions رئيسية لا يكاد يخلو Observer Design Pattern منها وهي : addObserver : ﻷضافة متابع جديد لهذا class أو كما يحلو لنا أن نسميه المدون removeObserver : لحذف متابع notifyObservers : لتنبيه المتابعين ونشر المقالة لهم ,وقد تختلف تصرفاتهم عندما يأتيهم التنبيه منهم يبدأ بقر ائتها أو إهمالهما أو حفظها لديه لغرض معين أو غير ذلك. لننتقل الان ل classes التي قامت بعمل implementation ل Observer وهم لا يختلفون كثيرا في implementation public class Reader implements Observer{ private String article ; private Subject sub ; public Reader(Subject blog) { article = "" ; sub = blog ; sub.addObserver(this); } @Override public void Update() { article = sub.getUpdate(); System.out.println("You :"+article); } } public class Reader implements Observer{ private String article ; private Subject sub ; public Reader(Subject blog) { article = "" ; sub = blog ; sub.addObserver(this); } @Override public void Update() { article = sub.getUpdate(); System.out.println("You :"+article); } } class reader والذي افترضت حين بدأت بكتابة المقالة أنه أنت , وclass الأخر هو Friend والذي افترضت أنه صديقك كما تلاحظون في كلا classes قمنا بعمل Override ل update تحضر المقالة و تقرأها, وفي داخل Constructor قمنا بإنشاء المتغيرات الخاصة بنا والانضمام إلى List المتابعين الخاصين بالمدونة public class PupSub { public static void main(String[] args) { // TODO code application logic here Publisher pup = new Publisher(); new Reader(pup); new Friend(pup); pup.setUpdate("Art1"); pup.setUpdate("Art2"); pup.setUpdate("Art3"); } } في البداية قمنا بإنشاء قائمة المتابعين الخاصة بالمدونة وكانت فارغة قم أنشأنا متابع من Reader class والذي سبق وافترضنا أنه أنت, ثم Friend class والذي هو صديقك. كلاكما متابع لهذه المدونة والمدون قد وضعكما في قائمته ويقوم بإخباركم عن كل جديد في هذه المدونة فور وصوله وأنتما تقومان بقراءة ما يأتيكما فور ما يأتي. ثم نشر المدون ثلاث تدوينات عبر setUpdate التي تستدعي notifyObservers ثم تقوم هذه function باستدعاء Update الخاصة بكل class من المتابعين ويقومون بدورهم بتحديث التدوينات التي لديهم عن طريق استداعاء getUpdate الخاصة بالمدون. قد تجد صعوبة في فهم هذا Pattern وغيره في المرة الأولى وهذا طبيعي. عليك تطبيقة وفهم أجزاءه وبعد ذلك سيتضح لك. وأخيرا, لماذا أستخدم Observer Design Pattern ؟ Observer Design Pattern يقلل من التداخل في الكود-Low Coupling- وهذا سيسهل عليك في حال رغبت في إضافة classes أو إزالتها, فجميع classes تعمل بشكل مستقل عن بعضها . المراجع: - http://informatic-ar.com/ال-coupling-وال-cohesion-في-تصميم-البرمجيات/ - https://sourcemaking.com/design_patterns/observer - Head First Design Patterns - البرمجة باستخدام Interfaces
  12. بسم الله الرحمن الرحيم نكمل ما بدأناه في الحديث عن design pattern التي يكثر استخدامها في بيئة android اليوم سيكون حديثنا عن Singleton هذا pattern يصنف ضمن Creational Patterns والتي تهتم بطرق إنشاء objct أو مجموعة objects والعلاقات التي بينها في حال كان لدينا أكثر من object. ونبدأ بتعريف هذا Pattern من كتاب GoF : إذا في Singleton يجعلك متأكدا من أن class الذي بنيته باستخدام هذا pattern انشأ منه object اوحد فقط . وأول ما يخطر في بالك في حال كنت جديدا على هذا النمط, لماذا object اوحد فقط ؟ سيكون الجواب في نهاية المقالة بإذن الله بداية سنبدأ بكتابة مثال بسيط على Singleton pattern بلغة java public class Singleton { private static Singleton singleton = null ; private Singleton() {} public static Singleton getSingleton() { if (singleton == null) singleton = new Singleton(); return singleton; } } يبدو واضحا من الوهلة الأولى أن class انف الذكر لا يمكن إنشاء أكثر من object منه. في private Constructor لا تقوم بإنشاء أي object كما أن data member التي لديك static لكنها private بمعنى لديك static data member لا تستطيع الوصول إليه إلا عن طريق method داخل class وهذه method تقوم بالتأكد فيما إذا كان لديك object تم إنشاؤه, والذي لن ينشأ غيره. لكن في الحقيقة لو استخدمنا نفس هذا implementation في multithreading فقد ينشأ أكثر من object, فلمنع هذا حدوث هذه المشكلة لدينا أكثر من حل,فمثلا نستخدم synchronized keyword والتي ستعمل فقط مع thread واحده في نفس الوقت, لتفادي إنشاء أكثر من instance - وقد لا يكون أفضل حل- قدد تتعدد طرق implementation ولكن تشترك في شيء واحد وهو أنه لا يمكن أنشاء أكثر من object من هذا class طيب جميل جداً لان لنطبق هذا pattern على Android سنقوم بالتالي بناء دالة تتأكد من حالة network وترجع true في حال كان هناك اتصال وError في حال لم يكن. ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if(info != null && info.isConnectedOrConnecting() && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting()){ //your code } ستكرر هذه code في كل مكان تنوي فيه التحقق من حالة الشبكة,, وعندما تكرر code فيعتبر إشكال في تصميمه. إذا ماذا سنفعل ؟ سنقوم بالتالي بناء App class , وهو class على مستوى التطبيق بمعنى يمكن لأي جزء من تطبيق أن يصل إليه, كيف ؟ يكون لدينا class فيه static method لكل object لا نحتاج لتكراره في code ومن ثم نستدعيه عن طريق class . مثال public static boolean NetworkState(Context context){ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); return info != null && info.isConnectedOrConnecting() && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting(); } في هذه الحالة يمكننا التحقق من حالة network بهذه الطريقة if(Application.NetworkState(Context context)){ //your code } وهذا يمنحنا ميزة سهولة التعديل maintainable في حال أردنا تغيير طريقة عمل function كإظهار تنبيه معين. وقس على ذلك من المزايا , كما أن هذا المثال يمكن استخدامه مع عدد كبير من الحالات كإنشاء object من مكتبة retrofit والوصول له من أي مكان على مسوى التطبيق. لا ينبغي عمل subclass ل Singleton class حيث سيتوجب عليك جعل Constructor public وبعد هذا التغيير لم يعد class يسمى Singleton حيث يمكن إنشاء instance منه. بالإضافة إلى أنك قد تكون جعلت static data member مما يعني أن subclass سيكون مشترك مع superclass في نفس data member وقد يؤدي إلى نتائج غير مرغوب فيها غالبا. نجيب على التساؤل الذي طرحناه في بداية المقالة لماذا object اوحد فقط ؟ أتضح ولو بشكل جزئي من الأمثلة التي ذكرناها في بداية المقالة بعض فوائد Singleton, وهو أنه ستمر بك بعض الحالات لا تريد فيها أكثر من object وحيد تتعامل معه ومن هذه الحالات Toast كما ذكرنا ,و thread pools و caches و logging والقائمة تطول, فمتى ما رأيت أن class فيه static class or data member هي التي تحتاجها فقط فسيكون Singleton حل مناسب لك. المراجع: Head First Design Patterns http://www.javaworld.com/article/2073352/core-java/simply-singleton.html http://www.blackwasp.co.uk/gofpatterns.aspx http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples
  13. بسم الله الرحمن الرحيم نكمل ما بدأناه في الحديث عن design pattern التي يكثر استخدامها في بيئة android اليوم سيكون حديثنا عن Singleton هذا pattern يصنف ضمن Creational Patterns والتي تهتم بطرق إنشاء objct أو مجموعة objects والعلاقات التي بينها في حال كان لدينا أكثر من object. ونبدأ بتعريف هذا Pattern من كتاب GoF : إذا في Singleton يجعلك متأكدا من أن class الذي بنيته باستخدام هذا pattern انشأ منه object اوحد فقط . وأول ما يخطر في بالك في حال كنت جديدا على هذا النمط, لماذا object اوحد فقط ؟ سيكون الجواب في نهاية المقالة بإذن الله بداية سنبدأ بكتابة مثال بسيط على Singleton pattern بلغة java public class Singleton { private static Singleton singleton = null ; private Singleton() {} public static Singleton getSingleton() { if (singleton == null) singleton = new Singleton(); return singleton; } } يبدو واضحا من الوهلة الأولى أن class انف الذكر لا يمكن إنشاء أكثر من object منه. في private Constructor لا تقوم بإنشاء أي object كما أن data member التي لديك static لكنها private بمعنى لديك static data member لا تستطيع الوصول إليه إلا عن طريق method داخل class وهذه method تقوم بالتأكد فيما إذا كان لديك object تم إنشاؤه, والذي لن ينشأ غيره. لكن في الحقيقة لو استخدمنا نفس هذا implementation في multithreading فقد ينشأ أكثر من object, فلمنع هذا حدوث هذه المشكلة لدينا أكثر من حل,فمثلا نستخدم synchronized keyword والتي ستعمل فقط مع thread واحده في نفس الوقت, لتفادي إنشاء أكثر من instance - وقد لا يكون أفضل حل- قدد تتعدد طرق implementation ولكن تشترك في شيء واحد وهو أنه لا يمكن أنشاء أكثر من object من هذا class طيب جميل جداً لان لنطبق هذا pattern على Android سنقوم بالتالي بناء دالة تتأكد من حالة network وترجع true في حال كان هناك اتصال وError في حال لم يكن. ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if(info != null && info.isConnectedOrConnecting() && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting()){ //your code } ستكرر هذه code في كل مكان تنوي فيه التحقق من حالة الشبكة,, وعندما تكرر code فيعتبر إشكال في تصميمه. إذا ماذا سنفعل ؟ سنقوم بالتالي بناء App class , وهو class على مستوى التطبيق بمعنى يمكن لأي جزء من تطبيق أن يصل إليه, كيف ؟ يكون لدينا class فيه static method لكل object لا نحتاج لتكراره في code ومن ثم نستدعيه عن طريق class . مثال public static boolean NetworkState(Context context){ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); return info != null && info.isConnectedOrConnecting() && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting(); } في هذه الحالة يمكننا التحقق من حالة network بهذه الطريقة if(Application.NetworkState(Context context)){ //your code } وهذا يمنحنا ميزة سهولة التعديل maintainable في حال أردنا تغيير طريقة عمل function كإظهار تنبيه معين. وقس على ذلك من المزايا , كما أن هذا المثال يمكن استخدامه مع عدد كبير من الحالات كإنشاء object من مكتبة retrofit والوصول له من أي مكان على مسوى التطبيق. لا ينبغي عمل subclass ل Singleton class حيث سيتوجب عليك جعل Constructor public وبعد هذا التغيير لم يعد class يسمى Singleton حيث يمكن إنشاء instance منه. بالإضافة إلى أنك قد تكون جعلت static data member مما يعني أن subclass سيكون مشترك مع superclass في نفس data member وقد يؤدي إلى نتائج غير مرغوب فيها غالبا. نجيب على التساؤل الذي طرحناه في بداية المقالة لماذا object اوحد فقط ؟ أتضح ولو بشكل جزئي من الأمثلة التي ذكرناها في بداية المقالة بعض فوائد Singleton, وهو أنه ستمر بك بعض الحالات لا تريد فيها أكثر من object وحيد تتعامل معه ومن هذه الحالات Toast كما ذكرنا ,و thread pools و caches و logging والقائمة تطول, فمتى ما رأيت أن class فيه static class or data member هي التي تحتاجها فقط فسيكون Singleton حل مناسب لك. المراجع: Head First Design Patterns http://www.javaworld.com/article/2073352/core-java/simply-singleton.html http://www.blackwasp.co.uk/gofpatterns.aspx http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples تم ترقية هذا الطرح المميز الى صفحة المقالات
  14. بسم الله الرحمن الرحيم بإذن الله ستكون هذه المقالة بداية لسلسلة من المقالات المتعلقة ب Design Patterns الأكثر أستخداما في بيئة Android. ونستهل هذه السلسلة ب Observer Design Pattern أو ما يسمى ب Publish-Subscribe (Pub-Sub) Pattern. في تعريف هذا Pattern في كتاب GoF : ولتبسيط التعريف نقول : عندما تريد تنبيه أو تحديث مجموعة objects عن حدوث تغيير في object أخر جميل جداً, دعنا نضرب مثالاً قبل أن نبدأ. - لدينا مدونة تنشر مقالات. - قمت أنت بالأشتراك في قائمة تقوم بتنبيهك في حال صدرت أي مقالة. - لم تعد مهتما بالمجال الذي تكتب هذه المدونة عنه, قمت بإلغاء الإشتراك منها. - قام مجموعة من أصدقائك بالإشتراك وإلغاء الإشتراك. المثال السابق هو ملخص لما للكود الذي سنقوم بكتابته في حال فهمك إياه فأنت قطعت شوط كبير في فهم هذا Design Pattern . بداية هذا هو UML الخاص بالكود الذي سنكتبه الان, سيتضح أكثر بعد كتابه الكود. جميل الان لنبدأ في كتابة الكود في البداية لدينا two interfaces الأول هو subjuct أو يمكنك أن تسميه puplisher والثاني observer أو يمكنك أن تسميه subscriber - إذا لم تكن تستخدم intrtfaces أثناء كتابتك لبرامجك فقد وضعت مرجع قد يفيدك في نهاية المقالة لمعرفة أهمية وأليه استخدام interfaces- public interface Subject { void addObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); public void setUpdate(String article); public String getUpdate(); } public interface Observer { void Update (); } ليس هناك الكثير من التفاصيل في إنشاء هذين interfaces لكن سنبدأ في التعليق عند مرحلة implementation دعنا نبدأ ب subjuct ونسوي له implementation في class Publisher public class Publisher implements Subject{ private List <Observer> observers ; String article; public Publisher() { observers = new ArrayList<>(); } @Override public void addObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers() { for(Observer ob : observers) ob.Update(); } @Override public String getUpdate() { return article; } @Override public void setUpdate(String article) { this.article = article ; notifyObservers(); } } في البداية قمنا بإنشاء List نستطيع أن نسميها قائمة المشتركين في هذا class الذي أفترضنا أنه مدونة لنشر المقالات, ومتغير String والذي هو عبارة عن المقالة التي نريد تنبيه متابعينا حال صدورها. لدينا ثلاث functions رئيسية لا يكاد يخلو Observer Design Pattern منها وهي : addObserver : ﻷضافة متابع جديد لهذا class أو كما يحلو لنا أن نسميه المدون removeObserver : لحذف متابع notifyObservers : لتنبيه المتابعين ونشر المقالة لهم ,وقد تختلف تصرفاتهم عندما يأتيهم التنبيه منهم يبدأ بقر ائتها أو إهمالهما أو حفظها لديه لغرض معين أو غير ذلك. لننتقل الان ل classes التي قامت بعمل implementation ل Observer وهم لا يختلفون كثيرا في implementation public class Reader implements Observer{ private String article ; private Subject sub ; public Reader(Subject blog) { article = "" ; sub = blog ; sub.addObserver(this); } @Override public void Update() { article = sub.getUpdate(); System.out.println("You :"+article); } } public class Reader implements Observer{ private String article ; private Subject sub ; public Reader(Subject blog) { article = "" ; sub = blog ; sub.addObserver(this); } @Override public void Update() { article = sub.getUpdate(); System.out.println("You :"+article); } } class reader والذي افترضت حين بدأت بكتابة المقالة أنه أنت , وclass الأخر هو Friend والذي افترضت أنه صديقك كما تلاحظون في كلا classes قمنا بعمل Override ل update تحضر المقالة و تقرأها, وفي داخل Constructor قمنا بإنشاء المتغيرات الخاصة بنا والانضمام إلى List المتابعين الخاصين بالمدونة public class PupSub { public static void main(String[] args) { // TODO code application logic here Publisher pup = new Publisher(); new Reader(pup); new Friend(pup); pup.setUpdate("Art1"); pup.setUpdate("Art2"); pup.setUpdate("Art3"); } } في البداية قمنا بإنشاء قائمة المتابعين الخاصة بالمدونة وكانت فارغة قم أنشأنا متابع من Reader class والذي سبق وافترضنا أنه أنت, ثم Friend class والذي هو صديقك. كلاكما متابع لهذه المدونة والمدون قد وضعكما في قائمته ويقوم بإخباركم عن كل جديد في هذه المدونة فور وصوله وأنتما تقومان بقراءة ما يأتيكما فور ما يأتي. ثم نشر المدون ثلاث تدوينات عبر setUpdate التي تستدعي notifyObservers ثم تقوم هذه function باستدعاء Update الخاصة بكل class من المتابعين ويقومون بدورهم بتحديث التدوينات التي لديهم عن طريق استداعاء getUpdate الخاصة بالمدون. قد تجد صعوبة في فهم هذا Pattern وغيره في المرة الأولى وهذا طبيعي. عليك تطبيقة وفهم أجزاءه وبعد ذلك سيتضح لك. وأخيرا, لماذا أستخدم Observer Design Pattern ؟ Observer Design Pattern يقلل من التداخل في الكود-Low Coupling- وهذا سيسهل عليك في حال رغبت في إضافة classes أو إزالتها, فجميع classes تعمل بشكل مستقل عن بعضها . المراجع: - http://informatic-ar.com/ال-coupling-وال-cohesion-في-تصميم-البرمجيات/ - https://sourcemaking.com/design_patterns/observer - Head First Design Patterns - البرمجة باستخدام Interfaces تم ترقية هذا الطرح المميز الى صفحة المقالات
  15. بسم الله الرحمن الرحيم في java لدينا Throwable class لديه Error و Exception ك subclass . Error class لا تستطيع معالجة أخطائه أو على الأقل أثناء Runtime لذلك سيكون حديثنا عن Exception class ماهو Exception ؟ هو خطأ-يقال في بعض الأحيان حدث ويقال استثناء- يحدث أثناء تشغيل البرنامج يقطع عملية تنفيذ الأوامر الخاصة بالبرنامج. الان لنتطرق لعملية التحكم في الخطأ ومعالجته -catch and handle exceptions- بداية نبدأ مع مفهوم try-catch وكالعادة نشوف المفهوم بعدين نشرحه public static void main(String[] args) { int a = 5 ; int b = 0 ; System.out.println(a/b); } نلاحظ أن عندنا عملية قسمة لكن على صفر بمعنى أن الناتج راح يكون عدد لا نهائي -infinity- وهذا يعطيني exception من نوع ArithmeticException ليتوقف البرنامج عن العمل -crash-و الحل طبعا أنك ما تقسم على صفر لكن سنحاول أن لا يحدث crash للبرنامج باستخدام try-catch block public static void main(String[] args) { int a = 5 ; int b = 0 ; try{ System.out.println(a/b); }catch(ArithmeticException e){ System.out.println("divid by zero!"); } } نلاحظ أن لدينا two blocks الأول try والثاني catch يبدأ البرنامج في تنفيذ مابداخل try في حال نجح في تنفيذه بدون exception فأنه يتجاوز catch بدون تنفيذها وفي حال ظهر exception فأنه يتوقف عن أكمل تنفيذ try وينتقل إلى catch . جميل جدا لكن ماذا لو كان هناك احتمالية وجود أكثر من exception داخل try block أو كان لدي خطأ لا أعرف لأي نوع من exceptions ينتمي -في حال وضعت catch ل ArithmeticException ثم ظهر لك NullPointerException فإن catch block لن يستطيع التعامل معه- ففي هذه الحالة أستخدم try مع أكثر من catch block كما في المثال public static void main (String args[]){ int x = 0 ; Integer y = null ; try { System.out.println(5/x); x = y ; }catch(ArithmeticException e){ System.out.println("catch "+e.getMessage()); }catch (NullPointerException e) { System.out.println("catch "+e.getMessage()); } } أما في حال لم تعرف هذا خطأ لأي نوع من exceptions ينتمي فتستخدم catch block مع Exception ك parameter لأن جميع ال Exceptions هم subclass من Exception وهذا أحد تطبيقات مبدأ polymorphism , فعلى سبيل المثل سنكرر المثال سابق ولكن نستبدل NullPointerException ب Exception public static void main (String args[]){ int x = 0 ; Integer y = null ; try { System.out.println(5/x); x = y ; }catch(ArithmeticException e){ System.out.println("catch "+e.getMessage()); }catch (Exception e) { System.out.println("catch "+e.getMessage()); } } في هذه الحالة في حال ظهور أي خطأ غير ArithmeticException سوف يقوم Exception بتنفيذ catch الخاصة به وهنا يأتي سىؤال لماذا لا أكتفي ب Exception ؟ إذا كنت تريد أن تعامل كل خطأ بطريقة معينة-كإظهار رسالة معينة أو تنبيه وما إلى ذلك- فيفضل استخدام أكثر من catch block لكل catch ال Exception الخاص بها , أما في حال كان يهمك أن لا يحدث crash لتطبيق فسيكون حل مناسب لك . جميل الآن نحن إما أن ننفذ try أو catch لكن ماذا لو كان لدينا أمر ما نريد أن ننفذه في حال تم تنفيذ try أو catch؟ الطريقة الصحيحة ليس أن تكتبه في كلا blocks لكن أن تستخدم جملة finally كما في المثال tring args[]){ int x = 0 ; Integer y = null ; try { System.out.println(5/x); x = y ; }catch(ArithmeticException e){ System.out.println("catch "+e.getMessage()); }catch (Exception e) { System.out.println("catch "+e.getMessage()); }finally { System.out.println("I will show up anyway"); } } ف finally block سوف يظهر على كل حال , في حال تنفيذ أي من try أو catch . إذا لنلخص قسم try & catch & finally نستخدم try و catch واحده في حال كان هناك خطأ واحد نستخدم try وأكثر من catch في حال كان هناك أحتمالية وجود أكثر من خطأ نستخدم finally في حال أردنا تنفيذ أمر معين في كل الحالات . نستخدم Exception ك نوع ل exception في حال كنا فقط نريد أن لا يحدث crash أو كانت جميع الأخطاء لها نفس التعامل -كأن تظهر جميع الأخطاء رسالة Error- طيب ماذا لو أردت أن تضع exception في حال قام أحدهم بخطأ ما , وليكن قام مدخل البيانات بإدخال راتب أحد الموظفين صفر فيكون ال exception بهذه الطريقة public static void main (String args[]) throws Exception{ int Salary = 0 ; try{ if(Salary<= 0 ){ throw new ArithmeticException(); } }catch(ArithmeticException e){ System.out.println("R U serious!"); } } قد تكون لاحظت أننا طوال هذه المقالة نستخدم exception الخاصة بنفس اللغة , لكن ماذا لو أردنا بناء exception الخاص بنا بحيث عندما يدخل مدخل البيانات راتب خاطئ يكون هنا SalaryException الأمر بسيط كل ما تحتاجه هو بناء class يعمل extends ل Exception وتضع الرسالة المراد إخراجها حال ظهور exception public class SalaryException extends Exception { public SalaryException() { super("Salary not valid"); } } public class Salary { public static void main (String args[]) throws Exception{ int Salary = 0 ; try{ if(Salary<= 0 ){ throw new SalaryException(); } }catch(SalaryException e){ System.out.println(e); } } } في هذه الحالة سوف يظهر لك Salary not valid . Exception class لديه تصنيفين رئيسيين Checked Exception و Unchecked Exception - Checked Exception هي Exception يشير لها Compiler أثناء وقت تنفيذ الكود -Compile time- ويجبر المبرمج على معالجتها ,مثل IOException SQLException DataAccessException ClassNotFoundException InvocationTargetException MalformedURLException - Unchecked Exception على العكس فهذه Exception تكتشف أثناء عمل البرنامج -RuntimeException- مثل NullPointerException ArrayIndexOutOfBound IllegalArgumentException IllegalStateException فحين تقوم بكتابة function وتضع داخلها throw لأحد أنواع Checked Exception فسيجبرك Compiler إما على declared exceptions باستخدام throws keyword أو استخدام try-catch كما في المثال class Test { static void Nmae (String name) throws IOException{ if (name == "") throw new IOException("Empty name"); } public static void main(String[] args) throws IOException { Nmae (""); } } نلاحظ أنك ستضع أيضا throws داخل أي function تستدعي فيها function التي بداخلها exceptions كم يحدث هنا في main بالمناسبة Error class الذي تحدثنا عنه بداية المقالة جميع أخطائه تصنف Unchecked Exception هذا وصلى الله وسلم وبارك على نبينا محمد مراجع : https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html https://crunchify.com/better-understanding-on-checked-vs-unchecked-exceptions-how-to-handle-exception-better-way-in-java/ http://beginnersbook.com/2013/12/throws-keyword-example-in-java/

عالم البرمجة

عالم البرمجة مقالات برمجة و دورات مجانية لإحتراف البرمجة هدفنا تبسيط البرمجة ونشرها بيد الكل بشكل ممتع ومتطور ومحدث بإستمرار لمواكبة جديد تطورات البرمجة الحديثة و المتقدمة بدون مقابل