一行代码完成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有些兼容性问题需要注意:
-
配置变更的影响
- 当设备发生旋转等配置变更时,透明 Activity 可能被意外销毁重建,导致回调丢失
- 解决方案:在 Manifest 中为该 Activity 添加
android:configChanges="orientation|screenSize"
-
启动模式的兼容性
- 使用
singleInstance
或singleTask
等特殊启动模式时,在部分设备上可能导致回调无法正确触发 - 建议使用默认的
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'
}