一行代码完成startActivityForResult

一行代码完成startActivityForResult

以前使用透明fragment来实现,在fragment里覆写onActivityResult来实现有result的回调,在onResume里感知无result的页面的返回.

后来Androidx有ActivityResultLauncher,可以在一处写完启动intent和接收result回调,本身的兼容性不错,能接受有result的回调,也能感知无result页面的返回.

但是有限制,回调必须注册在activity的onCreate()方法里,这个绝对忍不了(谷歌一直产出这种傻X api),于是使用透明activity来处理这个问题.

最终实现了此api:

less 复制代码
StartActivityResultHelper.startForResult(activity, intent, new ActivityResultCallback() {
            @Override
            public void onActivityResult(int resultCode, @Nullable Intent data) {
                LogUtils.w(resultCode,data);
            }
​
            @Override
            public void onActivityNotFound(Throwable e) {
​
            }
        });

内部实现如下:

1 启动透明activity

传递intent和intent的hashcode.

intent本身是parcelable,可以通过intent传递给透明activity

hashcode用于存取对应的回调.

scss 复制代码
// 静态回调引用
    private static HashMap<Integer, ActivityResultCallback> callbackMap = new HashMap<>();
​
    /**
     * Consider adding a <queries> declaration to your manifest when calling this method; see https://g.co/dev/packagevisibility for details
     * @param context
     * @param targetIntent
     * @param callback
     */
    public static void startForResult(Context context, Intent targetIntent, ActivityResultCallback callback) {
        init(context.getApplicationInfo());
        ComponentName componentName = targetIntent.resolveActivity(context.getPackageManager());
        if (componentName == null) {
            //找不到对应的activity
            callback.onActivityNotFound(new Throwable("Activity not found"));
            return;
        }
        if (componentName.getPackageName().equals(context.getPackageName())) {
            // 启动的activity是本app的
            if(debugable){
                Log.d("ActivityResultx","启动的activity是本app的,"+componentName.getClassName());
            }
        }else {
            if(debugable){
                Log.d("ActivityResultx","启动的activity是第三方的,"+componentName.getPackageName()+","+componentName.getClassName());
            }
        }
        callbackMap.put(targetIntent.hashCode(),callback);
        // 启动透明Activity
        if(debugable){
            Log.v("ActivityResultx","intent hash0,"+targetIntent.hashCode());
        }
        if(Looper.myLooper() == Looper.getMainLooper()){
            startActi(context, targetIntent);
        }else {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    startActi(context, targetIntent);
                }
            });
        }
    }
​
    private static void startActi(Context context, Intent targetIntent) {
        try{
            Intent intent = new Intent(context, TransparentActivity.class);
            intent.putExtra("target_intent", targetIntent);
            intent.putExtra("target_intent_hash", targetIntent.hashCode());
            context.startActivity(intent);
        }catch (Throwable throwable){
            throwable.printStackTrace();
            try{
                Intent intent = new Intent(context, TransparentActivity.class);
                intent.putExtra("target_intent", targetIntent);
                intent.putExtra("target_intent_hash", targetIntent.hashCode());
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }catch (Throwable throwable2){
                throwable2.printStackTrace();
                ActivityResultCallback remove = callbackMap.remove(targetIntent.hashCode());
                if(remove != null){
                    remove.onActivityNotFound(throwable2);
                }else {
                    if(debugable){
                        Log.w("ActivityResultx","ActivityResultListener callback is null,"+ targetIntent.hashCode());
                    }
                }
            }
        }
    }

2 透明activity有些兼容性问题需要注意:

  1. 配置变更的影响

    • 当设备发生旋转等配置变更时,透明 Activity 可能被意外销毁重建,导致回调丢失
    • 解决方案:在 Manifest 中为该 Activity 添加android:configChanges="orientation|screenSize"
  2. 启动模式的兼容性

    • 使用singleInstancesingleTask等特殊启动模式时,在部分设备上可能导致回调无法正确触发
    • 建议使用默认的standard启动模式,并在回调处理完成后立即调用finish()

因此,清单文件中已配置好:

ini 复制代码
 <activity
            android:name=".androidx.TransparentActivity"
            android:theme="@style/TransparentTheme"
            android:configChanges="orientation|screenSize"
            android:exported="false" />

theme:

ini 复制代码
<style name="TransparentTheme" parent="Theme.AppCompat.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:windowAnimationStyle">@null</item>
    </style>

3 activity内部逻辑:

全部逻辑都在onCreate里处理,符合ActivityResultLauncher的要求

通过静态方法将结果传递出去

ActivityResult里的intent手动fork一份再传递出去,以便与TransparentActivity解除内部的引用关系,不影响其finish()后onDestory()的回调.
结果处理StartActivityResultHelper.onActivityResult()时,延时1s,让TransparentActivity先onDestory(),从而后续在回调里使用ActivityUtils.getTopActivity()拿到的是触发startActivityResult的实际的业务activity,保证是可用的activity

scss 复制代码
public class TransparentActivity extends AppCompatActivity {
    private ActivityResultLauncher<Intent> launcher;
    int intentHashCode;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 注册ActivityResultLauncher
        launcher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        //LogUtils.i(result);
                        // 回调结果给工具类
                        // 创建一个复制的Intent
                        Intent forkedIntent = StartActivityResultHelper.forkIntent(result.getData());
                        StartActivityResultHelper.onActivityResult(result.getResultCode(), forkedIntent,intentHashCode);
                    // 先关闭透明Activity. 没用,匿名内部类持有该activity的引用,会导致一直无法回调onDestory,一定是上面执行完后才销毁,
                        // intent本身也持有着该activity的引用,导致无法销毁
                        finish();
                    }
                }
        );
​
        // 获取目标Intent并启动
        Intent targetIntent = getIntent().getParcelableExtra("target_intent");
        intentHashCode = getIntent().getIntExtra("target_intent_hash",0);
        if (targetIntent != null) {
            try{
                launcher.launch(targetIntent);
            }catch (Throwable e){
                e.printStackTrace();
                StartActivityResultHelper.onActivityResult(Activity.RESULT_CANCELED, null,intentHashCode);
                finish();
            }
        } else {
            // 没有目标Intent时直接失败
            StartActivityResultHelper.onActivityResult(Activity.RESULT_CANCELED, null,intentHashCode);
            finish();
        }
    }
}

4 结果的回传

csharp 复制代码
    // 供TransparentActivity调用的回调方法
    static void onActivityResult(int resultCode, Intent data,int intentHashCode) {
        if(debugable){
            Log.v("ActivityResultx","intent hash1,"+intentHashCode);
        }
        ActivityResultCallback remove = callbackMap.remove(intentHashCode);
​
        if (remove == null){
            if(debugable){
                Log.w("ActivityResultx","ActivityResultListener callback is null,"+intentHashCode);
            }
            return;
        }
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {
                remove.onActivityResult(resultCode,data);
            }
        },1000);
    }

封装好的库:

arduino 复制代码
//root 文件夹里的build.gradle 添加
allprojects {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
​
//module里添加:
dependencies {
  implementation 'com.github.hss01248.StartActivityResult:activityresult2:2.0.2'
}
相关推荐
kymjs张涛6 小时前
零一开源|前沿技术周刊 #14
android·android studio·android jetpack
咖啡の猫6 小时前
安装Android Studio
android·ide·android studio
咖啡の猫6 小时前
Android开发-创建、运行、调试App工程
android
凉冰不加冰7 小时前
MySQL面试集合
android·adb
liang_jy9 小时前
责任链模式
android·设计模式·面试
itseeker9 小时前
只需 5 分钟,让你的 Android App 快速接入 MCP 协议,打通 LLM 的调度
android·github
用户207038619499 小时前
Android AutoService 解耦实战
android
顾林海9 小时前
OkHttp拦截器:Android网络请求的「瑞士军刀」
android·面试·性能优化
jctech9 小时前
解构ComboLite:0 Hook的背后,是哪些精妙的架构设计?
android