[Android] ADB is unabled to connect my China phone – HiSense U950 (aka HiSense F1)

That was crazy time !

I just bought a cheap android phone for my incoming projects. Yes, budget was my very first issue that I could not bought expensive phone such as Samsung Galaxy S3, or Note 2 (just my dream) . So I decided to buy a cheap phone – Hisense F1, which price is arround 110 usd. My Android development experience takes me to this site, then download the USB driver. But no look, the link is just … broken, and it seems about the smart TV product, not smartphone !!!

A guy from tinhte.vn just posted some stuff rooting this phone, so I could found the link to download its driver.  So I thought just downloading driver, then everything would work well as I did with my HTC Cha Cha or Motorola Droid before.  Opening the adb and check connection

C:\Android\android-sdk\platform-tools>adb kill-server
C:\Android\android-sdk\platform-tools>adb start-server
* daemon not running. starting it now on port 5037 *
OK
* daemon started successfully *
C:\Android\android-sdk\platform-tools>adb devices
List of devices attached

The list was just empty !!!

I know the most problems that ADB could not see the device is driver compatible, but Windows shows me that driver has been installed correctly ! Dammit …   So I came back to Android development site, read carefully about “Hardware devices” and noticed on USB Vendor ID. Just remembering that the last time I installed driver for HTC Cha Cha,  adb_usb.ini file (C:\Users\<my user name>\.android\adb_usb.ini ) has been changed for some reason, and it contains USB Vendor ID list.

# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.
# USE 'android update adb' TO GENERATE.
# 1 USB VENDOR ID PER LINE.
0x0502
0x05C6
0x0BB4
0x0FCE
0x1004
0x18D1
0x19D2
0x22B8

Yes, I did not contain the vendor ID for Hisence phones !

So I just added “0x109b” at the ending of file and restarted the ADB

C:\Android\android-sdk\platform-tools>adb kill-server
C:\Android\android-sdk\platform-tools>adb devices
* daemon not running. starting it now on port 5037 *
OK
* daemon started successfully *
List of devices attached 
u950 device

Now, ADB can see my device …

 

 

[Sencha Ext JS 4] Unit Testing với Jasmine

Jasmine là một BDD (Behavior Driven Development) test framework có thể tích hợp vào nhiều môi trường khác nhau. Trong bài viết trước, tôi có nhắc đến Jasmine cùng với khái niệm về TDD. Thực tế thì BDD là một bản mở rộng của TDD mà thôi. Bạn có thể xem thảo luận về BDD và TTD tại đây. Về Unit testing, bạn có thể tìm hiểu qua rất nhiều các bài viết hoặc ebook nên tôi không đi vào quá chi tiết việc trình bày các khái niệm về unit testing. Quan điểm của tôi về unit testing như sau:

  • Unit testing để làm rõ và kiểm tra tính đúng đắn của specs
  • Unit testing cho thấy khả năng chia nhỏ các feature mà bạn đang hiện thực nó
  • Unit testing không thích hợp cho test UI, tốt nhất là áp dụng unit testing cho logic của ứng dụng. Bạn đặt logic của ứng dụng ở đâu (Model ? View ? ) thì hãy áp dụng unit testing ở đó.

Với ExtJS, hướng dẫn về unit testing được trình bày tương đối đơn giản và ngắn gọn, bạn nên đọc qua trước khi bắt đầu viết unit testing.  Bài viết sử dụng ứng dụng FlickrSearch  mà tôi đã trình bày từ trước, bạn có thể tìm hiều qua ứng dụng FlickrSearch. Phần trình bày về Jasmine sẽ tâp trung vào việc viết test cho 2 module Search Photo và màn hình Login. Trường hợp màn hình login là một thử nghiệm vì tôi không nghĩ mình sẽ viết unit testing cho phần UI như quan điểm trình bày ở trên.

Cài đặt Jasmine

Tôi sử dụng phiên bản Jasmine 1.3.1 – là phiên bản mới nhất tại thời điểm này. Bạn có thể thấy workspace của tôi khác một chút so với các ví dụ của ExtJS. Đơn giản là tôi thực hiện unit testing cho chính source code mà tôi đang viết.

jasmine workspace

Những module được đánh dấu là những phần thuộc về unit test. Như vậy bạn cần phải copy jasmine 1.3.1 vào project của mình. Folder specs là nơi đặt các test-case, app-test.js và run-test/html là các file để khởi động ứng dụng test.

app-test.js

File này khởi tạo môi trường chạy của jasmine cũng như khởi tạo các report cho các test run

Ext.Loader.setConfig({enabled:true});
Ext.require('Ext.app.Application');

var Application = null;
Ext.onReady(function() {
      Application = Ext.create('Ext.app.Application', {
      name: 'FlickrSearch',
      appFolder: 'app',
      controllers: ['ScreensController'],
      launch: function() {
         var htmlReporter = new jasmine.HtmlReporter();
         jasmine.getEnv().addReporter(htmlReporter);
         jasmine.getEnv().execute();
      }
 });
});

Test suite cho PhotoStore

Một test-suite bao gồm nhiều test-case, trong trưởng hợp này tôi tạo test-suite cho PhotoStore và định nghĩa một số test-case trong đó.

describe("Photo", function() {
    var controller = null;
    var store = null;
    beforeEach(function() {
        if (!controller) {
           controller = Application.getController("ScreensController");
        }
        if (!store) {
           store = controller.getStore('PhotoStore');
        }
   });
});

beforeEach (cùng với afterEach) là các hàm global – những hàm này sẽ được gọi trước (beforeEach) và sau (afterEach) mỗi test-case. Các test-case có cùng điều kiện khởi tạo và kết thúc có thể sử dụng các hàm này để khời tạo cũng như kết thúc test-case. Mục đích là để tránh lập lại các đoạn code dành cho khời tạo, kết thúc …  Trong trường hợp trên, mỗi test-case cần khởi tạo lại store và controller, nếu đặt các đoạn code này trong từng test-case sẽ dẫn đến việc các đoạn code này bị lập lại – bạn sẽ vi phạm nguyên tắc DRY – Don’t Repeat Yourself

Một số test-case cho PhotoStore

Kiểm tra việc khởi tạo controller

it("controller must be defined",function(){
 expect(controller).toBeDefined();
 });

Kiểm tra việc khởi tạo store

it("Store must be definded", function(){
 expect(store).toBeDefined();
 });

PhotoStore không phải là 1 store autoLoad, store này được kích hoạt thông qua việc gọi hàm load nên trong trường hợp này, thuộc tính autoLoad phải là false.

it("Store should not auto load",function(){
 expect(store.autoLoad).toEqual(false);
 });

test-case dưới đây minh họa việc test bất đồng bộ – store thực hiện một request tới server và chờ server response về . Giả định là request này sẽ phải thực hiện trong vòng 4 giây và nếu quá thời gian này, test -case sẽ fail

it("sets up latency configuration and receives latency", function() {
 runs(function() {
 store.load({params: { text: 'red' }});
 }, "an asynchronous method");
waitsFor(function() {
 return store.isLoading() == false;
 }, "store loading complete", 4000);
 });

Sau khi thực hiện request đến server và có response trong vòng 4 giây, kiểm tra kết quả store có dữ liệu hay không

it("Store should have photos", function() {
 expect(store.getCount()).toBeGreaterThan(0);
 });

Unit-test với UI

Theo quan điểm của tôi, UI có thể quá phức tạp để thực hiện unit-test. Với UI, nên sử dụng các phương pháp automation test sẽ có hiệu quả hơn ? Bạn có thể tham khảo thêm test-suite “authentication” mà tôi dùng để test UI. Kết quả chạy có thể thấy như sau

jasmine

[Sencha ExtJS 4] Xây dựng ứng dụng Flickr Search

Trong bài viết trước tôi đã trích dẫn một phần nhỏ của ứng dụng Flickr Search mà tôi đang viết demo. Từ loạt vbài này, tôi sẽ cố gắng trình giới thiệu từng bước để có thể xây dựng một ứng dụng Flickr Search tương đối hoàn chỉnh trên nền ExtJS 4.2 . Loạt bài viết này cũng không trình bày cách thức cài đặt môi trường làm việc với ExtJS, cũng như đi quá sâu vào việc trình bày xây dựng 1 ứng dụng MVC từ con số 0. Bạn cần phải nắm về MVC pattern, Javascript để có thể theo dõi loại bài này hoặc có thể để lại comment cho bất kỳ mục nào mà bạn không hiểu hoặc chưa rõ. Nhiều bạn từng hỏi tôi về IDE /application server ?  Tôi đang sử dụng WebStorm 5.0 để coding và NodeJS để deploy ứng dụng của mình. Bạn có thể tải trước mã nguồn của ứng dụng tại đây.

 

Ứng dụng này sẽ gồm những gì ?

Màn hình login – hiện tại nó không có tác dụng gì ngoài minh họa cho việc quản lý nhiều màn hình khác nhau.

login

Màn hình PhotoSearch – màn hình này sẽ bao gồm 1 toolbar, bạn sẽ gõ từ khóa tìm kiếm ở đây

search

Ngoài ra, khi bạn click lên hình thumbnail, một cửa sổ popup hiện ra cho phép bạn xem hình ở kích thước lớn hơn

popup

Bạn cần xây dựng những lớp nào ?

Trước hết, hãy xem qua screenshot project workspace của tôi

project

 Hãy chia ứng dụng của bạn ra các package tương ứng với M – Model , V – View , C – Controller. Dễ dàng thấy 3 màn hình mà tôi trình bày ở trên nằm lần lượt trong package view.  Trong project này tôi cũng sử dụng unit testing với Jasmine 1.3.1, tuy nhiên tôi không có ý định xây dựng ứng dụng này theo phương pháp TDD. TDD với ExtJS sẽ được trình bày trong một bài viết khác cùng với kỹ thuật Automation. Bạn có thể tham khảo file app-test.js, các file trong package specs cũng như chạy thử run-test.html.

Khởi tạo ứng dụng – app.js

Ext.Loader.setConfig({enabled:true});
Ext.application({
     requires: ['Ext.container.Viewport'],
     name: 'FlickrSearch',
     appFolder: 'app',
     controllers: ['ScreensController'],
     launch: function() {
         var viewport = Ext.create('Ext.container.Viewport', {
         layout: 'card',
         items: [
               {
                 xtype: 'authentication'
               },
               {
                 xtype: 'photosearch'
               }
         ]
      });
    this.on({
           signin: function() {
                 viewport.getLayout().setActiveItem(1);
          },
          signout: function() {
                viewport.getLayout().setActiveItem(0);
          }
    });
   }
});

Bạn cần quan tâm đến layout: ‘card’ – layout này có thể quản lý nhiều view (hoặc component)  – các view hoặc component sẽ có kích thước fit với ViewPort và tại mỗi thời điểm chỉ có 1 view hoặc component được hiển thị. Để chuyển qua view khác, gọi setActiveItem và truyền vào index thích hợp. Ứng dụng hiện tại có 2 view – authentication (index = 0) và photosearch (index = 1)

signin signout là 2 sự kiện được gửi từ ScreensController, application bằt 2 sự kiện này để thực hiện việc chuyển đổi giữa các màn hình

ScreensController

Controller này hoạt động theo đúng như tên của nó – handle hầu hết các action giữa các màn hình. Nếu bạn nhìn vào các code của các View, bạn sẽ không thấy các phần logic, truy cập store …ScreensController đã làm việc này. Theo quan điểm của tôi, view thuần túy chỉ định nghĩa phần giao diện, phần logic của nó nên chuyển sang cho controller hoặc model.

Tất cả các view do ScreensController quản lý cần được khai báo trong mảng views

 

 views:['AuthenticationView', 'PhotoSearchView', 'PhotoView'],

ScreensController giữ tham chiếu đến các view thông qua khai báo các tham chiếu đến từng view trong mảng refs

refs: [
        { ref: 'authentication', selector: 'authentication'},
        { ref: 'photosearch', selector: 'photosearch'}
 ],

Sau khi khai báo, ExtJS sẽ tự sinh ra getter cho view tương ứng – getter dựa trên selector với ký tự đầu tiên được viết hoa cùng với từ khóa get được thêm vào ở phía trước . Ví dụ, để tham chiếu đến photosearch, ta sẽ gọi

view = this.getPhotosearch();

từ đây, bạn có thể truy cập đến các hàm, biến được định nghĩa trong photosearch

Ở trên, tôi có trình bày trong app của chúng ta đang handle 2 sự kiện là sigin và signout. 2 sự kiện này được khởi động trong controller, thực tế thì đến từ 2 handler của các action trong các view do ScreensController quản lý

 signinHandler: function() {
    this.application.fireEvent("signin");
 },
 signoutHandler: function() {
    this.application.fireEvent("signout");
 },

PhotoModel và PhotoStore

Tham khảo đến bài viết trước để có cái nhìn tổng quan hơn về 2 class này

 

PhotoSearchView

Chú ý đến XTemplate được sử dụng trong view này. Tôi đã mất 1 tiếng để debug khi không thể render được các item từ XTemplate vì chưa khai báo itemSelector ( itemSelector: ‘div.thumb-wrap’, ). Với các phiên bản cũ của ExtJS thì itemSelector không bắt buộc (hoặc không có ?) nhưng với ExtJS 4.2 thì đây là thuộc tính bắt buộc. Tốt nhất khi sử dụng 1 component, class của ExtJS, bạn nên tham khảo document – mục config để xem các trường nào là bắt buộc.

Một số điểm cần chú ý

  • các view được khai báo trong controller sẽ được khởi tạo sẵn. Tôi bẫy sự kiện active của photosearch để xóa các kết quả search cũ khi màn hình này được active
  • alias được dùng để truy cập view từ controller
  • Ext.widget sẽ tạo ra instance mới – đừng hiểu nhầm đó là cách tham chiếu đến view.

 

Kết luận

ExtJS là một framework rất tốt để xây dựng các ứng dụng Rich Internet, đồng thời cũng là đối thủ nguy hiểm của Flex. Nếu bạn theo dõi blog của tôi từ trước, bạn có thể dễ dàng thấy tôi đang sử dụng ExtJS để thay thế Flex 🙂

 

 

 

[Sencha ExtJS 4] Truy cập Flickr Search API

Nếu bạn bắt đầu tiếp cận với ExtJS 4 và tham khảo bài viết của Evan Trimboli về việc truy cập Flickr Search API thì có thể bạn sẽ gặp 1 chút trở ngại với ExtJS 4. Blog này viết cho các phiên bản cũ của ExtJS – có thể là 3.x nên không còn đúng khi viết trên nền Ext JS 4.0

Tôi không tìm cách port lại các class Proxy cũng như JSON Reader mà chỉ đưa ra một cách tiếp cận khác, đó là hiện thực lớp PhotoStore để thực hiện việc truy cập Flickr Search API.

Ext.define('FlickrSearch.store.PhotoStore', {
          extend: 'Ext.data.Store',
          model: 'FlickrSearch.model.PhotoModel',
          proxy: {
                type: "jsonp",
                url: "http://api.flickr.com/services/rest/",
                callbackKey: 'jsoncallback',
                actionMethods: {
                    read: 'POST'
                },
           extraParams: {
               api_key: '7bcd4d0a48fc6fe0e86ec660f95541a9',
               method: 'flickr.photos.search',
               format:'json',
               nojsoncallback:'1'
           },
          reader: {
              type: 'json',
              root: 'photos.photo',
              totalProperty: 'photos.total',
              id: 'id'
          }
      },
});


// Model
Ext.define('FlickrSearch.model.PhotoModel',{
            extend: 'Ext.data.Model',
            fields:[
                    { name:'id'},
                    { name:'owner'},
                    { name:'secret'},
                    { name:'server'},
                    { name: 'farm', type: 'int' },
                    { name:'title'},
                    { name: 'ispublic', type: 'boolean' },
                    { name: 'isfriend', type: 'boolean' },
                    { name: 'isfamily', type: 'boolean' }
       ]});

 

Để thực hiện search 1 term với Flickr API,

doSearch: function(term) {
       Ext.getCmp('images-grid').store.load({params: { text: term }});
 }

Nhận đinh: 4.2.0 có thể nói là phiên bản update toàn diện của Ext JS và việc tương thích với ExtJS 3.x là rất thấp.

ANT & Flex Application: đôi khi Flash Builder không phải là tất cả ;)

Tôi có dịp trao đổi về chuyên môn với 1 số flex developer mà tôi gặp trong quá trình làm việc. Và thật bất ngờ khi phần lớn họ đều phụ thuộc quá nhiều vào Flex (Flash) Builder cho việc build ứng dụng và không mấy hào hứng với các công cụ build khác như mxmlc, hfcd … có vẻ các công cụ không mấy trực quan này không phải là lựa chọn hàng đầu cho các lập trình viên flex, hoặc cũng có thể do tính chất các project mà họ đang làm không thực sự phải dùng đến các công cụ build khác ngoài tính năng build đã có sẵn trong Flex (Flash Builder). Thực sự tôi cũng không có đủ kinh nghiệm để giải thích một cách bài bản khi nào nên dùng các công cụ khác ngoài Flex (Flash) Builder. Nhưng với project thực tế mà tôi đang làm việc, một automation tester cần chỉ cần build application hàng ngày thì việc mở Flash (Flex) Builder chỉ để build thì chả mấy khả quan tí nào.

Kịch bản mà tôi cần giải quyết đó là xây dựng một cơ chế tự động để có thể tự động check-out, merge source code, sau đó build và tự deploy lên server. Công cụ automation mà tôi đang sử dụng là RIATest (sẽ có một bài viết về mảng RIATest – FlexMonkey sau khi tôi hoàn thành việc đánh giá và so sánh 2 công cụ này).  Hệ thống tự động cần build 1 phiên bản application thông thường và một phiên bản tích hợp với các automation library, automation agent và deploy lên 2 server khác nhau. Tôi không có nhiều kinh nghiệm với ANT, ngay cả sau khi đã tốn khá nhiều thời gian với MAVEN để giải quyết một số trục trặc trong quá trình build tự động. Nhưng cá nhân tôi lại thấy cách tiếp cận của ANT dễ dàng hơn rất nhiều so với MAVEN.

Trước hết, nội dung của file build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="webviewer" default="riatest" basedir=".">
	<property file="settings.properties" />
	<!-- path to the flex task libraries. -->
	<path id="flextasks.classpath">
		<fileset dir="${FLEX_HOME}/ant/lib">
			<include name="*.jar" />
		</fileset>
	</path>

	<typedef resource="flexTasks.tasks" classpathref="flextasks.classpath" />
	<target name="riatest" depends="riatest.compile.web,deploy.web" />

	<target name="clean">
		<echo message="Cleaning Build Space" />
		<delete dir="${base.dir}/${build.dir}" />
	</target>

	<target name="deploy.web">
		<copy file="${swfFile}" tofile="${swfFileDomain}" />
	</target>

	<target name="riatest.compile.web" depends="clean">
		<echo message="Compiling Project with RIATest" />
		<mxmlc file="${projectFile}" output="${swfFile}" locale="en_US">
			<load-config filename="${FLEX_HOME}/frameworks/flex-config-riatest.xml" />
			<source-path path-element="${FLEX_HOME}/frameworks" />
			<source-path path-element="${resources.dir}" />
			<source-path path-element="${locales.dir}/{locale}" />
			<source-path path-element="${dev.dir}" />
			<static-link-runtime-shared-libraries />
			<compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
				<include name="libs/*" />
			</compiler.library-path>
			<compiler.include-libraries dir="${libs.dir}" append="true">
				<include name="cairngorm-2.2.1.swc" />
			</compiler.include-libraries>
			<compiler.include-libraries dir="${libs.dir}" append="true">
				<include name="flexspy-1.2.swc" />
			</compiler.include-libraries>
			<compiler.include-libraries dir="${automationlibs.dir}" append="true">
				<include name="automation_dmv.swc" />
				<include name="automation.swc" />
				<include name="automation_agent.swc" />
				<include name="automation_spark.swc" />
				<include name="riatestagent.swc" />
			</compiler.include-libraries>
			<theme dir="${FLEX_HOME}/frameworks/themes/Halo">
				<include name="halo.swc" />
			</theme>
		</mxmlc>
	</target>
</project>

File settings.properties

flex.path = C:/sdks/

flex.sdkVersion= 4.1.0
flex.sdkPath= ${flex.path}/${flex.sdkVersion}
FLEX_HOME= ${flex.sdkPath}

contentText = [This value will be overwritten by Flash Builder in the output app.xml]

base.dir =..
build.dir = ../bin-release

dev.dir = ../main/flex
resources.dir = ../main/resources/
locales.dir = ../main/locales/
automationlibs.dir = ../flexlib
libs.dir = ../libs

domain1.dir = C:/development/glassfish3/glassfish/domains/domain1/applications/enterprise-app/core-web_war
app.name = flex_application

## mxml or as
app.type = mxml

swfFile=${build.dir}/${app.name}.swf
swcDelegateFile=${build.dir}/${app.name}.swc
swfFileDomain = ${domain1.dir}/${app.name}.swf
projectFile=${dev.dir}/org/catapult//app/webview/${app.name}.${app.type}

swfViewerFile=${build.dir}/${appviewer.name}.swf
projectViewerFile=${dev.dir}/${appviewer.name}.${app.type}

Lưu ý:  trong file build.xml, dòng lệnh <load-config filename=”${FLEX_HOME}/frameworks/flex-config-riatest.xml” /> để load file cấu hình của flex SDK cho mxmlc. Do tôi có một số config riêng như cần include thêm các file AS (delegation class, các file AS được kế thừa từ các lớp xxxImpl) và có cả license Flex SDK. Nếu bạn không sử dụng các automation library hoặc include thêm các file AS khác thì nên thay đổi thành <load-config filename=”${FLEX_HOME}/frameworks/flex-config.xml” />

[Flex] ClassFactory và ItemRenderer

Trước khi trình bày về ClassFactory và ItemRenderer, chúng ta nên điểm qua về mẫu thiết kế (design pattern) Factory. Mẫu thiết kế Factory được dùng để giải quyết vấn đề tạo ra một đối tượng mà không thể xác định trước chính xác loại (class type) đối tượng cụ thể. Bằng cách định nghĩa một phương thức cho việc tạo đối tượng, và các lớp con thừa kế có thể nạp chồng (override ?)  để chỉ rõ đối tượng nào sẽ được tạo.

Một ví dụ thực tế của Factory chính là trình duyệt ảnh như ACDSee chẳng hạn. Bạn có 1 list các hình ảnh, tuy nhiên mỗi hình ảnh có thể là 1 đối tượng của JPEGRenderer hoặc PNGRenderer, nghĩa là thuôc các lớp hoàn toàn khác nhau. Và khi render list các hình ảnh này thì ACDSee cần phải xác định lớp nào – JPEGRenderer hoặc PNGRenderer sẽ được chọn.  Một FactoryClass sẽ đóng vai trò chọn ra class instance thich hợp.

Bài viết này chi gói gọn trong phạm vi trình bày cách sử dụng ClassFactory và ItemRenderer hơn là đi sâu vào phân tích tính chất của mẫu thiết kế Factory. Hơn nữa mẫu thiết kế này được phân tích rất chi tiết trong tài liệu ActionScript 3.0 Design Patterns.

ClassFactory

Flex ClassFactory cho phép tạo ra một instance của lớp bất kỳ với các thuộc tính giống nhau. Để khởi tạo một đối tượng của FactoryClass thì điều đầu tiên là bạn phải chỉ rõ đối tượng lớp nào cần được khởi tạo bằng cách truyền class vào hàm dựng của ClassFactory

var animalHasTailRenderer:ClassFactory = new ClassFactory(AnimalRenderer);
animalHasTailRenderer.properties = { hasTail: true };

Trong đoạn mã trên,  để tạo ra một class instance của animalHasTailRenderer thì Flex sẽ gọi phương thức newInstance() của đối tượng factory và thiết lập thuộc tính hasTail = true thông qua việc gán setter properties.

ItemRenderers

Khi tiếp cận với DataGrid, List … bạn sẽ va chạm rất nhiều với ItemRenderer . Người tiền nhiệm của ItemRenderer chính là cellRenderer AS2 – nếu bạn đã có kinh nghiệm sử dụng các component của Flash.

Ngữ cảnh sử dụng ItemRenderer thường được dùng với DataGrid – bạn cần đặt các checkbox hay combobox vào từng dòng của DataGrid chẳng hạn, hay mỗi dòng sẽ là một component khác nhau.  Bạn có thể xem một ví dụ điển hình của việc sử dụng itemRenderer như đoạn mã mxml dưới đây

<mx:DataGrid id="myGrid" dataProvider="{customerXMLList}">
     <mx:columns>
          <mx:DataGridColumn dataField="Company" itemRenderer="org.catapult.renderers.CompanyRenderer"/>
          <mx:DataGridColumn dataField="Contact" itemRenderer="org.catapult.renderers.ContactRenderer"/>
     </mx:columns>
</mx:DataGrid>

Một ItemRenderer có thể được tao từ mã ActionScript thuần túy hay mã MXML, tuy nhiên phải được implement từ IFactory. Lý do là bạn phải cài đặt phương thức newInstance để ClassFactory có thể gọi được.

ActionScript

package org.catapult.renderers {
     import mx.containers.VBox;
     import mx.core.IFactory;

     public class CompanyRenderer extends VBox implements IFactory {       
           public function newInstance():* {
                return new CompanyRenderer();
           }
     }
}

MXML

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" implements="mx.core.IFactory">
     <mx:Script>
           <![CDATA[
                public function newInstance():* {
                       return new CompanyRenderer();
                }
            ]]>
    </mx:Script>
</mx:VBox>

Bản chất của việc gán itemRenderer trong đoạn mã trên là việc tạo ra một class instance kiểu object factory.

Tham khảo:

1. ActionScript 3.0 Abstract Factory Design Pattern: Multiple Products and Factories

[Flex] Parsley, Inversion of Control (IoC) và Dependency Injection (DI)

Quan điểm của tôi là trước khi tìm hiều về Parsley thì cần phải hiểu về IoC (Inversion of Control) và DI (Dependency Injection). 2 khái niệm này không xa lạ với những lập trình viên server side, nhất là những ai sử dụng Java mà tiêu biểu là Spring. Tuy nhiên nếu chi đi từ việc phát triển UI/Client side như tôi thì 2 khái niệm này là khá mới mẻ …

Trong bài viết này, tôi giữ nguyên tên gọi IoC và DI mà không cố gắng tìm bản tiếng Việt vì quả thật là rất khó để có thể tìm một danh từ tiếng Việt cho IoC cũng như DI. Bạn hãy hiểu thông qua định nghĩa của chúng hơn là tìm một cái tên thuần Việt cho chúng.

IoC – Inversion of Control là gì ?

Về bản chất thì IoC có thể hiểu như việc khi bạn thiết kế 1 class thì class đó nên phụ thuộc vào một abstract class hay  interface chứ không phu thuộc (hoặc được dẫn xuất) từ một lớp cụ thể. IoC gắn liền với các ý tưởng thiết kế trong OOP, vì lập trình hướng thủ tục (POP – Procedure Oriented Programming), các module cấp phía trẹn lại phụ thuộc vào các module cấp dưới, và vấn đề là nếu các module cấp dưới có sự thay đổi thì phải  thay đổi cả các module cấp phía trên. Bạn có thể hiểu như là control của cấp trên phụ thuộc vào cấp dưới. Với OOP thì lập trình viên sẽ đi theo hướng thiết kế các lớp cấp cao phụ thuộc vào 1 abstract class hay interface, hơn là phụ thuộc (hay dẫn xuất) từ môt lớp cấp dưới, và khi đó tất cả sự thay đổi của các lớp cấp dưới đều không ảnh hưởng đến cấp phía trên … Như vây sự phụ thuộc được đảo ngược lại – khái niệm IoC có thể diễn gỉải theo cách này.

Với một framework thì các phương thức mà lập trình viên kế thừa từ framework sẽ được gọi bởi chính framework hơn là từ mã chương trình của lập trình viên viết. Việc điều phối, sắp xếp hoạt động của các luồng trong ứng dụng cũng do framework quản lý, và IoC cho phép một framework hoạt động như một hệ điều khiển mở rộng hơn là đóng vai trò chính điều khiển ứng dụng.

Với Cairngorm, PureMVC thì IoC được tiếp cận theo hướng mở rộng các lớp của framework và framework sẽ được định hướng cho biết các interface nào thích hợp để truy cập và thao tác các ứng dụng các đối tượng cụ thể. Parsley, Swiz cung cấp metadata annotation để móc nối với hoạt động của framwork.

DI – Dependency Injection là gì ?

Thường thì bạn sẽ định nghĩa các đối tượng trong ứng dụng một cách cụ thể trong chính mã nguồn. Với DI, đó là một cơ chế bên ngoài “tiêm” một biến định nghĩa instance của một đối tượng. DI thường được áp dụng trong Unitest, cho phép bạn tạo ra các đối tượng có quan hệ lỏng lẻo với nhau và dễ dàng thay thế các đối tượng “demo” mà bạn phải hardcode sẵn trong mã nguồn. Quá trình test sẽ kiểm định các đối tượng một cách độc lập chứ không quan tâm đến mối quan hệ giữa các đối tượng khác nhau.

Ngoài ra, việc áp dụng DI cũng tăng khả năng tái sử dụng mã nguồn, vì khi việc phụ thuộc giữa các đối tượng không còn được hard code trực tiếp trong mã nguồn nữa thì quá trình tái sử dụng có thể dễ dàng hơn, vì đơn giản là thay đổi các đối tượng được “tiêm” từ bên ngoài vào mà thôi …

Các tính năng của Parsley

Để minh họa cho việc cài đặt IoC và DI, trong bài tiếp theo tôi sẽ nói về framework Parsley với một ứng dụng nhỏ để minh họa. Có thể điểm qua các chức năng của Parsley như sau:

  • Flexible IOC Container: Hỗ trợ cấu hình với AS3 Metadata, MXML, XML , ActionScript
  • Dependency Injection: “tiêm” theo kiểu hoặc id cho cả hàm dựng (constructor), phương thức, thuộc tính …
  • Decoupled Bindings: Thông qua cơ chế đăng ký, Parsley cho phép binding dữ liệu khi nguồn dữ liệu và đối tượng đích tách biệt nhau.
  • Messaging Framework: đối tượng gửi và nhận hoàn toàn tách rời, có thể làm cơ sở cho kiến trúc MVC
  • Dynamic View Wiring:  Tạo kết giữa các  component Flex cho các đối tượng khai báo trong container
  • Advanced container features: Bất đồng bộ khởi tạo các đối tượng, vòng đời đối tượng, ngữ cảnh  cấu hình mô-đun
  • Integration with Flex Modules: Cho phép cấu hình được nạp và giải phóng cùng với module Flex
  • Localization: Tích hợp với Flex ResourceManager cho các ứng dụng Flex, ngoài ra còn có Framework dành riêng cho việc localization (bản địa hóa) cho các ứng dụng Flash.
  • Extensibility: Dễ dàng tạo ra các thẻ cấu hình do chính lập trình viên định nghĩa và có thể được sử dụng như AS3 Metadata, MXML hoặc tag XML.