Mở một Activity bằng CÁCH MỚI - LINH HOẠT hơn
Bài đăng này đã không được cập nhật trong 6 năm
Việc mở nhiều Activity và pass Data để thực hiện một công việc nào đó, nó không còn xa lạ gì với những Android Developer. Nhưng nếu cứ thực hiện liên tục 1 công việc lặp đi lặp lại sẽ thật buồn chán, tất nhiên sẽ có nhiều phản biện rằng: cách đó là tốt rồi. Điều này sẽ được đề cập từ từ được không nhỉ ^^ Chúng ta cùng nhau tìm ra một cách làm MỚI và xem nó có sự khác biệt gì nha.
Trước tiên mình cũng nêu ra mấy cách làm thông thường ta hay dùng thông qua một Ví dụ để trực quan nhất.
Ví dụ:
Một màn hình sẽ hiển thị thông tin (như hình dưới) đây là Activity TomDetailActivity
, tại đây cần show mấy thông tin được nhận từ MainActivity
truyền sang.
1. Cách làm cũ
Sử dụng Intent và Bundle
Đây là một cách làm thường xuyên nhất mà mỗi Developer hay dùng, nó cũng khá đơn giản để áp dụng. Nếu mà chia nhỏ ra thì sẽ có 2 cách.
Intent :
MainActivity.java
private void serializeTomData() {
Intent intent = new Intent(this, TomDetailActivity.class);
intent.putExtra(Common.NAME_KEY, "Dr. Tom Pro");
intent.putExtra(Common.NAME_KEY, "Eat. Climbing. Love mouse.");
intent.putExtra(Common.NAME_KEY, R.drawable.ic_avatar_tom);
startActivity(intent);
}
Bundle:
MainActivity.java
private void serializeTomDataWithBundle() {
Intent intent = new Intent(this, TomDetailActivity.class);
Bundle bundle = new Bundle();
bundle.putString(Common.NAME_KEY, "Dr. Tom Pro");
bundle.putString(Common.NAME_KEY, "Eat. Climbing. Love mouse.");
bundle.putInt(Common.NAME_KEY, R.drawable.ic_avatar_tom);
intent.putExtra(Common.EXTRA_TOM, bundle);
startActivity(intent);
}
Với 2 cách làm trên thì đều có chung 1 cách nhận data ở bên màn TomDetailActivity
và code dưới đây mình xử lý trong method onCreate()
, mình đã note trong comment code luôn
TomDetailActivity.java
public class TomDetailAct extends AppCompatActivity {
private String name;
private String shortBio;
private int idAvatar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tom_detail);
Intent intent = getIntent();
/*
* Apply for Bundle way
* bundle != null
* name = bundle.getString(Common.NAME_KEY)
* */
// Bundle bundle = intent.getBundleExtra(Common.EXTRA_TOM);
// if (bundle == null) return;
name = intent.getStringExtra(Common.NAME_KEY);
shortBio = intent.getStringExtra(Common.SHORT_BIO_KEY);
idAvatar = intent.getIntExtra(Common.PROFILE_PHOTO_ID, 0);
}
}
Serializable & Parcelable
Thiết nghĩ rằng đây là một cải tiến khá hay trong Passing Data, chúng ta thường áp dụng cho Object có nhiều thuộc tính và có nhiều ưu điểm hơn so với cách làm trên: như tốc độ truyền,... Bạn có thể đọc thêm về Parcelable Cả 2 cách dùng này Android đều support bằng việc dùng Intent hoặc Bundle đều được. Và ở đây mình sẽ demo code dùng Serializable, với Parcelable nếu bạn chưa biết thì đọc tài liệu ở trên rồi làm tương tự như cách này của mình là được thôi.
Trước tiên bạn có một Object chung có các thông tin cần truyền (name, bio, avatarId) , gọi chung đó là User.class
User.java
public class User implements Serializable {
private String name;
private String shortBio;
private int photoId;
public User(String name, String shortBio, int photoId) {
this.name = name;
this.shortBio = shortBio;
this.photoId = photoId;
}
}
Bạn sửa lại MainActivity và TomDetailActivity một chút như sau:
MainActivity.java
private void serializeTomData() {
Intent intent = new Intent(this, TomDetailActivity.class);
User user = new User("Dr. Tom Pro", "Eat. Climbing. Love mouse.", R.drawable.ic_avatar_tom);
intent.putExtra(Common.EXTRA_TOM, user);
startActivity(intent);
}
TomDetailActivity.java
public class TomDetailActivity extends AppCompatActivity {
private User mUser;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tom_detail);
Intent intent = getIntent();
mUser = (User) intent.getSerializableExtra(Common.EXTRA_TOM);
}
}
=> Nhận xét qua 4 cách làm trên:
- Serialization : putExtra(key, value)
- Deserialization: getStringExtra(key)
Đó là những điểm chung ta có thể thấy ngay, nhưng nếu chúng ta có đến vài chục hay hàng trăm Activity như trên mà mỗi cái có 1 sự sai sót về dữ liệu (chưa hề biết được là do bên truyền hay bên nhận). Như thế sẽ mất gấp đôi thời gian để checking. Đó là về lý thuyết còn trên thực tế effort cho việc debug nó có thể lớn hơn nhiều đó. Trước thực tế như vậy, chúng ta cùng xem giải pháp tiếp theo sẽ là gì ?
2. CÁCH LÀM MỚI
Từ nhận xét ở trên điểm chung quan trọng nhất : putExtra(key, value) rồi lấy dữ liệu ra : getStringExtra(key)
Vậy mình sẽ tạo ra 1 interface có 2 method intent
: lấy ra intent để mở ra activity mới và method launch
: khởi động chính activity đó. Interface này được gọi là ActivityArgs
ActivityArgs.kt
interface ActivityArgs {
/**
* @return returns an intent that can be used to launch this activity.
*/
fun intent(activity: Context): Intent
/**
* Launches the activity given your activity context.
*
* The default implementation uses the intent generated from [intent]
*/
fun launch(activity: Context) = activity.startActivity(intent(activity))
}
Tiếp theo, tạo một class thực thi bước quan trọng ở trên : TomProfileActivityArgs
, tại đây bạn xử lý logic về việc convert data phức tạp hay chỉ đơn giản là passing - receiving.
TomProfileActivityArgs.kt
/**
* Arguments to launch [TomDetailActivity]
*/
private const val NAME_KEY = "name_key"
private const val SHORT_BIO_KEY = "short_bio_key"
private const val PROFILE_PHOTO_ID = "profile_photo_id_drawable"
data class TomProfileActivityArgs(
var name: String,
var shortBio: String,
var profilePhotoId: Int
) : ActivityArgs {
override fun intent(activity: Context): Intent = Intent(
activity, TomDetailActivity::class.java
).apply {
putExtra(NAME_KEY, name)
putExtra(SHORT_BIO_KEY, shortBio)
putExtra(PROFILE_PHOTO_ID, profilePhotoId)
}
companion object {
fun deserializeFrom(intent: Intent): TomProfileActivityArgs {
return TomProfileActivityArgs(
name = intent.getStringExtra(NAME_KEY),
shortBio = intent.getStringExtra(SHORT_BIO_KEY),
profilePhotoId = intent.getIntExtra(PROFILE_PHOTO_ID, 0)
)
}
}
}
Việc còn lại khá đơn giản, hãy launch activity TomDetailActivity ở bất kì đâu bạn muốn. Chỉ việc gọi method launch(context) như đã định nghĩa thôi. Mình sẽ thực hiện nó trong MainActivity code dưới đây
MainActivity.kt
@SuppressLint("Registered")
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_detail.setOnClickListener({ goToTomProfile() })
}
private fun goToTomProfile() {
TomProfileActivityArgs(
"Dr. Tom Pro",
"Eat. Climbing. Love mouse.",
R.drawable.ic_avatar_tom
)
.launch(this)
}
}
Bên TomDetailActivity gọi method deserializeFrom(intent) để lấy data ra thôi.
TomDetailActivity.kt
@SuppressLint("Registered")
class TomDetailActivity : AppCompatActivity() {
private val args by lazy {
TomProfileActivityArgs.deserializeFrom(intent)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tom_detail)
avatar.setImageResource(args.profilePhotoId)
name.setText(args.name)
bio.setText(args.shortBio)
}
}
3. Demo
Mình cũng đã thực hiện 1 demo để cách bạn dễ dàng theo dõi trực tiếp
4. Tổng kết
Với cách làm mới này mình có thể dễ dàng care những sai sót trong quá trình truyền dữ liệu và tiết kiệm effort rất nhiều, vì mình chỉ quan tâm tới những class mang tên ActivityArgs như : TomDetailActivityArgs
Maintain sẽ thuận tiện hơn rất nhiều khi có sự thay đổi yêu cầu, hiển thị thêm thông tin hoặc giảm bớt thông tin đi, chỉnh sửa convention name key, v.v.. những việc đó rất dễ dàng.
Cuối cùng mình hy vọng phương pháp trên đem lại sự hữu ích và thú vị cho các bạn!!!
All rights reserved