عنوان:

‫استفاده از SignalR در اندروید


نویسنده: سیروان عفیفی
تاریخ: ۱۳۹۳/۰۷/۲۱ ۱۳:۲۵
آدرس: www.dntips.ir
همانطور که مطلع هستید، بخش سورس باز مایکروسافت برای برنامه‌نویس‌های جاوا نیز SDKی جهت استفاده از SignalR ارائه کرده است. در اینجا می‌توانید مخزن کد آن را در گیت‌هاب مشاهده کنید. هنوز مستنداتی برای این SDK به صورت قدم به قدم ارائه نشده است. لازم به ذکر است که مراجعه به قسمت‌های نوشته شده در اینجا نیز می‌تواند منبع خوبی برای شروع باشد. در ادامه نحوه استفاده از این SDK را با هم بررسی خواهیم کرد.
ابتدا در سمت سرور یک Hub ساده را به صورت زیر تعریف می‌کنیم:
public class ChatHub : Hub
{
        public void Send(string name, string message)
        {
            Clients.All.messageReceived(name, message);
        }
}
برای سمت کلاینت نیز یک پروژه Android Application داخل Eclipse به صورت زیر ایجاد می‌کنیم:

خوب، برای استفاده از SignalR در پروژه‌ی ایجاد شده باید کتابخانه‌های زیر را به درون پوشه libs اضافه کنیم، همچنین باید ارجاعی به کتابخانه Gson نیز داشته باشیم.

قدم بعدی افزودن کدهای سمت کلاینت برای SignalR می‌باشد. دقت داشته باشید که کدهایی که در ادامه مشاهده خواهید کرد دقیقاً مطابق دستورالمعل‌هایی است که قبلاً مشاهده کرده‌اید. برای اینکار داخل کلاس MainActivity.java کدهای زیر را اضافه کنید:

Platform.loadPlatformComponent( new AndroidPlatformComponent() );
HubConnection connection = new HubConnection(DEFAULT_SERVER_URL);
HubProxy hub = connection.createHubProxy("ChatHub");
connection.error(new ErrorCallback() {
    
    @Override
    public void onError(final Throwable error) {
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
        
    }
});
hub.subscribe(new Object() {
    @SuppressWarnings("unused")
    public void messageReceived(final String name, final String message) {
        
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(), name + ": " + message, Toast.LENGTH_LONG).show();
            }
        });
    }
});
connection.start()
.done(new Action<Void>() {
    
    @Override
    public void run(Void obj) throws Exception {
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(), "Done Connecting!", Toast.LENGTH_LONG).show();
            }
        });
    }
});
connection.received(new MessageReceivedHandler() {
    @Override
    public void onMessageReceived(final JsonElement json) {
        runOnUiThread(new Runnable() {
            public void run() {
                JsonObject jsonObject = json.getAsJsonObject();
                JsonArray jsonArray =jsonObject.getAsJsonArray("A");
                Toast.makeText(getApplicationContext(), jsonArray.get(0).getAsString() + ": " + jsonArray.get(1).getAsString(), Toast.LENGTH_LONG).show();
            }
        });
    }
});

همانطور که مشاهده می‌کنید توسط قطعه کد زیر SKD مربوطه در نسخه‌های قدیمی اندروید نیز بدون مشکل کار خواهد کرد:

Platform.loadPlatformComponent( new AndroidPlatformComponent() );

در ادامه توسط متد createHubProxy ارجاعی به هابی که در سمت سرور ایجاد کردیم، داده‌ایم:

HubProxy hub = connection.createHubProxy("ChatHub");

در ادامه نیز توسط یک روال رویدادگردان وضعیت اتصال را چک کرده‌ایم. یعنی در زمان بروز خطا در نحوه ارتباط یک پیام بر روی صفحه نمایش داده می‌شود:

connection.error(new ErrorCallback() {
    
    @Override
    public void onError(final Throwable error) {
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
        
    }
});
در ادامه نیز توسط کد زیر متد پویایی که در سمت سرور ایجاد کرده بودیم را جهت برقراری ارتباط با سرور اضافه کرده‌ایم:
hub.subscribe(new Object() {
    @SuppressWarnings("unused")
    public void messageReceived(final String name, final String message) {
        
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(), name + ": " + message, Toast.LENGTH_LONG).show();
            }
        });
    }
});
برای برقراری ارتباط نیز کدهای زیر را اضافه کرده‌ایم. یعنی به محض اینکه با موفقیت اتصال با سرور برقرار شد پیامی بر روی صفحه‌نمایش ظاهر می‌شود:
connection.start()
.done(new Action<Void>() {
    
    @Override
    public void run(Void obj) throws Exception {
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(), "Done Connecting!", Toast.LENGTH_LONG).show();
            }
        });
    }
});
در نهایت نیز برای نمایش اطلاعات دریافت شده کد زیر را نوشته‌ایم:
connection.received(new MessageReceivedHandler() {
    @Override
    public void onMessageReceived(final JsonElement json) {
        runOnUiThread(new Runnable() {
            public void run() {
                JsonObject jsonObject = json.getAsJsonObject();
                JsonArray jsonArray =jsonObject.getAsJsonArray("A");
                Toast.makeText(getApplicationContext(), jsonArray.get(0).getAsString() + ": " + jsonArray.get(1).getAsString(), Toast.LENGTH_LONG).show();
            }
        });
    }
});
همانطور که عنوان شد کدهای فوق دقیقاً براساس قواعد و دستورالعمل استفاده از SignalR در سمت کلاینت می‌باشد.

نظرات

  • مهمان در ۱۳۹۳/۰۷/۲۱ ۱۵:۴۶
    ممنون - بسیار عالی
    یک سئوال: آیا از این طریق میشه به همون قابلیت‌های Push Notification در GCM دست یافت؟
    و اینکه چقدر این روش قابل اتکا هست؟
    • سیروان عفیفی در ۱۳۹۳/۰۷/۲۱ ۱۶:۵۱
      دقیقاً یکی از استفاده‌هایی که برای خودم داره بحث Push Notification و ارسال پیام به کاربران متصل هست.
  • علی ساری در ۱۳۹۴/۰۴/۰۳ ۹:۴۵
    با تشکر از مطلب خوبتون
    در طی این مدت بازخوردهای شما از سیگنال آر در پروژ هایی که ازش استفاده کردید چطور بوده؟
    چند برابر بقیه سرویس‌ها مثل gcm یا parse بار روی سرور میاره؟
    و اینکه مزیت‌های سیگنال آر در مقایسه با این 2 سرویس چیه (البته parse که میدونم رایگان نیست)
    ممنون
    • سیروان عفیفی در ۱۳۹۴/۰۴/۰۳ ۱۲:۲۰
      یکی از مزایای استفاده از SignalR جهت ارسال push notification قابلیت سفارشی‌سازیه، یعنی شما با استفاده از قابلیتی مثل backplan به راحتی می‌تونید messageها رو به سرورهای دیگه فوروارد کنید همچنین می‌تونید از یکسری تکنیک برای داشتن performance بهتر استفاده کنید به طور مثال کاهش سایز اشیاء سریالایز شده و ...
      در مجموع انعطاف این روش خیلی بیشتره
  • محمد رعیت پیشه در ۱۳۹۵/۰۶/۰۴ ۱۰:۲۷
    طبق این لینک درست متوجه شدم که کلاینت جاوا از روش websocket پشتیبانی نمی‌کنه؟ من سورسش رو چک کردم توی روش هاش فقط اینها بود.
    public enum TransportType {
         ServerSentEvents,
         LongPolling,
         Auto
    }

    • سیروان عفیفی در ۱۳۹۵/۰۶/۰۴ ۱۱:۳۵
      ظاهراً پیاده‌سازی WebSocket  انجام شده.
      • محمد رعیت پیشه در ۱۳۹۵/۰۶/۰۸ ۹:۴
        متاسفانه این پیاده سازی خیلی قدیمه و مشکلات جدی هم داره، مثلا لینک ارتباط به سوکت رو با http شروع کردند در حالی که باید با ws باشه! نمیدونم بر چه اساسی چنین اشتباهاتی رو در کدنویسی رها کردند.
        بعضی موارد رو خودمون اصلاح کردیم و سوکت متصل میشه اما در صورتی که بخوایم همراه درخواست اطلاعات Bearer Token رو هم بفرستیم اونوقت دیگه سوکت متصل نمیشه (خطای InvalidFrameException سمت کلاینت جاوا ) !   یعنی SignalR هیچ راهکاری برای ارتباط با اندروید به روش سوکت نداره؟
        • محسن خان در ۱۳۹۵/۰۶/۰۸ ۹:۲۰
          نمیدونم بر چه اساسی چنین اشتباهاتی رو در کدنویسی رها کردند. 
          شاید اگر بهشون گزارش بدید در همون محل، بهتر باشه و مؤثرتر.
          • محمد رعیت پیشه در ۱۳۹۵/۰۶/۰۸ ۹:۳۵
            بعضی هاشو بقیه گزارش دادند اما انگار دیگه پشتیبانی نمیشه.
  • امین بحیرائی در ۱۳۹۶/۰۹/۲۲ ۱۸:۴۲
    از این روش همچنان میشه استفاده کرد یا نه؟ ظاهرا طبق گفته دوستان مشکلاتی داره
    یه سوال دیگه هم اینکه اگه بخوایم برای یک کلاینت اندروید خاص پیام بفرستیم امکان پذیر هست یا خیر؟ و چطور باید اون کلاینت رو شناسایی کرد؟
    • مریم احمدیان در ۱۳۹۶/۱۱/۰۳ ۱۶:۴۴
      مشکلی که دوستان در نظرهای قبلی مطرح کردند برای زمانی هست که شما از توکن برای شناسایی کلاینت استفاده می‌کنید و زمانی که کلاینت درخواستی به سمت سرور ارسال میکنه به همراه درخواست در بخش هدر اطلاعات توکن ارسال میشه به این صورت
       headers: { 'Authorization': 'Bearer ' + Token }
      تا اینجا مشکل خاصی وجود نداره و مشکل دقیقا زمانی اتفاق میوفته که مثلا در اپلیکیشن شما بخش چت یا نوتیفیکیشن بلادرنگ وجود داره و شما ناچارید از signalr و یا socket.io استفاده کنید. در صورت استفاده از هر کدام شما نمیتوانید همراه درخواست توکنی ارسال کنید بهترین راه برای حل این مشکل اینه که پیاده سازی جوری باشه که توکن رو از طریق url ارسال کنیم؛ کاری که اینستاگرام انجام داده 
      https://api.instagram.com/v1/users/{user-id}/?access_token=ACCESS-TOKEN
      مانند نمونه‌ای که در اینجا  پیاده سازی شده...