一. 前期基础知识储备
数据序列化在Android开发中占据举足轻重的地位,无论进程间通信,本地数据化存储,网络数据传输等,都离不开序列化的支持。针对不同场景选择正确的序列化方案,对应用的性能有着极大的影响,而Intent对象就是序列化数据的载体之一。
二.上代码,具体实现
下面是一些和Intent有关的知识汇总:
1)makeMainActivity() | makeRestartActivityTask() - 启动某个应用的主界面;
Intent intent_1 = Intent.makeMainActivity(new ComponentName(getApplication(),MainActivity.class));
Intent intent_2 = Intent.makeRestartActivityTask(new ComponentName(getApplication(),MainActivity.class));
想象一个具体的应用场景:在通知推送时,很多时候都希望用户点击推送后先进入目标页面,点击返回键可以退回到APP的主页面,那么点击推送消息后等于需要启动多个Activity(一组Activity)。而pendingIntent正好提供了一个静态方法getActivies(),里面可以设置一个Intent数组,用来指定一系列的Activity。
例如,我们希望点击前台通知栏“推送通知”后跳转至DetailActivity,但需求是先打开MainActivity,在进入DetailActivity。这时就可以用makeRestartActivityTask()和getActivitys()来实现了。
① 建立Intent数组
private Intent[] makeIntentStack(Context context){
Intent[] intents = new Intent[2];
intents[0] = Intent.makeRestartActivityTask(new ComponentName(context,MainActivity.class));
intents[1] = new Intent(context, DetailActivity.class);
return intents;
}
② 根据情况使用Intent数组
// 如果在前台,那么仅启动目标Activity
if (AppState.active()){
context.startActivity(intents[1]);
} else {
context.startActivities(intents);
}
需要注意的是,这个方法会清空栈,当APP在前台的时候需要小心使用。
2)Intent的Chooser createChooser() - 确定用户的意图;
Android的Intent提供了一个很棒的思想:用户只需说出自己的意图即可,其余全部交给系统,系统层面会找到可以实现想法的App或功能去处理。以跳转浏览器为例,用户手机上可能会安装多个浏览器,使用打开浏览器的Intent之后就会弹出一个浏览器选择界面供用户选择。
如果每次都出现选择菜单会干扰用户,我们当然希望用户只选择一次,以后直接跳转就好,针对这种需求,createChooser()应运而生。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_APP_BROWSER);
intent.setData(uri);
// createChooser() 根据指定好的intent再新建另一个Intent
Intent chooser = Intent.createChooser(intent,"请选择一款浏览器");
startActivity(chooser);
这种机制让“定制文案”变成了可能,提升了用户体验。其实,系统采用的是Intent包Intent的做法,它将真正的Intent变成了它的参数。外层的Intent指向了选择界面,选择界面中进行了最终的解析工作,将原本的Intent解析出来。
更进一步,能否根据需要屏蔽某些不想要的处理者呢?比如竞争对手的APP?参数packageManager.queryIntentActivities()可以让我们知道有哪些Activity能处理Intent,那么索性通过包名过滤一下,就能得到干净的选择页面了。
public abstract List<ResolveInfo> queryIntentActivities(@RecentlyNonNull Intent var1, int var2);
以分享操作爲例:
/**
* 参数为目标应用的包名,推荐扩展为多个包名,比如只分享到新浪有关的第三方应用,sina
* @param type
*/
private void initShareIntent(String type){
boolean found = false;
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/jpeg");
// gets the list of intents that can be loaded
List<ResolveInfo> resolveInfoList = getPackageManager().queryIntentActivities(share , 0);
if (! resolveInfoList.isEmpty()){
for (ResolveInfo info: resolveInfoList){
if (info.activityInfo.packageName.toLowerCase().contains(type)||
info.activityInfo.name.toLowerCase().contains(type)){
share.putExtra(Intent.EXTRA_SUBJECT, "subject");
share.putExtra(Intent.EXTRA_TEXT, "shared text");
share.setPackage(info.activityInfo.packageName);
found = true;
break;
}
}
if (!found){
return;
}
startActivity(Intent.createChooser(share, "Select One Activity"));
}
}
3)重复启动Activity问题;
如果Activity已经启动(可以不在前台),再次调用start Activity()方法,在onResume()方法中获得的Intent还是第一次,而非最新的。建议通过onNewIntent()方法得到新的Intent,再直接调用setIntent()方法重置Intent。
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 重置当前的Intent
setIntent(intent);
}
android lifeCycle components并不能提供对onNewIntent()方法生命周期的监听,所以无法解耦。这个方法也成为Andoid设计中的一个缺陷。如果非要用监听的方式处理逻辑,则只能在onNewIntent()方法中setIntent(newIntent),然后再onResumed()方法中进行统一处理了。
如果遇到重复启动Activity后,Intent中的值被覆盖的问题,那么可以在getIntent()后调用intent.removeExtra()方法清空无用的数据,避免数据冲突。
4)传递大对象;
Intent传递数据时是有大小限制的,在传递bitmap的时候需要格外注意,一般超过1MB就会出现异常。官方文档描述为:
The Binder transaction buffer has a limited fixed size, currently 1Mb,
which is shared by all transactions in progress for the process.
建议在传递数据的时候尽量不要传递过大的JSON。传递过大的JSON不仅会影响页面的打开速度,而且还可能在某些手机上出现ANR等异常。对于第三方分享来说,分享到第三方的图片不同,大小也不相同。为了保证程序的健壮性,应该将通过Intent分享的bitmap压缩到一个合理的大小。
建议不要直接将Bitmap放入Intent,最好将其转换为byte[]后再传。
如果想要在当前进程中传递一个很大的对象,又不想对这个对象进行序列化,这时候就需要用到一些技巧了,比如建立一个静态的存储器——ModelStorage。
public class ModelStorage {
private static ModelStorage mInstance = null;
private Map<Integer, Model> map = new android.support.v4.util.ArrayMap<>();
public static ModelStorage getInstance(){
// ...
}
public int putModel(Model model){
if (map.containsValue(model)){
for (Map.Entry<Integer, Model> entry : map.entrySet()){
if (entry.getValue() == model){
return entry.getKey();
}
}
return 0;
} else {
int index = map.size();
map.put(index, model);
return index;
}
}
public Model getModel(int key){
if (map.size() == 0){
return null;
} else {
return map.remove(key);
}
}
}
这个类的设计目的就是将Model对象进行存取,至于存取的数据结构可以自行更换,这里仅仅讲述设计方案。因为传递的对象通常都是set后立刻get,所以没必要做磁盘I/O,直接操作内存即可。
配合modelStorage使用的还有一个叫做Model的基类。这个类实现了Parcelable接口,它将自身存入modelStorage,然后将自己的index进行序列化和反序列化。
public abstract class Model implements Parcelable {
// ...
public void writeToParcel(Parcel dest, int flags){
int index = ModelStorage.getInstance().putModel(this);
dest.writeInt(index); // 实际写入的是index
}
public final static Creator<Model> CREATOR = new Creator<Model>() {
@Override
public Model createFromParcel(Parcel parcel) {
int index = parcel.readInt(); // 读出的也是index
return ModelStorage.getInstance().getModel(index);
}
@Override
public Model[] newArray(int i) {
return new Model[i];
}
};
}
最后,只需要将想要序列化的对象继承子Model即可。
public class Uesr extends Model {
public String name;
public int age;
public Uesr(String name, int age){
this.name = name;
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
}
存取的代码实现如下:
intent.putExtra("_key", new User("Jack", 22)); // 存
Uesr uesr = getIntent().getParcelableExtra("_key"); // 取
三.其他的一些Intent Action知识
Android基本的设计理念是鼓励减少组件间的耦合,因此Android提供了Intent (意图) ,Intent提供了一种通用的消息系统,它允许在你的应用程序与其它的应用程序间传递Intent来执行动作和产生事件。Intent作为联系各Activity之间的纽带,其作用并不仅仅只限于简单的数据传递。通过其自带的属性,其实可以方便的完成很多较为复杂的操作。例如直接调用拨号功能、处理接收短信,诸如此类,都可以通过设置Intent属性来完成。
1)从google搜索内容
Intent intent = new Intent();
intent.setAction(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY,"searchString")
startActivity(intent);
2)浏览网页
Uri uri = Uri.parse("http://www.google.com");
Intent it = new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);
3)拨打电话
Uri uri = Uri.parse("tel:xxxxxx");
Intent it = new Intent(Intent.ACTION_DIAL, uri);
startActivity(it);
4)调用发短信的程序
Intent it = new Intent(Intent.ACTION_VIEW);
it.putExtra("sms_body", "The SMS text");
it.setType("vnd.android-dir/mms-sms");
startActivity(it);
5)播放多媒体
Intent it = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/song.mp3");
it.setDataAndType(uri, "audio/mp3");
startActivity(it);
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);
6)打开联系人列表
方法一
Intent i = new Intent();
i.setAction(Intent.ACTION_GET_CONTENT);
i.setType("vnd.android.cursor.item/phone");
startActivityForResult(i, REQUEST_TEXT);
方法二
Uri uri = Uri.parse("content://contacts/people");
Intent it = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(it, REQUEST_TEXT);
7)打开录音机
Intent mi = new Intent(Media.RECORD_SOUND_ACTION);
startActivity(mi);
8)打开照相机
方法一:
Intent i = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
this.sendBroadcast(i);
方法二
long dateTaken = System.currentTimeMillis();
String name = createName(dateTaken) + ".jpg";
fileName = folder + name;
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, fileName);
values.put("_data", fileName);
values.put(Images.Media.PICASA_ID, fileName);
values.put(Images.Media.DISPLAY_NAME, fileName);
values.put(Images.Media.DESCRIPTION, fileName);
values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, fileName);
Uri photoUri = getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent inttPhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
inttPhoto.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(inttPhoto, 10);
9)从gallery选取图片
Intent i = new Intent();
i.setType("image/*");
i.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(i, 11);
再补充一些,利用Intent跳转系统设置页:
1. ACTION_ACCESSIBILITY_SETTINGS : // 跳转系统的辅助功能界面
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
2. ACTION_ADD_ACCOUNT : // 显示添加帐户创建一个新的帐户屏幕。【测试跳转到微信登录界面】
Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
startActivity(intent);
3. ACTION_AIRPLANE_MODE_SETTINGS: // 飞行模式,无线网和网络设置界面
Intent intent = new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS);
startActivity(intent);
或者:
ACTION_WIRELESS_SETTINGS :
Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
startActivity(intent);
4. ACTION_APN_SETTINGS: // 跳转 APN设置界面
Intent intent = new Intent(Settings.ACTION_APN_SETTINGS);
startActivity(intent);
5. 【需要参数】 ACTION_APPLICATION_DETAILS_SETTINGS: // 根据包名跳转到系统自带的应用程序信息界面
Uri packageURI = Uri.parse("package:" + "com.tencent.WBlog");
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,packageURI);
startActivity(intent);
6. ACTION_APPLICATION_DEVELOPMENT_SETTINGS : // 跳转开发人员选项界面
Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
startActivity(intent);
7. ACTION_APPLICATION_SETTINGS : // 跳转应用程序列表界面
Intent intent = new Intent(Settings.ACTION_APPLICATION_SETTINGS);
startActivity(intent);
或者:
ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS // 跳转到应用程序界面【所有的】
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS);
startActivity(intent);
或者:
ACTION_MANAGE_APPLICATIONS_SETTINGS :// 跳转 应用程序列表界面【已安装的】
Intent intent = new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
startActivity(intent);
8. ACTION_BLUETOOTH_SETTINGS : // 跳转系统的蓝牙设置界面
Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(intent);
9. ACTION_DATA_ROAMING_SETTINGS : // 跳转到移动网络设置界面
Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
startActivity(intent);
10. ACTION_DATE_SETTINGS : // 跳转日期时间设置界面
Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
startActivity(intent);
11. ACTION_DEVICE_INFO_SETTINGS : // 跳转手机状态界面
Intent intent = new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS);
startActivity(intent);
12. ACTION_DISPLAY_SETTINGS : // 跳转手机显示界面
Intent intent = new Intent(Settings.ACTION_DISPLAY_SETTINGS);
startActivity(intent);
13. ACTION_DREAM_SETTINGS 【API 18及以上 没测试】
Intent intent = new Intent(Settings.ACTION_DREAM_SETTINGS);
startActivity(intent);
14. ACTION_INPUT_METHOD_SETTINGS : // 跳转语言和输入设备
Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
startActivity(intent);
15. ACTION_INPUT_METHOD_SUBTYPE_SETTINGS 【API 11及以上】 // 跳转 语言选择界面 【多国语言选择】
Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
startActivity(intent);
16. ACTION_INTERNAL_STORAGE_SETTINGS // 跳转存储设置界面【内部存储】
Intent intent = new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS);
startActivity(intent);
或者:
ACTION_MEMORY_CARD_SETTINGS : // 跳转 存储设置 【记忆卡存储】
Intent intent = new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS);
startActivity(intent);
17. ACTION_LOCALE_SETTINGS : // 跳转语言选择界面【仅有English 和 中文两种选择】
Intent intent = new Intent(Settings.ACTION_LOCALE_SETTINGS);
startActivity(intent);
18. ACTION_LOCATION_SOURCE_SETTINGS : // 跳转位置服务界面【管理已安装的应用程序。】
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
19. ACTION_NETWORK_OPERATOR_SETTINGS : // 跳转到 显示设置选择网络运营商。
Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
startActivity(intent);
20. ACTION_NFCSHARING_SETTINGS : // 显示NFC共享设置。 【API 14及以上】
Intent intent = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
startActivity(intent);
21. ACTION_NFC_SETTINGS : // 显示NFC设置。这显示了用户界面,允许NFC打开或关闭。 【API 16及以上】
Intent intent = new Intent(Settings.ACTION_NFC_SETTINGS);
startActivity(intent);
22. ACTION_PRIVACY_SETTINGS : // 跳转到备份和重置界面
Intent intent = new Intent(Settings.ACTION_PRIVACY_SETTINGS);
startActivity(intent);
23. ACTION_QUICK_LAUNCH_SETTINGS : // 跳转快速启动设置界面
Intent intent = new Intent(Settings.ACTION_QUICK_LAUNCH_SETTINGS);
startActivity(intent);
24. ACTION_SEARCH_SETTINGS : // 跳转到 搜索设置界面
Intent intent = new Intent(Settings.ACTION_SEARCH_SETTINGS);
startActivity(intent);
25. ACTION_SECURITY_SETTINGS : // 跳转到安全设置界面
Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
startActivity(intent);
26. ACTION_SETTINGS : // 跳转到设置界面
Intent intent = new Intent(Settings.ACTION_SETTINGS);
startActivity(intent);
27. ACTION_SOUND_SETTINGS // 跳转到声音设置界面
Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
startActivity(intent);
28. ACTION_SYNC_SETTINGS : // 跳转账户同步界面
Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
startActivity(intent);
29. ACTION_USER_DICTIONARY_SETTINGS : // 跳转用户字典界面
Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_SETTINGS);
startActivity(intent);
30. ACTION_WIFI_IP_SETTINGS : // 跳转到IP设定界面
Intent intent = new Intent(Settings.ACTION_WIFI_IP_SETTINGS);
startActivity(intent);
31. ACTION_WIFI_SETTINGS : // 跳转Wifi列表设置
Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
startActivity(intent);
四. Android 两种序列化方式 Serializable 和 Parcelable
再补充一些关于序列化的知识:
“在日常的应用开发中,我们可能需要让某些对象离开内存空间,存储到物理磁盘,以便长期保存,同时也能减少对内存的压力,而在需要时再将其从磁盘读取到内存,比如将某个特定的对象保存到文件中,隔一段时间后再把它读取到内存中使用,那么该对象就需要实现序列化操作,在java中可以使用Serializable接口实现对象的序列化,而在android中既可以使用Serializable接口实现对象序列化也可以使用Parcelable接口实现对象序列化,但是在内存操作时更倾向于实现Parcelable接口,这样会使用传输效率更高效。接下来我们将分别详细地介绍这样两种序列化操作”。
参考文章:《Android序列化总结》
《Android 进阶6:两种序列化方式 Serializable 和 Parcelable》