搞懂 Activity Result API (一)

3,257 阅读7分钟

搞懂 Activity Result API (一)

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

1、常规用法

当我们进行 Android 开发时,必然要用到的一个操作姿势:从 Activity A 启动 另一个 Activity B ,并 接收 Activity B 的返回结果。那我们的开发姿势可能是这样滴:

  • 启动另一个 Activity B

    startActivityForResult(new Intent(ActivityA.this, ActivityB.this),requestCode)

    或者使用 ARouter

    ARouter.getInstance().build("/app/activity_b").navigation(mContext, requestCode)

  • 接收 Activity B 的返回结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) {
        return;
    }

    if (requestCode == xxx) { // 1
        ...
    }
}

​ 上面的操作姿势挺好的呀,有啥问题呢?试想一下,如果 Activity A 的业务比较复杂,它的界面和用户有许多不同的操作互动,需要启动多个 Activity , 获取多个 Activity 的返回结果,那上述 注释1 的姿势就变成下面这样色的啦:

if (requestCode == 1) {// 2
    ...
} else if (requestCode == 2) {
    ...
} else if ...

​ 上面注释2,是我们根据 requestCode 来区分不同的 Activity 返回结果,进而处理我们的逻辑,那如果我们处理的逻辑比较复杂,那这个 onActivityResult 就比较复杂啦,会有大量的if ... else...

​ 此时,有一个朋友站起来说,这个也不算是啥痛点呀,我们的页面没有那么复杂,或者说我们可以接受这样的大量的if ... else... 来处理逻辑。

​ 这位朋友别着急,你先坐下,我们在继续分析,看一下上面的方式还有啥不那么友好的地方。

​ 咦,定睛一看,我们发现无论是 启动过程 , 还是 接收过程,都需要 requestCode 参与进去。启动 Activity 的时候,我们为其 Activity 指定一个 requestCode 来标识,在接收该 Activity 的结果时,使用这个 requestCode 来区分是哪一个 Activity 。一般情况下,项目中不是一个人开发,那我们就需要维护一个 requestCode 避免同组的小伙伴起相同啦,害。

​ 此时,又有一个朋友站起来了说,维护个 requestCode 也不算啥痛点呀。

​ 这位朋友你说的对,你别坐下啦,请这位朋友向前10步走,然后左拐,出去!!!

​ 好了,在座的朋友们都是身有其感的,那我们继续搞懂下 Activity Result API


2、使用 Activity Result API

我们用 Activity Result API 的方式来走一遍上面的过程:

  • 启动另一个 Activity B

    • 首先 new 一个约束 ActivityResultContracts.StartActivityForReult , 这个约束使启动活动的输入是 Intent , 输出是 ActivityResult ,后续会解释这个输入和输出的取值依据。

      ActivityResultContracts.StartActivityForResult contract = new ActivityResultContracts.StartActivityForResult();

    • 然后嘞,再 new 一个 接收 Activity B 结果的回调

      ActivityResultCallback<ActivityResult> callBack = new ActivityResultCallback<ActivityResult>() {
              @Override
              public void onActivityResult(ActivityResult result) {
                  ... //3
              }
          };
      
    • 再然后嘞,搞一个启动 Activity B 的启动器:将前面的 约束结果回调 传入到 registerForActivityResult 方法中。

      private ActivityResultLauncher<Intent> mActivityBLauncher = registerForActivityResult(contract, callBack);

    • 最后,启动啦:例如在点击事件中,调用下面方法,即可启动一个 Activity B

      mActivityBLauncher.launch(new Intent(ActivityA.this, ActivityB.this));

  • 接收 Activity B 的返回结果:

    在上面启动的时候,我们new 了一个 接收 Activity B 结果的回调,那就是在里面(上面注释3的地方)处理我们的逻辑啦:

    if (result.getResultCode() == Activity.RESULT_OK) {
        Intent intent = result.getData();
        ...
    }
    

​ 使用 Activity Result API 的方式,我们再看下这两个问题:

  • 业务启动多个 Activity ,处理逻辑分开啦,互不干扰,你会发现都不用写在 onActivityResult 方法中啦;
  • 启动哪一个 Activity ,也不需要维护 requestCode

3、原理探究

​ 既然是搞懂 Activity Result API,那我们继续往下挖一下。

(1)约束

​ 首先先看一下 约束ActivityResultContracts 该类是 Android 提供的一些标准约束的集合,StartActivityForResult 只是其中的一个 static final class 类:

public static final class StartActivityForResult extends ActivityResultContract<Intent, ActivityResult> {
    @Override
    public Intent createIntent(Context context, Intent input) {
        return input;
    }

    @Override
    public ActivityResult parseResult(int resultCode, Intent intent) {
        return new ActivityResult(resultCode, intent);
    }
}

可以看到,这个 StartActivityForResult 类 继承 ActivityResultContract :该抽象类,约束了 启动 Activity输入输出

  • I: 表示输入类型,在 约束使用StartActivityForResult 类时,启动器中的输入类型,为上述 private ActivityResultLauncher<Intent> mActivityBLauncher 中的 Intent
  • O:表示输出类型,在 约束使用StartActivityForResult 类时,回调中的输出类型,为上述回调中的 ActivityResult
public abstract class ActivityResultContract<I, O> {
    public abstract Intent createIntent(Context context, I input);
    public abstatc O parseResult(int resultCode, Intent intent);
}

通过上面的分析,我们可以总结到:这个 约束 决定了 输入类型 和 输出类型。

  • 启动 Activity 的时候,可以指定输入类型,并将这个输入在启动器的 launch 方法中传入进去,并通过 createIntent 方法来构造出Intent
  • 获取 Activity 的结果时,通过 parseResult 方法来获取指定地输出类型 O

(2)回调

​ 接下来看一下回调ActivityResultCallback , 当 Activity result 是可用时,回调此方法

public interface ActivityResultCallback<O> {
    void onActivityResult(O result);
}

当使用 StartActivityForResult 约束时,输出类型 OActivityResult , 该类型表示 活动结果的容器:

public final class ActivityResult {
    private final int mResultCode;
    private final Intent mData;

    public ActivityResult(int resultCode, Intent data) {
        mResultCode = resultCode;
        mData = data;
    }

    public Intent getData() {
        return mData;
    }

     public int getResultCode() {
        return mResultCode;
    }
}

所以,当回调 onActivityResult 方法时:

@Override
public void onActivityResult(ActivityResult result) {
   if (result.getResultCode() == Activity.RESULT_OK) {
    Intent intent = result.getData();
    ...
   }
}

(3)注册

接下来,继续看下 registerForActivityResult 方法:

// ComponentActivity.java
public fianl <I, O> ActivityResultLauncher<I> registerForActivityResult(
    ActivityResultContract<I, O> contract,
    ActivityResultCallback<O> callback) {
    return registerForActivityResult(contract, mActivityResultRegistry, callback); // 4
}

registerForActivityResult 方法,又转调用了另一个重载方法,注意到有一个新参数:mActivityResultRegistry

// ComponentActivity.java
private final ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {
    @Override
    public <I, O> void onLaunch(final int requestCode, ActivityResultContract<I, O> contract, I input,
                                ActivityOptionsCompat options) {
        ComponentActivity activity = ComponentActivity.class;
        // Start activity path
        Intent intent = contract.createIntent(activity, input);
        // startActivityForResult path
        ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
    }
}

我们可以看到,原来在 mActivityResultRegistry 中,封装了 startActivityForResult,那我们看下 ActivityResultRegistry 是何方神圣:

// ActivityResultRegistry.java
public abstract class ActivityResultRegistry {
    public abstract <I, O> void onLaunch(int requestCode, ActivityResultContract<I, O> contract,
                                        I input, ActivityOptionsCompat options);

    public fianl <I, O> ActivityResultLauncher<I> register(final String key, 
                                                          final LifecycleOwner lifecycleOwner,
                                                          final ActivityResultContract<I, O> contract,
                                                          final ActivityResultCallback<O> callback) {
        ... // lifecycle code
        final int requestCode = registerKey(key); // 产生requestCode
        return new ActivityResultLauncher<I>() {
             @Override
            public void launch(I input, @Nullable ActivityOptionsCompat options) {
                mLaunchedKeys.add(key);
                onLaunch(requestCode, contract, input, options);
            }

            @Override
            public void unregister() {
                ActivityResultRegistry.this.unregister(key);
            }
        }
    }

    public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
        doDispatch(key, resultCode, data, mKeyToCallback.get(key));
    }

    private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
                                @Nullable CallbackAndContract<O> callbackAndContract) {
        callback.onActivityResult(contract.parseResult(resultCode, data));
    }

    private int registerKey(String key) {
        Integer existing = mKeyToRc.get(key);
        if (existing != null) {
            return existing;
        }
        int rc = generateRandomNumber();
        bindRcKey(rc, key);
        return rc;
    }

    final void unregister(@NonNull String key) {
        ...
    }
}

从这个 ActivityResultRegistry 我们可以看到 提供一个 抽象方法 onLaunchregister 方法 、doDispatch方法

  • onLaunch 方法是一个抽象方法,在 ComponentActivty 中实现该方法,封装 startActivityForResult,返回一个 mActivityResultRegistry 参数,而该参数会传递到 ComponentActivity 类中的 registerForActivityResult(contract, mActivityResultRegistry, callback); 方法,即上述注释4的地方:

    // ComponentActivity.java
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(@NonNull ActivityResultContract<I, O> contract, ActivityResultRegistry registry, @NonNull ActivityResultCallback<O> callback) {
            return registry.register("activity_rq#" + this.mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
        }
    
  • register 方法干了啥事呢?首先产生 requestCode 、然后 返回一个启动器 ActivityResultLauncher<I>,那我们看下这个 启动器:

    // ActivityResultLauncher.java
    public abstract class ActivityResultLauncher<I> {
        public void launch(I input) {
            launch(input, null);
        }
    
        public abstract void launch(I input, ActivityOptionsCompat options);
    
        public abstract void unregister();
    }
    

    这个启动器是一个抽象类,它是在 ActivityResultRegistryregister 方法实现了这个抽象类,而在实现这个抽象类的时候,调用了 ActivityResultRegistry 自身的 onLaunch 抽象方法,而这个 onLaunch 抽象方法 在 ComponentActivity 中通过引入 mActivityResultRegistry 对象被实现了。该抽象类的 launch 方法,正是给外部调用的启动的地方,如: mActivityBLauncher.launch(new Intent(ActivityA.this, ActivityB.this));

  • doDispatch方法:在 ComponentActivity 中的 onActivityResult 拦截了 返回结果,并通过 mActivityResultRegistry 进行分发

    // ComponentActivity.java 
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
                super.onActivityResult(requestCode, resultCode, data);
            }
        }
    

    如上述的 ActivityResultRegistry.java 中的 doDispatch 方法,对具体分发的内容 转出到 暴露外部的回调接口:ActivityResultCallback

    // ActivityResultRegistry.java 
    private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
                                    @Nullable CallbackAndContract<O> callbackAndContract) {
            // 这里的contract.parseResult(resultCode, data) 就是我们外界定义的输出参数,也就是那个 约束中的输出类型 O
            callback.onActivityResult(contract.parseResult(resultCode, data));
        }
    

    此时,这里的 callback 就是我们在外部定义的 回调 ActivityResultCallback


(4)启动

​ 通过 registerForActivityResult 方法返回一个启动器,例如:mActivityBLauncher ,然后在我们的点击事件中启动这个 Activity

mActivityBLauncher.launch(new Intent(ActivityA.this, ActivityB.this));

(5)资源回收

​ 当页面要执行 onDestory 方法时,启动器还提供一个 unregister 方法用于释放资源,当然啦,你不手动调用也行,因为 ComponentActivity 已经引用了生命周期感知的 Lifecycle

mActivityBLauncher.unregister();

4、遗留问题探究

(1)如何使用 ARouter 来使用 Activity Result API

(2)没有使用 Jetpack 组件,如何使用 Activity Result API