一行代码完成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'
}
相关推荐
阿巴斯甜18 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker19 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952720 小时前
Andorid Google 登录接入文档
android
黄林晴21 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android