Khi bạn làm 1 ứng dụng web bằng Java và cần phải tương tác tốt với các thiết bị cầm tay như điện thoại di động, smart-phone, tablet...trên trang web để trả về đúng phiên bản giao diện web phù hợp cho thiết bị đó.

Khi bạn muốn làm 1 hệ thống thống kê chi tiết website của bạn được vào bằng những thiết bị gì, model gì, của hãng sản xuất nào, kích thước màn hình bao nhiêu. Và từ những con số liệu đó, bạn có thể đưa ra những tiêu chí đánh giá và quyết định đẩy mạnh ở mảng nào cho website của mình.

Có rất nhiều thư viện open-source giúp bạn detect và hoàn thành tốt chức năng này. Tuy vậy, trong bài viết tôi chỉ giới thiệu với bạn 1 thư viện mà tôi vẫn dùng từ trước tới nay và cảm thấy rất hài lòng với nó. Đó là WURFL (Wireless Universal Resource FiLe).


1. WURFL (Wireless Universal Resource FiLe).

WURFL (Wireless Universal Resource FiLe) là 1 kho mô tả thiết bị (Device Description Repository - DDR). Nó là 1 framework cho phép các ứng dụng ánh xạ các HTTP request tới các mô tả của thiết bị di động khi request trang.

Vai trò WURFL đã mở rộng không chỉ là kho mô tả cho các thiết bị di động mà trở thành 1 framework để hỗ trợ việc detect các thông tin gửi lên từ HTTP Client (bao gồm desktop, thiết bị di động, tablet...).

Nhờ có WURFL, các trang web và ứng dụng dành cho mobile có thể:
  • Lập trình trừu tượng hóa đi sự khác biệt của các thiết bị (đơn giản nhất, hiệu quả nhất).
  • Tránh sự sửa đổi ứng dụng bất cứ khi nào có 1 thiết bị mới được thêm vào.
  • Tránh việc phải theo dõi các model thiết bị mới được sản xuất trên thị trường. (Điều này có nghĩa là nhóm phát triển WURFL sẽ cập nhật cho bạn) .

WURFL là 1 cơ sở dữ liệu toàn cầu của tất cả các thiết bị và các API để khai thác thông tin chương trình.

Bạn hoàn toàn có thể tự thêm mới các model thiết bị trong file wurfl.xml. Bạn có thể tìm hiểu cấu trúc và tự phát triển tại đây.

Trên trang web của WURLF, có 2 mục mà ta cần quan tâm nhiều nhất là mục Capabilities WURFL APIs.

Capacities là 1 Map, nó sẽ chứa các key và value mà ta cần khi sử dụng để detect.
WURFL APIs là mục chứa link download thư viện và DDR (file wurfl.xml), chứa Javadoc để ta có thể dễ dàng tra cứu và thực nghiệm.

Bạn  tải bộ thư viện WURFL 1.4.4 về và thêm vào trong Netbeans. Để quản lý thư viện tốt và có khả năng sử dụng lại nhiều lần, bạn nên đọc bài viết Làm thế nào để quản lý thư viện hiệu quả trong Netbeans?

2. Làm việc với WURFL

Nếu bạn đã rành về Web và hiểu rõ Life-cycle của 1 quá trình Request-Response từ máy client gửi lên Web Server như thế nào thì ắt hẳn bạn sẽ biết về User Agent. Và dựa vào User Agent được gửi lên kèm request từ máy client ta có thể biết được client đó dùng trình duyệt gì, hệ điều hành gì, phiên bản bao nhiêu, v.v...Nếu bạn chưa biết về User Agent, bạn có thể đọc qua các link sau:


Để tiện cho việc test các model thiết bị khác nhau, tôi sẽ phải cần 1 công cụ giải lập thay đổi User Agent trên desktop sang mobile. Tôi dùng add-on: User-Agent-Switcher cho Firefox (nếu bạn dùng Chrome thì bạn có thể search trên Store của Chrome, tôi không dùng Chrome nên không biết).

Khi cài đặt User-Agent-Switcher cho Firefox, nó chỉ có sẵn 1 vài User Agent mặc định, bạn cần tải thêm database cho nó.



Như bạn thấy sau khi import cơ sở dữ liệu mới, User Agent của chúng ta giờ đã khá phong phú và đủ để test rồi.

Tiếp theo, làm việc với Netbeans, tôi tạo 1 dự án Java Web Application và đặt tên là MobileDeviceDetector.





Ok, Finish, tôi vừa tạo xong dự án Web App, cấu trúc của nó như sau:



Lúc trước, bạn đã tải bộ thư viện WURFL máy của bạn, bạn vào nơi mà bạn lưu file tải về, giải nén và copy file wurfl.zip vào folder WEB-INF trong dự án:



Tạm thời như thế đã, tiếp theo, tôi tạo 1 Java Servlet tên là DeviceDetector, servlet này có nhiệm vụ hứng request và xử lý request đó trả về thông tin thiết bị của client.





Mã mặc định mà Netbeans generates ra bạn xóa đi, để lại mã setContentType(); thôi.



Tiếp theo tôi add các thư viện WURFL (các file .jar, source, javadoc) vào trong dự án. Cấu trúc thư viện sẽ như sau:


Và bây giờ tôi sẽ viết code để detect và xử lý các request hứng được từ phía client gửi lên như sau:



Source Code Servlet: DeviceDetector

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.wurfl.core.Device;
import net.sourceforge.wurfl.core.WURFLHolder;
import net.sourceforge.wurfl.core.WURFLManager;

/**
 *
 * @author Code4LifeVn
 */
@WebServlet(urlPatterns = {"/DeviceDetector"})
public class DeviceDetector extends HttpServlet {

    /**
     * Processes requests for both HTTP
     * GET and
     * POST methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        // => getServletContext() trả về 1 đối tượng ServletContext, ServletContext định nghĩa 1 tập các phương thức để sử dụng giao tiếp với Servlet container như lấy về MIME Type. Dispatch request...
        // => gọi phương thức getAttribute() trả về thuộc tính của Servlet container có tên là WURFLHolder.class.getName() nếu không có sẽ trả về null. Đảm bảo Holder được nạp
        WURFLHolder holder = (WURFLHolder) getServletContext().getAttribute(WURFLHolder.class.getName());

        // => lấy đối tượng WURFLManager từ holder (WURFLHolder giữ instance của WURFLManager)
        WURFLManager manager = holder.getWURFLManager();

        // => trả về 1 đối tượng Device từ HttpServletRequest bằng cách gọi phương thức getDeviceForRequest();
        // => Interface Device đại diện cho 1 thiết bị được kết nối tới. Nó cho phép ta có được các thông tin hữu ích của thiết bị
        Device deviceForRequest = manager.getDeviceForRequest(request);

        // => gọi phương thức getCapabilities() để lấy về 1 Map các capabilities
        // => Các key lấy ra phải đúng với các key có trên trang http://wurfl.sourceforge.net/help_doc.php
        Map<String, String> capabilities = deviceForRequest.getCapabilities();

    }

    // 
    /**
     * Handles the HTTP
     * GET method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * POST method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// 
}

Tôi dừng lại ở mã lấy ra 1 Map các capabilities, bởi vì lúc này để lấy ra các thông tin qua key của Map đó, chúng ta phải sử dụng http://wurfl.sourceforge.net/help_doc.php để tra cứu và truyền vào các key chính xác với thông tin đang cần. Dựa vào các bảng thống kê các Capability Name, Type, Description bạn truyền giá trị khi muốn get thông tin nào đó từ Map đã lấy ra trong code trên. Và tôi hoàn thiện đoạn mã như sau: (bạn có thể test các Capability Name khác tùy mục đích của bạn).



Source Code Servlet (đầy đủ): DeviceDetector

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.wurfl.core.Device;
import net.sourceforge.wurfl.core.WURFLHolder;
import net.sourceforge.wurfl.core.WURFLManager;

/**
 *
 * @author Code4LifeVn
 */
@WebServlet(urlPatterns = {"/DeviceDetector"})
public class DeviceDetector extends HttpServlet {

    /**
     * Processes requests for both HTTP
     * GET and
     * POST methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        // => getServletContext() trả về 1 đối tượng ServletContext, ServletContext định nghĩa 1 tập các phương thức để sử dụng giao tiếp với Servlet container như lấy về MIME Type. Dispatch request...
        // => gọi phương thức getAttribute() trả về thuộc tính của Servlet container có tên là WURFLHolder.class.getName() nếu không có sẽ trả về null. Đảm bảo Holder được nạp
        WURFLHolder holder = (WURFLHolder) getServletContext().getAttribute(WURFLHolder.class.getName());

        // => lấy đối tượng WURFLManager từ holder (WURFLHolder giữ instance của WURFLManager)
        WURFLManager manager = holder.getWURFLManager();

        // => trả về 1 đối tượng Device từ HttpServletRequest bằng cách gọi phương thức getDeviceForRequest();
        // => Interface Device đại diện cho 1 thiết bị được kết nối tới. Nó cho phép ta có được các thông tin hữu ích của thiết bị
        Device deviceForRequest = manager.getDeviceForRequest(request);

        // => gọi phương thức getCapabilities() để lấy về 1 Map các capabilities
        // => Các key lấy ra phải đúng với các key có trên trang http://wurfl.sourceforge.net/help_doc.php
        Map&<String, String> capabilities = deviceForRequest.getCapabilities();
        String brand_name = capabilities.get("brand_name");
        String model = capabilities.get("model_name");
        String modelExtra = capabilities.get("model_extra_info");
        String device_os = capabilities.get("device_os");
        String device_os_version = capabilities.get("device_os_version");
        String mobile_browser = capabilities.get("mobile_browser");
        
        String requestInfo = String.format("Bạn sử dụng điện thoại %s %s %s - Hệ điều hành %s %s - Trình duyệt %s", brand_name, model, modelExtra, device_os, device_os_version, mobile_browser);
        
        PrintWriter out = response.getWriter();        
        out.println(requestInfo);
        out.close();
    }

    // 
    /**
     * Handles the HTTP
     * GET method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * POST method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// 
}

Cuối cùng là chạy test thử, để servlet này chạy được, chúng ta cần phải cấu hình cho nó trong file web.xml như sau:

File web.xml


    
        
            net.sourceforge.wurfl.core.web.WURFLServletContextListener
        
    
    
        wurfl
        /WEB-INF/wurfl.zip
    


Sau khi cấu hình trong file web.xml xong, bạn Run Servlet DeviceDetector này, nó sẽ được chạy trên trình duyệt của bạn. Bây giờ, chúng ta sử dụng add-on User-Agent-Switcher để đổi User Agent phục vụ cho việc test, ví dụ tôi đổi User Agent sử dụng iPhone 3, Servlet sẽ trả về thông tin sau:



Tương tự, với các thiết bị khác:


Hy vọng, qua bài viết này bạn sẽ áp dụng tốt kỹ thuật detect để giải quyết những vấn đề liên quan khi làm việc với thiết bị di động, tablet,...

5 nhận xét:

  1. EmEm bị sai ngay chổ

    Map&ltString, String&gt capabilities = deviceForRequest.getCapabilities();

    mặc dù đã add hết lib của wurfl on mong anh giúp đở

    Trả lờiXóa
  2. @Duy : em đã đặt thư viện thiết bị wurf.zip vào trong WEB-INF chưa?

    Trả lờiXóa
  3. Em đã đặt rồi ạ, nhưng nó vẫn bị lổi, làm theo hướng dẫn của anh step by step luôn vì em mới làm quen với Java thôi trước giờ em chỉ biết C#. Nếu không phiền thì anh có thể hổ trợ em bằng Team Viewer được không ạ, cảm ơn anh !

    Trả lờiXóa
  4. Em đã sửa được rồi, nguyên nhân sai là do em copy code trên web về. Anh xem lại dòng code 46 nhé: dấu "<" mà hiển thị code là &lt thay vì Map thì sẽ là Map&ltString... mong các bạn sau không bị sai như này

    Trả lờiXóa
  5. À ok em, do cái format code nó hợi "dị" nên nhiều lúc bị lỗi ở các ký tự <> :D để anh sửa lại cho các bạn khác sử dụng.

    Trả lờiXóa

 

code4lifevn team

Thanh niên nghiêm túc :)

Name: Manh Do

Age: years old

Job: Senior Java and Mobile Developer

Country: VietNam

Name: Hung Nguyen

Age: years old

Job: Android Developer

Country: VietNam

Name: Trung PH

Age: years old

Job: Senior iOS and Android Developer

Country: VietNam

Name: Điệp NT

Age: years old

Job: Senior .Net and Android Developer

Country: VietNam