开发中使用到的Intent知识扩展

一. 前期基础知识储备

        数据序列化在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》

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值