Android中的IPC方式——Bundle

说一个很常见的现象,我们在使用Android手机的过程中,肯定都会遇到从一个App中跳转到另一个App的场景。例如,在支付宝中跳转到淘宝中,并显示淘宝中某个商品的详情页。这个本质上就是从一个进程中的某个Activity跳转到了另一个进程中的某个Activity,借助Bundle,我们就可以在这个过程中跨进程的传递一些数据信息(例如,现在很常见的,我们是否在特定的网页浏览的指定秒数,这个浏览结果,我们就可以通过Bundle在不同进程间进行传递)。

在Android四大组件(Activity, Service, Broadcast Receive, Content Provider)中,除了Content Provider之外,其他三个都可以在Intent中添加Bundle数据。Bundle能够顺利的从一个进程被传递到另一个进程,主要得益于Bundle能够被序列化,也就是Bundle类实现了Parcelable接口。其底层原理也是将Bundle及其存储的数据通过序列化和反序列化来实现。所以,能够存储在Bundle中的数据(其类型)也必须能够被序列化。例如,Java基本数据类型,实现了Parcelable或Serializable接口的对象或一些Android支持的特殊对象。其实通过查看Bundle类的(put或get)方法,我们就能够知道哪些类能够被支持在Bundle中传输。

写了个小Demo实现借用Bundle跨进程传递数据,本Demo实现了从BundleTest应用中的OriginActivity跳转到AndroidView应用中的TargetActivity,并通过Bundle传递一段字符串。

OriginActivity的代码如下:

package com.itachi.android.bundletest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class OriginActivity extends AppCompatActivity {
    private static final String EXTRA_KEY = "EXTRA_KEY";
    private static final String EXTRA_MESSAGE = "Message from Origin Activity";
    private Button mOriginButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_origin);
        mOriginButton = (Button)findViewById(R.id.origin_button);
        mOriginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startTargetActivity();
            }
        });
    }

    private void startTargetActivity() {
        Intent intent = new Intent();
        // 如果需要跳转的目标Activity为非主Activity 即未在Manifest文件中声明为启动Activity
        // 则目标Activity需要声明android:exported="true"
        intent.setComponent(new ComponentName("com.itachi.android.androidview",
                "com.itachi.android.androidview.TargetActivity"));
        Bundle bundle = new Bundle();
        bundle.putString(EXTRA_KEY, EXTRA_MESSAGE);
        intent.putExtras(bundle);
        startActivity(intent);
    }
}

TargetActivity的代码如下: 

package com.itachi.android.androidview;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class TargetActivity extends AppCompatActivity {
    private static final String EXTRA_KEY = "EXTRA_KEY";

    private TextView mTargetTextView;
    private Button mTargetButton;
    private String message = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_target);
        mTargetTextView = (TextView) findViewById(R.id.target_textview);
        mTargetButton = (Button) findViewById(R.id.target_button);
        message = getIntent().getExtras().getString(EXTRA_KEY);

        mTargetButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mTargetTextView.setText(message);
            }
        });
    }
}

在上面的Demo中,我们通过OriginActivity启动了TargetActivity,并将字符串数据传递给了TargetAcivity,实现了从进程com.itachi.android.bundletest到进程com.itachi.android.androidview的跨进程通信。

再举个我们可能都用到过的功能,现在很多App都带有广告推送功能,一些App可能会推荐你看一个广告并且在达到特定的观看时长后,给予用户一定的“奖励”。那么用户在某个(例如ActivityA)进入广告页(ActivityB)后,观看的时长怎么返回给原界面(ActivityA)呢 ?我们可以通过startActivity(intent)方法将Bundle从ActivityA传递到ActivityB,反过来,怎么在用户从ActivityA(用户点击back键或者利用手势导航)返回ActivityB时,传递数据呢?

这里就要介绍Activity类中的几个方法,如下:

1.startActivityForResult(Intent intent, int requestCode)

2.onActivityResult(int requestCode, int resultCode, Intent data)

3.setResult(int resultCode, Intent data)

我们可以通过startActivityForResult方法启动新的Activity,其中的参数requestCode用于标识请求的是请求的是哪个Activity。在新启动的Activity中,我们可以通过setResult方法,将数据打包到intent中。在返回上一个Activity后,会调用原Activity的onActivityResult方法,进而实现了将数据返回到之前的Activity。

针对前面的例子,我们可以这样实现:

1.ActivityA通过startActivityForResult(Intent intent, int requestCode)启动ActivityB(需要利用requestCode标识ActivityB)

2.ActivityB启动后,通过setResult(int resultCode, Intent data)将需要返回的数据(比如是否看完了广告)打包到intent中

3.用户返回上一个Activity,系统回调ActivityA的onActivityResult(int requestCode, int resultCode, Intent data)方法,将数据解析到ActivityA中。

我们再在前面的Demo中,加入这个功能,修改后的代码如下:

OriginActivity

package com.itachi.android.bundletest;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class OriginActivity extends AppCompatActivity {
    private static final String TAG = "OriginActivity";

    private static final String EXTRA_KEY = "EXTRA_KEY";
    private static final String EXTRA_MESSAGE = "Message from Origin Activity";
    private Button mOriginButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_origin);
        mOriginButton = (Button)findViewById(R.id.origin_button);
        mOriginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startTargetActivity();
            }
        });
    }

    private void startTargetActivity() {
        Intent intent = new Intent();
        // 如果需要跳转的目标Activity为非主Activity 即未在Manifest文件中声明为启动Activity
        // 则目标Activity需要声明android:exported="true"
        intent.setComponent(new ComponentName("com.itachi.android.androidview",
                "com.itachi.android.androidview.TargetActivity"));
        Bundle bundle = new Bundle();
        bundle.putString(EXTRA_KEY, EXTRA_MESSAGE);
        intent.putExtras(bundle);
        startActivityForResult(intent, 1);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        Log.v(TAG, "onActivityResult..." + ", request:" + requestCode + ", resultCode:" + resultCode);
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    String message = data.getExtras().getString(EXTRA_KEY);
                    Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
                }
        }
    }

    @Override
    protected void onResume() {
        Log.v(TAG, "onResume...");
        super.onResume();
    }
}

TargetActivity

package com.itachi.android.androidview;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class TargetActivity extends AppCompatActivity {
    private static final String TAG = "TargetActivity";
    private static final String EXTRA_KEY = "EXTRA_KEY";
    private static final String MESSAGE_OK = "Message from Target Activity";

    private TextView mTargetTextView;
    private Button mTargetButton;
    private String message = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_target);
        mTargetTextView = (TextView) findViewById(R.id.target_textview);
        mTargetButton = (Button) findViewById(R.id.target_button);
        message = getIntent().getExtras().getString(EXTRA_KEY);

        mTargetButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mTargetTextView.setText(message);
            }
        });
    }

    @Override
    public void onBackPressed() {
        // 此方法会在点击返回键(Back键)时被回调
        Log.v(TAG, "onBackPressed...");
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString(EXTRA_KEY, MESSAGE_OK);
        intent.putExtras(bundle);
        setResult(RESULT_OK, intent);
        super.onBackPressed();
    }

    @Override
    protected void onPause() {
        Log.v(TAG, "onPause...");
        super.onPause();
    }
}

演示效果如下:

 

 需要注意,setResult方法需要在Activity的finish流程之前调用,否则前一个Activity的onActivityResult方法无法获取到返回的intent。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值