在同一個(gè)地方跌倒兩次棘伴,才能體會(huì)到“好記性不如爛筆頭”!
一、Bundle簡(jiǎn)介
??bundle在Android開(kāi)發(fā)中非常常見(jiàn)驶悟,它的作用主要時(shí)用于傳遞數(shù)據(jù);它所保存的數(shù)據(jù)是以key-value(鍵值對(duì))的形式存在的,也就是說(shuō)bundle是保存數(shù)據(jù)的容器材失,內(nèi)部使用了Arraymap去存儲(chǔ)數(shù)據(jù)痕鳍,也提供了很多get,put方法龙巨。
??bundle傳遞的數(shù)據(jù)包括:string笼呆、int、boolean旨别、byte诗赌、float、long昼榛、double等基本類(lèi)型或它們對(duì)應(yīng)的數(shù)組境肾,也可以是對(duì)象或?qū)ο髷?shù)組。當(dāng)bundle傳遞的是對(duì)象或?qū)ο髷?shù)組時(shí)胆屿,必須實(shí)現(xiàn)Serialiable或Parcelable接口奥喻。
??bundle主要用于以下3個(gè)場(chǎng)合:
??1. Activity狀態(tài)數(shù)據(jù)的保存與恢復(fù),涉及到兩個(gè)回調(diào):①void onSaveInstanceState(Bundle outState)非迹;② void onCreate(Bundle savedInstanceState)环鲤;
??2. Fragment的setArguments方法:void setArgument(Bundle args);
??3. 消息機(jī)制中的Message的setData方法:void setData(Bundle data)憎兽。
二冷离、Bundle源碼解析
- 首先看下Bundle的聲明:
public final class Bundle extends BaseBundle implements Cloneable, Parcelable
??從聲明中我們可以看出:①它使用了final進(jìn)行修飾,所以不可以被繼承纯命;②它實(shí)現(xiàn)了兩個(gè)接口Cloneable和Parcelable西剥,這就意味著它必須實(shí)現(xiàn)以下方法:
??1. public Object clone()
??2. public int describeContents()
??3. public void writeToParcel(Parcel parcel, int flags)
??4. public void readFromParcel(Parcel parcel)
??5. public static final Parcelable.Creator<Bundle> CREATOR = new Parcelable.Creator<Bundle>() - 再看bundle的內(nèi)存結(jié)構(gòu):
ArrayMap<String, Object> mMap = null
??它使用的是ArrayMap,這個(gè)集合類(lèi)存儲(chǔ)的也是鍵值對(duì)亿汞,但是與Hashmap不同的是瞭空,hashmap采用的是“數(shù)組+鏈表”的方式存儲(chǔ),而Arraymap中使用的是兩個(gè)數(shù)組進(jìn)行存儲(chǔ)疗我,一個(gè)數(shù)組存儲(chǔ)key咆畏,一個(gè)數(shù)組存儲(chǔ)value,內(nèi)部的增刪改查都將會(huì)使用二分查找來(lái)進(jìn)行吴裤,這個(gè)和SparseArray差不多旧找,只不過(guò)sparseArray的key值只能是int型的,而Arraymap可以是map型麦牺,所以在數(shù)據(jù)量不大的情況下可以使用這兩個(gè)集合代替hashmap去優(yōu)化性能钮蛛;
三鞭缭、Bundle繼承的方法
??Bundle操作的基本數(shù)據(jù)類(lèi)型如下表所示,它們都繼承自BaseBundle (From class android.os.BaseBundle )
返回類(lèi)型 | 函數(shù) | 函數(shù)說(shuō)明 |
---|---|---|
void | clear() | Removes all elements from the mapping of this Bundle. |
boolean | containsKey(String key) | Returns true if the given key is contained in the mapping of this Bundle. |
object | get(String key) | Returns the entry with the given key as an object. |
boolean | getBoolean(String key, boolean defaultValue) | Returns the value associated with the given key, or defaultValue if no mapping of the desired type exists for the given key. |
boolean | getBoolean(String key) | Returns the value associated with the given key, or false if no mapping of the desired type exists for the given key. |
boolean[] | getBooleanArray(String key) | Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key. |
double | getDouble(String key, double defaultValue) | Returns the value associated with the given key, or defaultValue if no mapping of the desired type exists for the given key. |
double | getDouble(String key) | Returns the value associated with the given key, or 0.0 if no mapping of the desired type exists for the given key. |
double[] | getDoubleArray(String key) | Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key. |
int | getInt(String key) | Returns the value associated with the given key, or 0 if no mapping of the desired type exists for the given key. |
int | getInt(String key, int defaultValue) | Returns the value associated with the given key, or defaultValue if no mapping of the desired type exists for the given key. |
int[] | getIntArray(String key) | Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key. |
long | getLong(String key) | Returns the value associated with the given key, or 0L if no mapping of the desired type exists for the given key. |
long | getLong(String key, long defaultValue) | Returns the value associated with the given key, or defaultValue if no mapping of the desired type exists for the given key. |
long[] | getLongArray(String key) | Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key. |
String | getString(String key) | Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key. |
String | getString(String key, String defaultValue) | Returns the value associated with the given key, or defaultValue if no mapping of the desired type exists for the given key or if a null value is explicitly associated with the given key. |
String[] | getStringArray(String key) | Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key. |
boolean | isEmpty() | Returns true if the mapping of this Bundle is empty, false otherwise. |
Set<String> | keySet() | Returns a Set containing the Strings used as keys in this Bundle. |
void | putAll(PersistableBundle bundle) | Inserts all mappings from the given PersistableBundle into this BaseBundle. |
void | putBoolean(String key, boolean value) | Inserts a Boolean value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putBooleanArray(String key, boolean[] value) | Inserts a boolean array value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putDouble(String key, double value) | Inserts a double value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putDoubleArray(String key, double[] value) | Inserts a double array value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putInt(String key, int value) | Inserts an int value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putIntArray(String key, int[] value) | Inserts an int array value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putLong(String key, long value) | Inserts a long value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putLongArray(String key, long[] value) | Inserts a long array value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putString(String key, String value) | Inserts a String value into the mapping of this Bundle, replacing any existing value for the given key. |
void | putStringArray(String key, String[] value) | Inserts a String array value into the mapping of this Bundle, replacing any existing value for the given key. |
void | remove(String key) | Removes any entry with the given key from the mapping of this Bundle. |
int | size() | Returns the number of mappings contained in this Bundle. |
四愿卒、構(gòu)造方法
- Constructs a new, empty Bundle.
Bundle()
- Constructs a new, empty Bundle that uses a specific ClassLoader for instantiating Parcelable and Serializable objects.
Bundle(ClassLoader loader)
- Constructs a new, empty Bundle sized to hold the given number of elements. The Bundle will grow as needed.
Bundle(Int capacity)
- Constructs a Bundle containing a copy of the mappings from the given Bundle. Does only a shallow copy of the original Bundle.
Bundle(Int b)
- Constructs a Bundle containing a copy of the mappings from the given PersistableBundle. Does only a shallow copy of the PersistableBundle.
Bundle(PersistableBundle b)
五缚去、實(shí)戰(zhàn)練習(xí)
1. 在Activity to Activity傳遞數(shù)據(jù)時(shí)使用Bundle
① 當(dāng)傳遞簡(jiǎn)單數(shù)據(jù)時(shí)
??新建一個(gè)MainActivity,對(duì)應(yīng)的布局文件比較簡(jiǎn)單,就是一個(gè)Button琼开,點(diǎn)擊這個(gè)按鈕后,程序跳轉(zhuǎn)到SecondActivity枕荞,并將傳遞的數(shù)據(jù)在SecondActivity的TextView中顯示出來(lái)柜候。這樣,就使用Bundle實(shí)現(xiàn)了數(shù)據(jù)在Activity之間的傳遞躏精。
package com.example.bundletest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
//聲明控件對(duì)象
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲取控件的對(duì)象
mButton = findViewById(R.id.button);
//為Button綁定監(jiān)聽(tīng)器
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 存入數(shù)據(jù)
*/
//實(shí)例化一個(gè)Bundle
Bundle bundle = new Bundle();
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//設(shè)置數(shù)據(jù)
String name = "Trump";
int num = 123;
//把數(shù)據(jù)放入到Bundle容器中
bundle.putString("Name", name);
bundle.putInt("Num", num);
//把Bundle容器中的數(shù)據(jù)放到Intent中
intent.putExtra("Message", bundle);
//啟動(dòng)該Intent渣刷,實(shí)現(xiàn)Activity的跳轉(zhuǎn)
startActivity(intent);
}
});
}
}
??新建一個(gè)SecondActivity,用于顯示傳遞的數(shù)據(jù)矗烛。對(duì)應(yīng)的布局文件也很簡(jiǎn)單辅柴,就是一個(gè)TextView。
package com.example.bundletest;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
public class SecondActivity extends AppCompatActivity {
//聲明控件對(duì)象
private TextView textView;
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//獲取控件的對(duì)象
textView = findViewById(R.id.text_view);
/**
*讀取數(shù)據(jù)
*/
Intent intent = getIntent();
//從Intent中取出Bundle
Bundle bundle = intent.getBundleExtra("Message");
//獲取數(shù)據(jù)
assert bundle != null;
String name = bundle.getString("Name");
int num = bundle.getInt("Num");
//顯示數(shù)據(jù)
textView.setText(name + "\n" + num);
}
}
??運(yùn)行程序后瞭吃,結(jié)果如下圖所示:
??點(diǎn)擊Button碌嘀,結(jié)果如下圖所示:
② 當(dāng)傳遞的參數(shù)很多,或者傳遞一個(gè)類(lèi)的對(duì)象時(shí)
??新建一個(gè)JavaBean歪架,將這個(gè)類(lèi)命名為FunPerson股冗,并實(shí)現(xiàn)Serializable接口。
package com.example.bundletest;
import java.io.Serializable;
public class FunPerson implements Serializable {
//創(chuàng)建實(shí)例變量
private String name;
private int num;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
}
??修改MainActivity中的代碼:
public class MainActivity extends AppCompatActivity {
//聲明控件對(duì)象
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲取控件的對(duì)象
mButton = findViewById(R.id.button);
//為Button綁定監(jiān)聽(tīng)器
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 存入數(shù)據(jù)
*/
FunPerson person = new FunPerson();
//設(shè)置數(shù)據(jù)
String name = "Trump is fun";
int num = 12345;
person.setName("name");
person.setNum(num);
//實(shí)例化一個(gè)Bundle
Bundle bundle = new Bundle();
//把FunPerson數(shù)據(jù)放入到Bundle容器中
bundle.putSerializable("Person", person);
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//把Bundle容器中的數(shù)據(jù)放到Intent中
intent.putExtras(bundle);
//啟動(dòng)該Intent和蚪,實(shí)現(xiàn)Activity的跳轉(zhuǎn)
startActivity(intent);
}
});
}
}
??修改SecondActivity中的代碼:
public class SecondActivity extends AppCompatActivity {
//聲明控件對(duì)象
private TextView textView;
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//獲取控件的對(duì)象
textView = findViewById(R.id.text_view);
/**
*讀取數(shù)據(jù)
*/
Intent intent = getIntent();
//從Intent中取出Bundle
Bundle bundle = intent.getExtras();
//獲取FunPerson里的數(shù)據(jù)數(shù)據(jù)
assert bundle != null;
FunPerson person = (FunPerson)bundle.getSerializable("Person");
assert person != null;
String name = person.getName();
int num = person.getNum();
//顯示數(shù)據(jù)
textView.setText(name + "\n" + num);
}
}
??看下運(yùn)行后的結(jié)果:
2. 在Activity to Fragment傳遞數(shù)據(jù)時(shí)使用Bundle
??Activity重新創(chuàng)建時(shí)止状,會(huì)重新構(gòu)建它所管理的Fragment,原先的Fragment的字段值將會(huì)全部丟失攒霹,但是通過(guò)Fragment.setArguments(Bundle bundle)方法設(shè)置的bundle會(huì)保留下來(lái)怯疤。所以盡量使用Fragment.setArguments(Bundle bundle)方式來(lái)傳遞參數(shù)。
??有兩種實(shí)現(xiàn)方案:
① 方法一:使用Fragment的靜態(tài)方法newInstance()來(lái)傳遞數(shù)據(jù)
??新建MainActivity:
public class MainActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//發(fā)送數(shù)據(jù)
BlankFragment blankFragment = BlankFragment.newInstance("Message_1 To Fragment", "Message_2 To Fragment");
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//FrameLayout用于動(dòng)態(tài)更新fragment
fragmentTransaction.replace(R.id.frame_layout, blankFragment);
fragmentTransaction.commit();
}
});
}
}
??MainActivity的布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:text="SendMsg"
android:textAllCaps="false"
app:layout_constraintBottom_toTopOf="@+id/guide_line"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guide_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="255dp" />
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guide_line"
app:layout_constraintVertical_bias="0.0" >
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
??新建一個(gè)Fragment:
public class BlankFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_blank, container, false);
TextView textView = view.findViewById(R.id.text_view);
//Fragment獲取數(shù)據(jù)
Bundle bundle = getArguments();
String message = null;
if (bundle != null) {
message = bundle.getString(ARG_PARAM1);
}
textView.setText(message);
return view;
}
}
??BlankFragment的布局文件比較簡(jiǎn)單催束,就是一個(gè)顯示用的TextView集峦。
??運(yùn)行程序,點(diǎn)擊Button泣崩,結(jié)果如下圖bundle4所示:
② 方法二:
??修改MainActivity的代碼:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//發(fā)送數(shù)據(jù)
ToFragment fragment = new ToFragment();
//新建一個(gè)Bundle實(shí)例
Bundle bundle = new Bundle();
bundle.putString("data", "From Activity To Fragment");
//將數(shù)據(jù)傳遞到Fragment
fragment.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//FrameLayout用于動(dòng)態(tài)更新fragment
fragmentTransaction.replace(R.id.frame_layout, fragment);
fragmentTransaction.commit();
}
});
}
}
??新建一個(gè)碎片ToFragment少梁,簡(jiǎn)單起見(jiàn),就不給新建的碎片弄一個(gè)布局文件矫付,直接使用BlankFragment的布局文件凯沪,節(jié)省時(shí)間:
public class ToFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//簡(jiǎn)單起見(jiàn),此處直接使用BlankFragment的布局文件
View view = inflater.inflate(R.layout.fragment_blank, container, false);
TextView textView = view.findViewById(R.id.text_view);
//得到從Activity傳來(lái)的數(shù)據(jù)
Bundle bundle = this.getArguments();
String message = null;
if (bundle != null) {
message = bundle.getString("data");
}
textView.setText(message);
return view;
}
}
??運(yùn)行程序后結(jié)果如下所示:
3. 在消息機(jī)制的Message中使用setData()傳遞數(shù)據(jù)時(shí)用到Bundle
??這個(gè)栗子的思路也很簡(jiǎn)單买优,點(diǎn)擊屏幕妨马,給Activity發(fā)送一個(gè)Message挺举,傳遞兩個(gè)參數(shù),并通過(guò)Toast顯示出來(lái)烘跺,最后finish()掉這個(gè)Activity湘纵。
??新建一個(gè)Activity:
public class MainActivity extends AppCompatActivity {
final static int FLAG = 1;
@SuppressLint("HandlerLeak")
public Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case FLAG:
//獲取Message傳遞過(guò)來(lái)的數(shù)據(jù)
String data1 = msg.getData().getString("text1");
String data2 = msg.getData().getString("text2");
init(data1, data2);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this, this));
}
public void init(String str1, String str2) {
//將獲取的數(shù)據(jù)Toast出來(lái)
Toast.makeText(MainActivity.this, str1 + '\n' + str2, Toast.LENGTH_SHORT).show();
finish();
}
}
??在建一個(gè)Java類(lèi):
@SuppressLint("ViewConstructor")
public class MyView extends View {
private MainActivity activity;
public MyView(Context context, MainActivity activity) {
super(context);
this.activity = activity;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
Rect rect = new Rect(0, 0, 320, 480);
if (rect.contains(x, y)) {
Message message = new Message();
message.what = MainActivity.FLAG;
//新建Bundle的實(shí)例
Bundle bundle = new Bundle();
//往Bundle中傳入數(shù)據(jù)
bundle.putString("text1", "Trump want to ban TimTok");
bundle.putString("text2", "Make America great again");
//message利用bundle傳遞數(shù)據(jù)
message.setData(bundle);
//用activity中的handler發(fā)送消息
activity.mHandler.sendMessage(message);
}
return super.onTouchEvent(event);
}
}
??運(yùn)行程序,得到如下結(jié)果:
??點(diǎn)擊屏幕指定區(qū)域滤淳,得到如下結(jié)果:
六梧喷、小結(jié)
??到此,Bundle的分析基本就結(jié)束了脖咐,其實(shí)Bundle比較簡(jiǎn)單铺敌,只是一個(gè)數(shù)據(jù)容器,不像Activity等有復(fù)雜的生命周期屁擅。對(duì)于開(kāi)發(fā)者來(lái)說(shuō)偿凭,只需要了解Bundle的功能、使用場(chǎng)景并掌握常用的數(shù)據(jù)存取方法即可派歌。