Android Bài 29: Truyền Dữ Liệu Qua Lại Giữa Các Activity

Posted by

Chào mừng các bạn đã đến với bài học Android thứ 29, bài học về làm cách nào truyền dữ liệu qua lại giữa các Activity. Đây là bài học trong chuỗi bài viết về lập trình ứng dụng Android bằng Java của Yellow Code Books.

Cho đến bài học hôm nay, chắc hẳn các bạn đã rất tự tin với việc thao tác với Activity rồi. Tuy nhiên Activity vẫn còn nhiều điều hay ho khác mà bạn vẫn nên biết. Một trong những điều đó chính là việc làm thế nào mà các Activity có thể “nói chuyện” được với nhau, hay nói cách khác là truyền dữ liệu cho nhau.

Nếu bạn là người mới tiếp cận Android, ngay lúc này đây, có thể bạn vẫn chưa thấy được nhu cầu từ việc truyền dữ liệu qua lại giữa các Activity. Nhưng tin mình đi, mình và những bạn có kinh nghiệm khác đều đã gặp phải nhu cầu này khá sớm. Ngay cả TourNote cũng cần phải xây dựng chức năng truyền dữ liệu qua lại giữa hai Activity rồi đấy. Nếu thấy thú vị thì mời các bạn cùng xem tiếp bài học nhé.

Tại Sao Phải Truyền Dữ Liệu Qua Lại Giữa Các Activity?

Bạn cũng đã biết, mỗi Activity chính là một màn hình của ứng dụng (cho đến bài học hôm nay thì đúng là vậy). Và bạn cũng đã thấy mỗi Activity là một đơn vị độc lập, chẳng hạn mỗi chúng đều có một vòng đời khác nhau. Như vậy sẽ rất khó nếu như chúng không có một cơ chế nào đó truyền đạt dữ liệu qua cho nhau.

Mình giả sử bạn đang xây dựng ứng dụng email. Trong ứng dụng của bạn có một Activity hiển thị danh sách tóm tắt các email đến, giả sử Activity này có tên là ListActivity. Và một Activity hiển thị nội dung cụ thể của từng email, giả sử tên của nó là DetailActivity. Ban đầu khi mở ứng dụng, người dùng sẽ nhìn thấy ListActivity trước, khi họ nhấn vào một email trong danh sách các email ở Activity này, ứng dụng sẽ kích hoạt DetailActivity, và truyền thông tin của item mà người dùng đã nhấn để DetailActivity hiển thị nội dung email tương ứng. Tiếp theo nếu người dùng đọc xong email đó ở DetailActivity mà không thấy email quan trọng, họ có thể nhấn nút xóa email, khi này ứng dụng sẽ quay lại ListActivity, và có thông tin trả về từ DetailActivity cho biết email đã bị xóa, ListActivity sẽ xóa item tương ứng với thông tin trả về đó.

Bạn thấy dó, một ví dụ nhỏ thôi mà có khá nhiều lý do để truyền dữ liệu qua lại giữa các Activity rồi. Và thực tế kinh nghiệm của mình thấy rằng hầu hết các Activity trong ứng dụng đều cần kiến thức của bài hôm nay cả.

Vậy chúng ta cùng xem cụ thể cách để truyền dữ liệu qua lại giữa các Activity như thế nào nhé.

Cách Truyền Dữ Liệu Qua Lại Giữa Các Activity

Nếu bạn đã thử khai báo các thuộc tính static trong ứng dụng để lưu trữ các dữ liệu mà tất cả các Activity có thể đọc được và chỉnh sửa được. Và bạn cho rằng thuộc tính static này là cách trao đổi dữ liệu giữa các Activity, thì bạn đã sai rồi. Các thuộc tính static trong ứng dụng chỉ nên là các hằng số, hoặc các giá trị dùng chung khác như các link ví dụ mình để ở đây cho bạn xem. Nếu bạn khai báo một thuộc tính static, và set giá trị cho nó, rồi bạn kích hoạt một Activity khác có sử dụng dữ liệu ở thuộc tính static này, sẽ không chắc dữ liệu đó đúng như bạn mong muốn đâu. Một lần nữa, đừng dùng thuộc tính static để chia sẻ dữ liệu giữa các Activity nhé.

Chúng ta chỉ có duy nhất một cách để truyền dữ liệu qua lại giữa các Activity. Đó là cách “nhét” dữ liệu vào Intent và nhờ thành phần này chuyển giúp. Hệ thống sẽ đảm bảo dữ liệu được gửi qua “nguyên vẹn” và kịp thời ở Activity mới.

Ồ, vậy Intent ngoài việc kích hoạt các thành phần của Android, trong đó có Activity như bạn đã biết, nay lại có công năng của “người vận chuyển” nữa.

Loại Dữ Liệu Được Sử Dụng Trong Intent

Ở mục trên bạn đã biết rằng, Intent có tác dụng vận chuyển dữ liệu qua các Activity. Vậy dữ liệu sẽ được tổ chức bên trong Intent như thế nào?

Dữ liệu được “nhét” vào trong Intent và được lấy ra khỏi Intent theo các cặp dữ liệu dạng key/value. Key ở đây là một chuỗi, giúp định danh cho dữ liệu value. Nếu bạn để vào trong Intent cặp key/value nào, thì bạn phải lấy ra bởi cặp key/value đó, phải đảm bảo khai báo đúng key và lấy ra đúng kiểu dữ liệu của value khi để vào. Điều này tương tự như khi bạn di chuyển đi chơi xa, thì khi đóng gói hành lý của bạn, nhân viên tiếp nhận hành lý phải dán nhãn tên của bạn hay ID của bạn lên hành lý, để đảm bảo bạn lấy đúng hành lý (chính là value) khi đến nơi dựa vào nhãn tên hay ID đó (chính là key).

Tuy có một cách thôi, nhưng bạn có thể sử dụng một trong hai hình thức sau. Một là sử dụng Extra, hoặc sử dụng Bundle. Sự khác nhau giữa hai cách này theo mình là không quan trọng, bạn nên biết cả hai cách, và sẽ có lời khuyên của mình nên sử dụng cách nào ở bên dưới.

Dùng Extra

Có thể nói, truyền nhận dữ liệu bằng Extra là cách dễ nhất.

Gửi Dữ Liệu

Đầu tiên, để gửi dữ liệu bằng Extra. Sau khi khai báo Intent và trước khi bạn dùng nó để kích hoạt activity nào đó, bạn có thể sử dụng các phương thức được nạp chồng của nó để gửi dữ liệu. Các phương thức đó có chung một tên là putExtra().

Truyền dữ liệu qua lại giữa các Activity - Đặt dữ liệu vào Extra

Bạn nhớ là các phương thức putExtra() này không có s đằng sau Extra nhé. Extras sẽ dành cho mục Bundle dưới đây.

Với mỗi putExtra() như vậy, tham số đầu tiên chính là key mà mình có nói trên kia. Tham số thứ hai tương tự chính là value. Phương thức này được nạp chồng làm nhiều bản để bạn dễ dàng sử dụng từng loại value mà bạn muốn. Tuy nhiên bạn đừng để ý hai kiểu valueParcelableSerializable, hai kiểu này hơi phức tạp một chút, chúng ta sẽ có một bài viết riêng về hai kiểu này khi thích hợp.

Đoạn code sau ví dụ cách để đặt dữ liệu vào Intent bằng Extra.

Intent intent = new Intent(this, ContactActivity.class);
intent.putExtra("Key_1", "Truyền một String");  // Truyền một String
intent.putExtra("Key_2", 5);                    // Truyền một Int
intent.putExtra("Key_3", true);                 // Truyền một Boolean
startActivity(intent);

Nhận Dữ Liệu

Khi này, theo như ví dụ trên thì ContactActivity sẽ được kích hoạt với dữ liệu là ba cặp key/value được truyền qua. Ở phương thức onCreate() hoặc bất cứ chỗ nào của ContactActivity, bạn đều có thể lấy bất cứ cặp key/value nào ra dùng. Bằng cách gọi đến getXxxExtra().

Truyền dữ liệu qua lại giữa các Activity - Lấy dữ liệu khỏi Extra

Mình ghi chung là Xxx, vì Xxx này sẽ được bạn thay thế bằng kiểu dữ liệu phù hợp với key bên “đóng gói”, như getBooleanExtra(), getStringExtra(), getIntExtra(),… Dĩ nhiên tham số name truyền vào phương thức này phải đúng là key bên đóng gói luôn.

Một số phương thức cần phải có tham số thứ hai, tham số này chính là dữ liệu mặc định nếu như hệ thống không tìm thấy dữ liệu với key mà bạn cung cấp. Việc cung cấp tham số thứ hai này tránh một số lỗi xảy ra đối với chương trình của chúng ta.

Đoạn code sau minh họa cách lấy dữ liệu ra khỏi Intent bằng ExtraonCreate() của Activity. Bạn có thể thấy từng cặp key/value khớp với khi bạn đặt dữ liệu vào trên kia.

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_contact);

	// Các dòng code khác...

	Intent intent = getIntent();
	String value1 = intent.getStringExtra("Key_1");
	int value2 = intent.getIntExtra("Key_2", 0);
	boolean value3 = intent.getBooleanExtra("Key_3", false);
}

Dùng Bundle

Thực ra BundleExtra không khác gì nhau hết. Nếu như Extra trên kia sẽ “xé lẻ” dữ liệu ra và gởi theo từng dòng. Thì Bundle sẽ giúp bạn “đóng gói” dữ liệu lại và gởi nguyên kiện. Tất nhiên Bundle sẽ tiện hơn trong trường hợp bạn muốn gởi cùng một bộ dữ liệu đến nhiều Activity khác nhau.

Ngoài nhiệm vụ đóng gói dữ liệu để truyền qua lại giữa các Activity ở bài học này, thì Bundle còn dùng trong một số mục đích khác, đơn cử như truyền dữ liệu qua Fragment mà bạn sẽ được biết ở bài sau. Nên tốt hơn hết bạn nên học cách sử dụng Bundle ngay từ bài này nhé.

Gửi Dữ Liệu

Chỉ có phát sinh vài dòng code so với Extra trên kia thôi, đầu tiên là dòng tạo ra Bundle. Sau đó vẫn là các dòng đặt dữ liệu vào Bundle, các dòng này có hơi khác với các dòng đặt dữ liệu vào Extra một chút, nếu với Extra bạn dùng các phương thức nạp chồng với cùng một tên putExtra() thì với Bundle bạn phải dùng đúng phương thức putXxx() với Xxx là kiểu dữ liệu bạn cần dùng.

Truyền dữ liệu qua lại giữa các Activity - Đặt dữ liệu vào Bundle

Khi Bundle đã chứa đủ dữ liệu, bạn cần phải đặt Bundle này vào trong Intent bằng một dòng code putExtras() (nhớ là có s nhé). Bạn xem code như sau.

Intent intent = new Intent(this, ContactActivity.class);
Bundle bundle = new Bundle();
bundle.putString("Key_1", "Truyền một String");	// Truyền một String
bundle.putInt("Key_2", 5);                    	// Truyền một Int
bundle.putBoolean("Key_3", true);             	// Truyền một Boolean
intent.putExtras(bundle);
startActivity(intent);

Nhận Dữ Liệu

Cũng tương tự như bên gửi thôi, nếu đã gửi theo Bundle, thì bên nhận cũng sẽ nên nhận theo Bundle trước rồi mới lấy từng dữ liệu ra dùng. Để lấy Bundle ra khỏi Intent thì chúng ta có phương thức getExtras().

Sau khi lấy Bundle ra khỏi Intent, việc tiếp theo sẽ gọi đến các phương thức getXxx() của nó. Các phương thức này của Bundle cũng giống như các phương thức getXxxExtra() của Extra trên kia. Chỉ khác một chỗ getXxx() của Bundle thường có hai phương thức nạp chồng, giúp bạn linh động hơn. Thường thì bạn nên dùng getXxx() với hai tham số, như vậy bạn có thể định nghĩa được giá trị mặc định cho từng phương thức khi mà nó không tìm thấy dữ liệu từ key mà bạn cung cấp, giúp tránh một số lỗi không cần thiết.

Truyền dữ liệu qua lại giữa các Activity - Lấy dữ liệu khỏi Bundle

Để chắc chắn thì khi nhận dữ liệu với Bundle, bạn nên kiểm tra xem Bundle đó có tồn tại hay không (kiểm tra khác null) trước nhé.

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_contact);

	// Các dòng code khác...

	Intent intent = getIntent();
	Bundle bundle = intent.getExtras();
	if (bundle != null) {
		String value1 = bundle.getString("Key_1", "");
		int value2 = bundle.getInt("Key_2", 0);
		boolean value3 = bundle.getBoolean("Key_3", false);
	}
}

Bạn vừa xem qua cách thức chuyển dữ liệu qua lại giữa các Activity. Giờ là lúc chúng ta thực hành xây dựng một chút cho TourNote rồi.

Thực Hành Gửi Dữ Liệu Từ MainActivity Qua ContactActivity

mục thực hành của bài 26 chúng ta đã xây dựng sự kiện nhấn cho hai menu item trên ActionBar của MainActivity, sao cho khi người dùng nhấn vào About App hay Help cũng sẽ mở ra ContactActvitity cả. Như hình dưới đây.

Truyền dữ liệu qua lại giữa các Activity - Hình ảnh TourNote trước khi thực hành

Thực ra ContactActivity mà mình muốn hướng đến sẽ chứa hai loại nội dung. Một là thông tin về ứng dụng, hai là thông tin giúp đỡ. Việc phân biệt nội dung nào được hiển thị sẽ dựa vào lựa chọn của người dùng trên menu item của MainActivity. Chính vì vậy chúng ta sẽ xây dựng ContactActivity sao cho có thể nhận được dữ liệu từ MainActivity chuyển qua, dữ liệu này chỉ đơn giản báo cho ContactActivity biết người dùng vừa nhấn chọn About App hay Help.

Bạn đã hiểu yêu cầu của bài thực hành hôm nay rồi đúng không nào. Nên nhớ là chúng ta chỉ nói đến cách thức gửi nhận dữ liệu để ContactActivity hiểu được chuyện gì đang xảy ra thôi nhé, do đó bài này chúng ta chỉ sử dụng Toast để kiểm chứng dữ liệu nhận. Còn việc hiển thị cái gì khi đã nhận dữ liệu thì ở bài sau chúng ta sẽ xây dựng tiếp.

Xây Dựng Các Giá Trị Hằng Số

Bạn đã biết việc sử dụng Extra hay Bundle là việc lưu trữ các dữ liệu dạng key/value rồi chuyển đi và nhận chúng ở nơi khác đúng không nào. Vậy để đảm bảo các key luôn luôn được dùng đúng, chúng ta nên định nghĩa nó là một giá trị tĩnh (static)hằng (final). Giá trị này nên định nghĩa bên lớp nhận dữ liệu thì tốt hơn về mặt quản lý code (theo ý mình thôi nhé, bạn có thể định nghĩa một lớp chuyên chứa các giá trị key này). Do đó mình thêm các thuộc tính được tô sáng sau vào ContactActivity.

public class ContactActivity extends AppCompatActivity {

    public static final String KEY_SHOW_WHAT = "show_what";
    public static final String VALUE_SHOW_ABOUT = "show_about";
    public static final String VALUE_SHOW_HELP = "show_help";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Gửi Dữ Liệu Từ MainActivity

Dĩ nhiên bên MainActivity, chúng ta chỉnh sửa một tí nơi kích hoạt ContactActivity, sao cho Intent có thể chứa Bundle mà chúng ta muốn như sau.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
	if(drawerToggle.onOptionsItemSelected(item)) {
		return true;
	}

	switch (item.getItemId()) {
		case R.id.search:
			Toast.makeText(this, "Search button selected", Toast.LENGTH_SHORT).show();
			return true;
		case R.id.about:
			Intent intent = new Intent(this, ContactActivity.class);
			Bundle bundle = new Bundle();
			bundle.putString(ContactActivity.KEY_SHOW_WHAT, ContactActivity.VALUE_SHOW_ABOUT);
			intent.putExtras(bundle);
			startActivity(intent);
			return true;
		case R.id.help:
			intent = new Intent(this, ContactActivity.class);
			bundle = new Bundle();
			bundle.putString(ContactActivity.KEY_SHOW_WHAT, ContactActivity.VALUE_SHOW_HELP);
			intent.putExtras(bundle);
			startActivity(intent);
			return true;
	}

	return super.onOptionsItemSelected(item);
}

Nhận Dữ Liệu Ở ContactActivity

Như mình có nói, ContactActivity chỉ nhận dữ liệu rồi dùng Toast để show ra làm bằng chứng là nó đã nhận đúng dữ liệu. Còn việc hiển thị cái gì thì bài học sau chúng ta sẽ nói tiếp nhé. Như vậy code của ContactActivity như sau.

public class ContactActivity extends AppCompatActivity {

    public static final String KEY_SHOW_WHAT = "show_what";
    public static final String VALUE_SHOW_ABOUT = "show_about";
    public static final String VALUE_SHOW_HELP = "show_help";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            String valueShow = bundle.getString(KEY_SHOW_WHAT, "");
            Toast.makeText(this, "Show value: " + valueShow, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Kết quả là, tùy vào cách người dùng chọn vào menu item mà ContactActivity sẽ hiển thị Toast như sau.

Truyền dữ liệu qua lại giữa các Activity - Hình ảnh TourNote sau khi thực hành

Download Source Code Mẫu

Bạn có thể download source code mẫu của bài này ở đây.

Chúng ta vừa biết thêm kiến thức thú vị nữa về Activity trong Android. Dĩ nhiên sẽ còn nhiều kiến thức khác mà bạn cũng sẽ thấy thích. Hãy tiếp tục theo dõi các bài học của mình nhé.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:

Đánh giá 5 sao bên dưới mỗi bài nếu thấy thích.
Comment bên dưới mỗi bài nếu có thắc mắc.
Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.

Bài Kế Tiếp

Chúng ta sẽ nói đến việc một màn hình có thể linh động hiển thị nhiều thành phần con theo từng ngữ cảnh, bằng việc tìm hiểu kiến thức về Fragment.

Advertisements
Rating: 4.9/5. From 7 votes.
Please wait...

2 comments

Gửi phản hồi