Sử dụng XML và JSON với Android, Phần 2: Cung cấp các ứng dụng Android lai với JSON

Trộn lẫn JavaScript, JSON, các hàm gọi lại, mã Java Android - SDK cho các ứng dụng di động linh hoạt

Loạt bài viết hai phần này xem xét các kỹ thuật dùng để xử lý hai trong số các định dạng dữ liệu phổ biến nhất được sử dụng trên Internet hiện nay, đó là XML (Ngôn ngữ đánh dấu mở rộng) và JSON (Ký hiệu đối tượng JavaScript), trên nền tảng Android. Phần 1 đã trình bày các vấn đề cơ bản về XML và JSON. Phần 2 xem xét cách thức trong đó mã JavaScript được lưu trữ trên máy chủ - Webkit trao đổi dữ liệu với mã Java™ trong một ứng dụng Android. Bài viết này tập trung vào JSON, được trình bày như là cách tiếp cận có khả năng nhất và linh hoạt nhất.

Frank Ableson, Thiết kế phần mềm

Sau khi sự nghiệp trong đội bóng rổ của trường cao đẳng kết thúc mà không có một hợp đồng dài hạn nhiều năm chơi cho đội LA Lakers, Frank Ableson đã chuyển chí hướng của mình sang thiết kế phần mềm máy tính. Ông thích giải quyết các vấn đề phức tạp, nhất là trong các lĩnh vực truyền thông và lập giao diện phần cứng. Khi không làm việc, ông dành thời gian với người vợ Nikki và con cái. Bạn có thể gặp Frank tại địa chỉ frank@cfgsolutions.com.



12 07 2011

Để chạy mã ví dụ kèm theo bài viết này bạn nên cài đặt phiên bản 1.5 hoặc mới hơn của Android SDK cùng với Eclipse. Để tìm hiểu thêm về việc thiết lập môi trường của bạn xin vui lòng truy cập vào trang web của các nhà phát triển Android. Xem phần Tài nguyên để có các liên kết.

Mở đầu

Thật khó để tưởng tượng được một lĩnh vực công nghệ phổ biến hơn lĩnh vực điện thoại di động. Nhiều nền tảng đang tranh giành nhãn hiệu hàng đầu của ngành công nghiệp này về mặt bán hàng và tranh giành tâm tưởng của khách hàng. Các thiết bị là các mẫu vật kỹ thuật tiên tiến, nhưng nhiên liệu làm nên sự nổi tiếng của chúng là trải nghiệm của người dùng, mang lại bởi một số lượng rất lớn các ứng dụng có sẵn dành cho những nền tảng. Cụ thể hơn, các nền tảng iPhone và Android là những thiết bị mới nhất tranh chấp tay đôi giành trái tim và ví tiền của khách hàng doanh nghiệp cũng như người tiêu dùng.

Các từ viết tắt thường dùng

  • API: Application Programming Interface-Giao diện lập trình ứng dụng.
  • CSS: Cascading Style Sheets-Các bảng định kiểu chồng nhau
  • HTML: HyperText Markup Language-Ngôn ngữ đánh dấu siêu văn bản.
  • IDE: Integrated Development Environment-Môi trường phát triển tích hợp.
  • SDK: Software Developer Kit-Bộ công cụ của nhà phát triển phần mềm.
  • UI: User Interface-Giao diện người dùng.
  • XML: Extensible Markup Language-Ngôn ngữ đánh dấu mở rộng.

Đa số các ứng dụng có sẵn cho những người dùng di động được các nhà phát triển di động viết thông qua các SDK (Bộ công cụ của nhà phát triển phần mềm) lõi do các nhà cung cấp nền tảng tương ứng cung cấp. Sự phổ biến của các thiết bị di động kết hợp với một số lượng lớn các lập trình viên công nghệ web tài năng, những người đã kiếm được thứ hạng của họ qua Web trong suốt thập kỷ qua đã dẫn đến một mô hình ứng dụng mới —ứng dụng lai, một ứng dụng sử dụng cả hai giao diện trình duyệt web lẫn các thành phần di động nguyên gốc. Các ứng dụng lai hiện có cho cả iPhone và Android, mặc dù trọng tâm trong bài viết này là về các ứng dụng lai Android và việc sử dụng JavaScript và JSON.

Các ứng dụng lai được xây dựng xung quanh máy WebKit có trong điều khiển WebView của Android. Đây là một widget (tiện ích) giao diện người dùng, trưng ra các khả năng của WebKit cho một lập trình viên Android. Thành phần điều khiển này có thể được sử dụng để biểu hiện các trang web từ xa trong một ứng dụng, để cung cấp một trải nghiệm giao diện người dùng quen thuộc cho các nhà phát triển cũng như người dùng và để tận dụng môi trường JavaScript mạnh mẽ và linh hoạt trong một ứng dụng Android nguyên gốc.

Các ứng dụng lai thường tận dụng tiện ích của WebView để khai thác sức mạnh của WebKit chủ yếu cho các phần tử giao diện người dùng, tuy nhiên các ứng dụng lai làm nhiều thứ hơn là chỉ hiển thị một số HTML trong tiện ích. Các ứng dụng lai linh hoạt — có nhiều chức năng chứa trong SDK của Android được kết hợp với các công nghệ web của HTML, CSS và JavaScript, làm cho các ứng dụng lai chỉ còn bị hạn chế bởi trí tưởng tượng của bạn mà thôi. Để làm tăng thêm hiểu biết về một ứng dụng lai, bài viết này xem xét một ứng dụng mẫu có tên AndroidJSON, triển khai thực hiện một số tương tác giữa một Activity (Hoạt động), một WebView (Khung nhìn Web) và sử dụng JSON để trao đổi dữ liệu. Ứng dụng này trình diễn một số tương tác giữa Hoạt động và HTML và JavaScript được WebView lưu trữ trên máy chủ với các tính năng chính là một máy tính (calculator) dùng JavaScript.

Trước tiên, bạn sẽ xem xét việc nhúng một máy WebKit trực tiếp vào một ứng dụng Android.


Một máy tính cầm tay dùng JavaScript nhúng trong Android

Đa số các ứng dụng Android dựa trên SDK chứa một hay nhiều công việc triển khai thực hiện của lớp Activity. Lớp Activity về bản chất là một màn hình hoặc trang có chứa các phần tử giao diện người dùng được người sử dụng ứng dụng trải nghiệm.

Lớp Activity hiển thị một tập hợp các phần tử giao diện người dùng do lập trình viên định nghĩa như các nút, các nhãn, các hộp nhập văn bản, danh sách các nút tròn và v.v.. Tất cả các mục dự kiến này đều có mặt trong SDK của Android. Ngoài các chủ đề giao diện người dùng này, có một widget (tiện ích) đặc biệt, được gọi đơn giản là WebView.

Máy tính cầm tay dùng JavaScript trình diễn mối quan hệ cộng sinh giữa môi trường Java của Activity và môi trường JavaScript của WebView. Ứng dụng này vượt ra ngoài việc chỉ cần yêu cầu WebView hiển thị nội dung HTML — nó thực sự kết nối môi trường Java để cung cấp chức năng cho môi trường JavaScript và trong khi làm như vậy nó kết hợp chặt chẽ cả hai môi trường, cho phép một trải nghiệm người dùng độc đáo. Khi hai môi trường được kết nối, dữ liệu được trao đổi dưới dạng JSON để cung cấp những tính năng khác nhau, tất cả đều được giải thích kỹ lưỡng trong bài viết này. Hãy bắt đầu bằng cách xem xét cách máy tính cầm tay dùng JavaScript sử dụng tiện ích WebView.

Trước khi đi sâu vào các chi tiết cụ thể về cách xây dựng ứng dụng, cần mất chút thời gian để xem lại các tính năng khác nhau của ứng dụng. Hình 1 cho thấy màn hình ứng dụng.

Hình 1. Biểu thị máy tính cầm tay dùng JavaScript đang hoạt động
Ảnh chụp màn hình biểu thị máy tính cầm tay dùng JavaScript đang hoạt động

Trong ứng dụng Android nguyên gốc mẫu, có tên là AndroidJSON, màn hình được định nghĩa khi sử dụng thành phần Activity. Nó chứa các phần tử giao diện người dùng truyền thống ở nửa trên của màn hình như một TextView (một nhãn tĩnh), một EditText (một hộp văn bản trong đó người dùng nhập vào công thức) và ba nút (Simple, Complex và Make Red). Activity cũng có một cá thể đơn của thành phần điều khiển WebView, hiển thị ở nửa dưới của màn hình.

WebView hiển thị một tệp HTML (index.html) được đóng gói với ứng dụng Android (mặc dù bạn cũng có thể tải về tệp này từ Internet). Trang web này chứa tiêu đề, một vài văn bản mẫu, các kết quả tính toán và sáu nút để thực hiện các chức năng khác nhau (Log Info, Log Error, Dynamic, How Many Calls, History và Kill This App).

Các tệp được quan tâm trong dự án này là tệp AndroidJSON.java (mã ứng dụng Android)>, tệp index.html (trang web) và tệp main.xml (một tệp bố trí giao diện người dùng mà bạn sẽ xem xét sau). Xem phần Tải về để có các liên kết đến các tệp này.

Đầu tiên, xem xét chức năng của ba nút trong Activity:

Simple (Đơn giản)
Nút Simple làm cho các nội dung của EditText được đánh giá như một biểu thức toán học. Lưu ý rằng các nội dung của EditText, hoặc công thức, được chuyển tới thành phần điều khiển WebView và được đánh giá trong JavaScript.
Complex (Phức tạp)
Nút Complex gửi một đối tượng JSON tới WebView để đánh giá. Đây được coi là sự phức tạp do thực tế là đối tượng được diễn giải bằng mã JavaScript và được xử lý bằng toán học. Nút này chuyển đổi giữa một hàm cộng các phần tử của một mảng các số nguyên và hàm nhân cùng một mảng các số nguyên.
Make Red (Chuyển thành màu đỏ)
Nút này thứ ba chủ yếu ở đây cho vui. Khi được chọn, nút này áp dụng một kiểu dáng với nội dung của WebView đã nhúng, chuyển các phần tử văn bản được chứa trong thẻ <body> thành màu đỏ.

Bây giờ xem xét chức năng bên trong một tệp index.html, được thành phần điều khiển WebView nhúng bật lên trong thời gian chạy.

Log Info (Thông tin bản ghi nhật ký)
Nút này gọi một cuộc gọi lại trong ứng dụng Android để viết một mục nhập tới bản ghi nhật ký của ứng dụng theo thể loại Thông tin.
Error Info (Thông tin về lỗi)
Nút này gọi một cuộc gọi lại trong các ứng dụng Android để viết một mục vào bản ghi nhật ký ứng dụng theo thể loại Lỗi.
Dynamic (Động)
Nút này gọi một cuộc gọi lại trong ứng dụng Android để lấy một đoạn văn bản biểu diễn mã JavaScript hợp lệ. Mã này được đưa trở lại vào WebView và được thực thi, biểu thị sự tương tác giữa hai bên của ứng dụng. Lưu ý rằng cách tiếp cận này dễ bị một số hoạt động khai thác bảo mật tấn công do tin cậy mù quáng vào hàm eval của JavaScript. Tuy nhiên, trọng tâm của bạn ở đây là xem xét ứng dụng chứ không phải là ứng dụng đã hoàn toàn sẵn sàng để sản xuất.
How many calls (Có bao nhiêu cuộc gọi)
Mỗi lần một trong những hàm gọi lại được gọi, một bộ đếm tăng lên. Nút này chỉ hiển thị bộ đếm này.
History (Lược sử)
Mỗi lần một trong các hàm JavaScript được gọi, một chuỗi biểu diễn tên hàm được thêm vào một mảng JavaScript. Khi nút history được gọi, mảng này được chuyển đổi thành JSON và chuyển đến phần ứng dụng Android nguyên gốc. Mảng này được tái tạo như là một đối tượng trong mã Java và được liệt kê từng phần với mỗi phần tử của mảng được ghi vào bản ghi nhật ký.
Kill This App (Kết thúc ứng dụng này)
Nút này là một trong những nút khác để làm vui cho ứng dụng này. Nút này gọi một cuộc gọi lại chấm dứt hoạt động Android thông qua một cuộc gọi tới finish() (kết thúc).

Giống như nhiều ứng dụng đang phát triển, ứng dụng Android sử dụng các khả năng Logging (ghi nhật ký) được xây dựng trong Android. Một số ảnh chụp màn hình được chia sẻ trong bài viết này lấy từ Khung nhìn DDMS (Dalvik Debug Monitor Service- Dịch vụ Theo dõi gỡ lỗi của Dalvik) trong Eclipse, ở đấy nhìn thấy cửa sổ LogCat. Để biết thêm thông tin về cách sử dụng các công cụ phát triển Android, xin vui lòng tham khảo các liên kết trong phần Tài nguyên.

Đã xong những giải thích này về hàm của ứng dụng, bây giờ hãy xem xét cách xây dựng giao diện người dùng.


Thiết lập giao diện người dùng

Tạo giao diện người dùng cho ứng dụng này bao gồm cả ba tệp đã giới thiệu ở trên. Hãy bắt đầu với tệp bố trí, main.xml, trong Liệt kê 1.

Liệt kê 1. Tệp main.xml, tệp bố trí giao diện người sử dụng
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView android:layout_width="fill_parent"  
android:layout_height="wrap_content" android:text="@string/title" />
    <EditText android:id="@+id/formula" android:layout_width="fill_parent" 
android:layout_height="wrap_content" android:text="" android:visible="False" />
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <Button android:text="Simple" android:id="@+id/btnSimple" 
android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>
        <Button android:text="Complex" android:id="@+id/btnComplex"
 android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>
        <Button android:text="Make Red" android:id="@+id/btnRed" 
android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>    
    </LinearLayout>
    <WebView android:layout_width="fill_parent" android:layout_height="fill_parent"
 android:id="@+id/calculator" android:layout_weight="1" />
</LinearLayout>

Trong Liệt kê 1, tệp bố trí có chứa các phần tử giao diện người dùng khác nhau. Lưu ý rằng sự có mặt của thuộc tính android:id cho phép ứng dụng thực hiện tham chiếu đến một tiện ích cụ thể trong bố trí. Ví dụ, WebView có chứa id của calculator; tuy nhiên, TextView không chứa một id khi giá trị của nó không thay đổi trong suốt vòng đời của ứng dụng.

Phương thức onCreate() có trong tệp AndroidJSON.java chịu trách nhiệm về mở rộng cách bố trí như trong Liệt kê 2.

Liệt kê 2. Thiết lập giao diện người dùng
public class AndroidJSON extends Activity {
    private final String tag = "AndroidJSON";
    private WebView browser = null;
    private int flipflop = 0;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        final EditText formula = (EditText) this.findViewById(R.id.formula);
        final Button btnSimple = (Button) this.findViewById(R.id.btnSimple);
        final Button btnComplex = (Button) this.findViewById(R.id.btnComplex);
        final Button btnRed = (Button) this.findViewById(R.id.btnRed);
    // remaining code removed for brevity - shown in next listings
}

Bạn mở rộng cách bố trí bằng một cuộc gọi đến phương thức setContentView(). Lưu ý rằng bạn thiết lập các phần tử giao diện người dùng bằng cách gọi phương thức findViewById(). Tệp R.java được tự động tạo ra mỗi khi tệp main.xml được lưu lại. Các phần tử bố trí chứa thuộc tính android:id trở thành các giá trị trong lớp R.id, như trong Liệt kê 3.

Liệt kê 3. R.java
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.msi.androidjson;

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int icon=0x7f020000;
    }
    public static final class id {
        public static final int btnComplex=0x7f050002;
        public static final int btnRed=0x7f050003;
        public static final int btnSimple=0x7f050001;
        public static final int calculator=0x7f050004;
        public static final int formula=0x7f050000;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int title=0x7f040000;
    }
}

Bạn sẽ xem lại mã thiết lập Button sau trong bài viết này, bây giờ hãy tập trung vào việc thiết lập điều khiển WebView hoặc tiện ích. Trong khi Button và các phần tử giao diện người dùng khác khá đơn giản, thì WebView đòi hỏi nhiều nỗ lực hơn đáng kể. Thế nhưng đừng lo lắng - nó không khó khăn lắm, đặc biệt nếu tính đến sử dụng cho việc xem xét kỹ thuật cắt và dán hiệu quả và tin cậy vẫn thường được áp dụng! Nhìn vào đoạn mã trong Liệt kê 4, một lần nữa được lấy ra từ phương thức onCreate() có trong tệp AndroidJSON.java.

Liệt kê 4. Thiết lập widget WebView
        // connect to our browser so we can manipulate it
        browser = (WebView) findViewById(R.id.calculator);

        // set a webview client to override the default functionality
        browser.setWebViewClient(new wvClient());

        // get settings so we can config our WebView instance
        WebSettings settings = browser.getSettings();

        // JavaScript?  Of course!
        settings.setJavaScriptEnabled(true);

        // clear cache
        browser.clearCache(true);

        // this is necessary for "alert()" to work
        browser.setWebChromeClient(new WebChromeClient());

        // add our custom functionality to the javascript environment
        browser.addJavascriptInterface(new CalculatorHandler(), "calc");

        // uncomment this if you want to use the webview as an invisible calculator!
        //browser.setVisibility(View.INVISIBLE);

        // load a page to get things started
        browser.loadUrl("file:///android_asset/index.html");

        // allows the control to receive focus
        // on some versions of Android the webview doesn't handle input focus properly
        // this seems to make things work with Android 2.1, but not 2.2
       // browser.requestFocusFromTouch();

Trong Liệt kê 4, lưu ý rằng bạn đã kết nối một biến xem xét Activity có tên là browser (trình duyệt) cho thành phần điều khiển WebView. WebView là một lớp khá phức tạp có thể được tùy chỉnh rất cao. Ví dụ, bạn cần phải thiết lập một vài lớp để có được hàm mong muốn liên kết với một trình duyệt web. Đây là một trong những công việc mà lập trình viên phải nỗ lực tối thiểu để nhận được một số hàm có ích. Tuy nhiên, chỉ có bầu trời là giới hạn các tuỳ chỉnh có thể đi xa tới đâu! Với mục đích của ứng dụng này, điều khiển WebView được triển khai ở một mức độ tối thiểu.

WebViewClient cung cấp các móc nối để bắt giữ các sự kiện khác nhau như bắt đầu và kết thúc nạp trang, đệ trình lại biểu mẫu, chặn bàn phím và nhiều sự kiện khác mà các lập trình viên yêu thích để ngắt chương trình và xử lý. Tương tự như vậy, bạn cần một cá thể của WebChromeClient để cho phép các hàm như hàm JavaScript rất có ích là alert() (cảnh báo). Bạn sử dụng lớp WebSettings để bật lên JavaScript cho thành phần điều khiển này.

Để làm cho thành phần điều khiển WebView chuyển hướng đến một trang, bạn có một vài lựa chọn khác nhau. Trong ứng dụng này, bạn sử dụng phương thức loadurl() với một đường dẫn phân biệt đầy đủ tới tệp index.html được đóng gói như là một tài sản của dự án. Để biết thêm thông tin về việc thiết lập thành phần điều khiển WebView, hãy xem tài liệu trực tuyến cho gói android.webkit (xem phần Tài nguyên). Tệp có tên là index.html được nạp trực tiếp vào thành phần điều khiển WebView từ các tài nguyên đi kèm với ứng dụng. Lưu ý trong Hình 2 thư mục assets (các tài sản) nằm trong thư mục resources (các tài nguyên). Thư mục này là nơi lý tưởng để lưu trữ các tệp html để sử dụng trong một ứng dụng lai. (Xem phiên bản chỉ có văn bản của Hình 2.)

Hình 2. Dự án trong Eclipse
Dự án trong Eclipse

Người ta có thể cho rằng những khía cạnh quan trọng và thú vị nhất khi làm việc với WebView là bước tiếp theo: kết nối môi trường JavaScript của WebView với mã của Activity (Hoạt động) của Android.


Kết nối với giao diện JavaScript

Bước tiếp theo là cho phép mã Java trong Activity tương tác với mã JavaScript trong tệp HTML do WebView quản lý. Điều này được thực hiện bằng một cuộc gọi đến phương thức addJavascriptInterface() như trong Liệt kê 4.

Các đối số cho hàm này là một thể hiện của một lớp Java và một trình định danh vùng tên. Ví dụ, với ứng dụng này, bạn định nghĩa một vùng tên là calc và triển khai thực hiện mã trong một lớp có tên là CalculatorHandler như trong Liệt kê 5.

Liệt kê 5. Triển khai thực hiện CalculatorHandler
// Javascript handler
    final class CalculatorHandler
    {
        private int iterations = 0;
        // write to LogCat (Info)
        public void Info(String str) {
            iterations++;
            Log.i("Calc",str);
        }
        // write to LogCat (Error)
        public void Error(String str) {
            iterations++;
            Log.e("Calc",str);
        } 
        // sample to retrieve a custom - written function with the details provided 
        // by the Android native application code
        public String GetSomeFunction()
        {
            iterations++;
            return "var q = 6;function dynamicFunc(v) { return v + q; }";
        }
        // Kill the app        
        public void EndApp() {
            iterations++;
            finish();
        }
        public void setAnswer(String a)
        {
            iterations++;
            Log.i(tag,"Answer [" + a + "]");
        }
        public int getIterations()
        {
            return iterations;
        }
        public void SendHistory(String s)
        {
            Log.i("Calc","SendHistory" + s);
            try {
                JSONArray ja = new JSONArray(s);
                for (int i=0;i<ja.length();i++) {
                    Log.i("Calc","History entry #" + (i+1) + " is [" + ja.getString(i) 
+ "]");
                }
            } catch (Exception ee) {
                Log.e("Calc",ee.getMessage());
            }
        }
    }

Trong môi trường JavaScript, bạn truy cập các phương thức của CalculatorHandler thông qua cú pháp window.calc.methodname. Ví dụ, CalculatorHandler triển khai thực hiện một phương thức có tên là Info() (Thông tin), nó lấy một đối số chuỗi ký tự và ghi nó vào bản ghi nhật ký ứng dụng. Để truy cập phương thực này từ môi trường JavaScript, hãy sử dụng cú pháp như sau: window.calc.Info("write this string to the application log!");.

Khi đã hiểu cơ bản về cách mã Java được gọi từ mã JavaScript, hãy xem xét tệp index.html trong Liệt kê 6 để xem các phương thức khác nhau được gọi ra sao.

Liệt kê 6. Tệp index.html được lấy ra (và được thực hiện) trong thành phần điều khiển WebView
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=0.25,
    user-scalable=yes" />
<title>Android to JavaScript with JSON</title>
</head>
<script language="JavaScript">
var cmdHistory = new Array();
function startup() {
    try {
        window.calc.Info("Starting up....");
        cmdHistory[cmdHistory.length] = "startup";
    } catch (ee) {

    }
}
function PerformSimpleCalculation(formula) {
    try {
        cmdHistory[cmdHistory.length] = "PerformSimpleCalculation";
        var answer = eval(String(formula));
        document.getElementById('data').value = answer;
        window.calc.setAnswer(answer);
    }    catch (ee)     {
        window.calc.Error(ee);
    }
}
function PerformComplexCalculation(andmethod) {
    try    {
        /*
         * argument to this function is a single object with 2 "members or properties"
         * operation: this is a string naming what we want the function to do.
         * array of arguments: this is an array of integers
         * 
         */
        //alert(andmethod.operation);
        //alert(andmethod.arguments.length);
        if (andmethod.operation == "addarray") {
            cmdHistory[cmdHistory.length] = "PerformCompleCalculation-addarray";
            var i;
            var result = 0;
            for (i=0;i<andmethod.arguments.length;i++) {
                result += andmethod.arguments[i];
            }
            document.getElementById('data').value = result;
            window.calc.setAnswer(result);
        }
        if (andmethod.operation == "multarray") {
            cmdHistory[cmdHistory.length] = "PerformCompleCalculation-multarray";
            var i;
            var result = 1;
            for (i=0;i<andmethod.arguments.length;i++) {
                result *= andmethod.arguments[i];
            }
            document.getElementById('data').value = result;
            window.calc.setAnswer(result);            
        }
    }    catch (ee)    {
        window.calc.Error(ee);
    }
}
function dynamicfunction()
{
    try {
        cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";
        eval(String(window.calc.GetSomeFunction()));
        var result = dynamicFunc(parseInt(document.getElementById('data').value));
        document.getElementById('data').value = result;
    }catch (ee) {
        alert(ee);
    }
}
</script>
<body >
<center>
<h3>Running in Web View :)</h3>
this is some sample text here <br />
<input type="text" id="data" value="starting value"><br />
<button onclick="window.calc.Info(document.getElementById('data').value);">Log
 Info</button>&nbsp;&nbsp;
<button onclick="window.calc.Error(document.getElementById('data').value);">Log
 Error</button><br />
<button onclick="dynamicfunction();">Dynamic</button>
<button onclick="alert(String(window.calc.getIterations()));">How 
    Many Calls</button>
<button onclick="window.calc.SendHistory(JSON.stringify(cmdHistory));">
    History</button>
<button onclick="if (window.confirm('End App?')) window.calc.EndApp();">Kill This
 App</button><br />
</center>
</body>
</html>

Xem xét các trình xử lý nút ở cuối của Liệt kê 6. Về bản chất các trình xử lý này đang gọi các phương thức trong vùng tên window.calc được triển khai thực hiện trong lớp CalculatorHandler trong tệp AndroidJSON.java.

Liệt kê 5Liệt kê 6 hoạt động liên quan chặt chẽ với nhau để biểu diễn sự tương tác mã được bắt đầu trong môi trường JavaScript và được triển khai thực hiện trong các tệp nguồn Java. Nhưng làm thế nào để bắt đầu một số hành động từ mã Activity mà bạn muốn xảy ra trong WebView?

Đây là lúc nghiên cứu sâu hơn về mã Java.


Chèn mã JavaScript

Bắt đầu với nhiệm vụ chuyển một công thức toán học thành mã JavaScript để đánh giá. Một trong những tính năng tuyệt vời (và nguy hiểm!) của JavaScript là hàm eval(). Hàm eval() cho phép đánh giá thời gian chạy của một chuỗi mã. Trong ví dụ này, bạn lấy một chuỗi ký tự từ thành phần điều khiển EditText và chuyển đến môi trường JavaScript để đánh giá. Cụ thể, chúng ta gọi hàm PerformSimpleCalculation() có trong Liệt kê 6.

Liệt kê 7 bao gồm mã từ tệp AndroidJSON.java có trách nhiệm xử lý các lựa chọn nút ấn.

Liệt kê 7. Gọi hàm JavaScript PerformSimpleCalculation() từ Java
  btnSimple.setOnClickListener(new OnClickListener()
  {
       public void onClick(View v) {
         Log.i(tag,"onClick Simple");
         // Perform action on click
         try
         {
            String formulaText =  formula.getText().toString();
            Log.i(tag,"Formula is [" + formulaText + "]" );
            browser.loadUrl("javascript:PerformSimpleCalculation(" + formulaText + ");");
         }
         catch (Exception e)
         {
               Log.e(tag,"Error ..." + e.getMessage());
         }
       }
  });

Mặc dù có nhiều dòng trong phương thức này, nhưng ở đây chỉ có một dòng duy nhất cần tập trung vào là dòng browser.loadurl() chuyển một một chuỗi ký tự định dạng: javascript:<code to execute>.

Đoạn mã JavaScript được nội xạ vào trang hiện hành của WebView và được thực thi. Bằng cách này, mã Java có thể thực thi mã JavaScript được định nghĩa trong WebView.

Trong ví dụ Simple (Đơn giản) này, một chuỗi ký tự được chuyển qua. Tuy nhiên, khi bạn cần phải làm việc với một cấu trúc phức tạp hơn thì sao? Đây là nơi JSON có thể trợ giúp. Liệt kê 8 cho thấy lời gọi hàm PerformComplexCalculation(), có trong Liệt kê 6.

Liệt kê 8. Gọi một hàm phức tạp hơn bằng cách chuyển một đối tượng JSON
btnComplex.setOnClickListener(new OnClickListener()
{
     public void onClick(View v) {
         Log.i(tag,"onClick Complex");
         // Perform action on click
         try
         {
             String jsonText = "";
         
             if (flipflop == 0)
             {     
                 jsonText = "{ \"operation\" : \"addarray\",\"arguments\" :
 [1,2,3,4,5,6,7,8,9,10]}";
                 flipflop = 1;
             } else {
                 jsonText = "{ \"operation\" : \"multarray\",\"arguments\" :
 [1,2,3,4,5,6,7,8,9,10]}";
                 flipflop = 0;
             }
             Log.i(tag,"jsonText is [" + jsonText + "]" );
             browser.loadUrl("javascript:PerformComplexCalculation(" + jsonText + ");");
         }
         catch (Exception e)
         {
             Log.e(tag,"Error ..." + e.getMessage());
         }
         
     }
});

Xem xét hàm JavaScript PerformComplexCalculation từ Liệt kê 6. Lưu ý rằng đối số được chuyển vào không phải là một chuỗi ký tự, mà là một đối tượng do chính bạn tạo ra.

  • operation - Tên của một hàm hoặc thủ tục cần xử lý.
  • arguments - Đây là một mảng các số nguyên

Đối tượng này chỉ có hai đặc tính, nhưng có thể phức tạp hơn tùy ý để đáp ứng một nhu cầu lớn hơn. Trong ví dụ này, hàm JavaScript PerformComplexCalculation() hỗ trợ hai phép toán khác nhau: addarray (cộng mảng) và multarray (nhân mảng). Khi các phép toán đã thực hiện công việc của mình dựa vào lời gọi, kết quả được chuyển trả về cho mã Java bằng cách gọi hàm: window.calc.setAnswer. Ở đây bạn thấy luồng dữ liệu hai chiều giữa mã Java và mã JavaScript.

Trong ví dụ này, bạn đã chuyển một đối tượng JSON qua, nhưng theo kinh nghiệm quan sát được khi làm việc với các chuỗi Java trả về từ mã Java, nó giúp chuyển đổi chúng thành các chuỗi JavaScript. Điều này có thể được thực hiện bằng cách chuyển các giá trị thành hàm String như trong ví dụ này: eval(String(formula));.

Hàm JavaScript eval() sử dụng các chuỗi JavaScript. Nếu không có sự chuyển đổi này, thì hàm eval về cơ bản không làm gì cả.

Để có một ví dụ phức tạp hơn một chút, người ta khuyến khích bạn đi dần dần qua dãy mã khi chọn nút Dynamic (Động) trong WebView.

Để kết thúc các ví dụ về mã, hãy xem xét cách chuyển một mảng các chuỗi từ môi trường JavaScript sang môi trường Java.


Trao đổi các đối tượng JSON

Mã JavaScript trong ứng dụng ví dụ (index.html) ghi lại lời gọi hàm cục bộ vào trong một mảng mức trang có tên là cmdHistory. Mỗi khi một hàm được gọi, bạn thêm một mục nhập mới vào mảng này. Ví dụ, khi hàm dynamicfunction() được gọi, một chuỗi mới được lưu trữ: cmdHistory[cmdHistory.length] = "PerformCompleCalculation-dynamic";.

Không có gì quá đặc biệt về cách tiếp cận này; nó chỉ đơn giản là một ví dụ về thu thập dữ liệu sử dụng ở mức trang. Có lẽ dữ liệu này sẽ có ích khi được lưu trong cơ sở dữ liệu của ứng dụng Android. Làm thế nào để nhận lại các dữ liệu này trong mã Java?

Để gửi mảng các đối tượng chuỗi của bạn, bạn gọi hàm JSON.stringify, chuyển vào mảng này làm đối số. Theo tùy chọn, hàm stringify có thể cho phép tùy chỉnh về cách định dạng một đặc tính cụ thể của một đối tượng phức tạp. Để biết thêm thông tin về điều này được thực hiện như thế nào, bạn có thể xem xét lời giải thích có trong json.org (xem phần Tài nguyên).

Hình 3 cho thấy những gì kết thúc trong bản ghi nhật ký (Log) sau khi phân tích cú pháp mảng JSON tiếp sau một lần chạy ứng dụng điển hình.

Hình 3. Phân tích cú pháp mảng JSON được gửi từ JavaScript
Ảnh chụp màn hình về phân tích cú pháp mảng JSON được gửi từ JavaScript

Ví dụ này chỉ đang lưu trữ dữ liệu chuỗi, do đó cho rằng bạn có thể chỉ cần nối thêm nó vào một chuỗi dài hơn và gọi một hàm đơn giản trong CalculatorHandler, đến lượt hàm này có thể phân tích cú pháp nó. Tuy nhiên, điều gì sẽ xảy ra nếu các ứng dụng muốn theo dõi dữ liệu khác như giá trị của các biến nào đó hoặc thậm chí cố gắng để nắm bắt những nét chính của mã bằng cách ghi lại khoảng thời gian của một lời gọi hàm cụ thể? Rõ ràng là khả năng ghi lại và trao đổi các đối tượng là đáng quan tâm trong nhiều tình huống phức tạp hơn.


Tóm tắt

Bài viết này trình diễn các kỹ thuật về việc chuyển dữ liệu giữa mã Java trong ứng dụng Android với mã JavaScript trong WebView cũng như nhiều chủ đề tổng quát hơn về các ứng dụng lai sử dụng WebKit. Các ứng dụng lai trộn lẫn JavaScript, JSON, các hàm gọi lại, mã Java SDK của Android và thành phần quan trọng nhất trong tất cả, đó là trí tưởng tượng của bạn, để cung cấp các ứng dụng di động có khả năng và linh hoạt.


Tải về

Mô tảTênKích thước
Article source codeandjson.zip58KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=732194
ArticleTitle=Sử dụng XML và JSON với Android, Phần 2: Cung cấp các ứng dụng Android lai với JSON
publish-date=07122011