Android Activity Results API

一、Activity Results API介绍

Activity Results API 是 Google官方推荐的Activity、Fragment获取数据的方java怎么读式。Activity Results API 中两个重要的组件:ActivityResultContragoogle翻译ctjava怎么读ActivityResultLauncher

ActivityResultContract: 协议,它定义了如Google何传递数据和如何处理返回的数据。ActivityResultContract是一个抽象类,你需要继承它来创建自己的协议,每个 ActivityResultContracjavascriptt 都需要定义输入和输出类型,如果您不需要任何输入,可使用 Void(在 Kotlin 中,使用 Void? 或 Unit)作为输入类型。

Actijava环境变量配置vityResultLauncher: 启动器,调用ActivityResultLauncherlaunch方法来启动页面跳转,作用相当于原来的startActivity()

二、java怎么读调用预定义的Contractjvm优化

在Activity和Fragment中调用内置的Contract的简单使用。

googleplay入库

implementation 'androidx.activity:activity:1.4.0'
implementation 'androidx.fragment:fragment:1.4.1'

1、预定义Contract介绍

预定义Contract 释义
StartActivityF数据处理的最小单位orResult() 通用的Contract,不做任何转换,Intent作为输入,ActivityResult作为输出,这也是最常用的一个协定。
RequestMultiplePermissions(application) 用于请求一组权限
RequestPermission() 用于请求单个权限
TakePicturePrevjvm类加载机制iew() 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回值为Bitmap图片
TakePicture() 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,并将数据处理图片保存到给定的Uri地址,返回true表示保存成功。
TakeVidjava环境变量配置eo() 调用MediaStore.ACTION_VIDEO_CAPTURE拍摄视频,保存到给定的Uri地址,返回一张缩略图jvm是什么
PickContact() 从通讯录APP获取联系人
CreateDocument() 提示用户选择一个文档,返回一个(file:/http:/content:)google网站登录入口开头的Uri。
OpenDocumentTree() 提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。
OpenMugoogleplayltipleDocuments() 提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式。
OpenDocument() 提示用户选择文档,返回它的Uri
GetContengoogle中国t() 提示用选择一googleplay条内容,返回一个通jvm优化ContentResoljvm是什么ver#openInputStream(Uri)访问原生数据的Uri地址(content://形式)jvm是什么意思 。默认情况下,它增加了 Intent#CATEGORY_OPENABLE, 返回可以表示流的内容。google

上面这些预定义的Contract中,除了StartActivityForResjava环境变量配置ultRequestMultiplePermissions之外,基本都是处理的appetite与其他APP交互,返java环境变量配置回数据的场景,比如,拍照,选择图片,选择联系人,打开文档等等。使用最多的就是StartActivityForResultRequestMultiplePermissions了。

2、简单使用

ActivityResultLauncher必需在activity的onCreate()方法或fragment的onCreate()、onAttach()前先注册,然后在需要调用application的地googleplay安卓版下载方调用launch方法

1)示例一:页面跳转java语言

从MainActivity跳转到TwoActicity


public class MainActivity extends AppCompatActivity {
    private ActivityResultLauncher<Intent> mIntentActivityResultLauncher;
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        //只能在onCreate、onAttach方法中注册
        mIntentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                //回传的数据处理
                String answer = result.getData().getStringExtra("message");
                Toast.makeText(mContext, answer, Toast.LENGTH_LONG).show();
            }
        });
        //按钮点击事件
        findViewById(R.id.bt).setOnClickListener(v -> {
            //执行跳转的方法
            jumpToTwoActivity();
        });
    }
    private void jumpToTwoActivity() {
        //封装Intent
        Intent intent = new Intent(mContext,TwoActivity.class);
        intent.putExtra("message", "问:吃饭了吗?");
        //跳转
        mIntentActivityResultLauncher.launch(intent);
    }
}

从TwoActivity返回数据

public class TwoActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //页面传递过来的数据
        String askMsg = getIntent().getStringExtra("message");
        Toast.makeText(this, askMsg, Toast.LENGTH_LONG).show();
        //点击事件
        findViewById(R.id.bt).setOnClickListener(v -> {
            //回传数据
            Intent intent = new Intent();
            intent.putExtra("message", "答:我吃过了");
            setResult(RESULT_OK, intent);
            finish();
        });
    }
}

2)示例二:申请单个或多个权限

推荐用第三方请求权限的库,不推荐这样写

//申请单个权限
registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
    @Override
    public void onActivityResult(Boolean result) {
        if(result){
            Log.d(TAG,"获取权限成功");
        }else{
            Log.d(TAG,"获取权限失败");
        }
    }
}).launch(Manifest.permission.READ_CONTACTS);
//申请多个权限
String[] permissions = {Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
    @Override
    public void onActivityResult(Map<String, Boolean> result) {
        //result的key为权限,value为权限是否申请通过
        //是否请求权限前弹窗询问?
        //第一次用户用户点击拒绝权限shouldShowRequestPermissionRationale返回false,如果用户拒绝了权限并且勾选了不再询问shouldShowRequestPermissionRationale返回true
        //通过这个特性可以判断哪些权限被永久拒绝
        ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.CAMERA);
    }
}).launch(permissions);

3)示例三:获取联系人信息

获取联系appearance人信息前需要申请权限ManJavaifest.permission.READ_CONTACTS。代码如下:

<uses-permission android:name="android.permission.READ_CONTACTS"/>

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private ActivityResultLauncher<Void> mIntentActivityResultLauncher;
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        //申请读取通讯录权限
        registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
            @Override
            public void onActivityResult(Boolean result) {
                if(result){
                    Log.d(TAG,"获取权限成功");
                    //跳转读取联系人信息
                    mIntentActivityResultLauncher.launch(null);
                }else{
                    Log.d(TAG,"获取权限失败");
                }
            }
        }).launch(Manifest.permission.READ_CONTACTS);
        //只能在onCreate、onAttach方法中注册
        mIntentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.PickContact(), new ActivityResultCallback<Uri>() {
            @SuppressLint("Range")
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public void onActivityResult(Uri result) {
                if (result != null) {
                    StringBuilder builder = new StringBuilder();
                    Cursor cursor = getContentResolver().query(result, null, null, null);
                    while (cursor.moveToNext()) {
                        //联系人ID
                        String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
                        builder.append("用户ID:" + id + ",");
                        //联系人姓名
                        String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
                        builder.append("用户姓名:" + name + ",");
                        Cursor query = getContentResolver().query(
                                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                null,
                                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id,
                                null,
                                null);
                        builder.append("用户手机号:");
                        while (query.moveToNext()) {
                            String phoneNum = query.getString(query.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                            builder.append(phoneNum + " | ");
                        }
                        query.close();
                    }
                    cursor.close();
                    //打印
                    Log.d(TAG, builder.toString());
                }
            }
        });
    }
}

弹出权限申请窗口时点击了同意权限。日志打印结果为:

D/MainActivity: 获取权限成功
D/MainActivity: 用户ID:1,用户姓名:张三,用户手机号:130 1234 5678 | 131 1234 5678 | 

4)示例四:调用相机拍照

//申请多个权限
 String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
 registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
     @Override
     public void onActivityResult(Map<String, Boolean> result) {
         //申请权限成功后申请调用相机拍照
         mIntentActivityResultLauncher.launch(null);
     }
 }).launch(permissions);
 //申请调用相机拍照
mIntentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.TakePicturePreview(), new ActivityResultCallback<Bitmap>() {
     @Override
     public void onActivityResult(Bitmap bitmap) {
         Glide.with(mContext).load(bitmap).format(PREFER_ARGB_8888).into(iv);
     }
 });

5)示例jvm垃圾回收机制五:获取文件

在Android Q以上获取文件比较困难,存在适配的问题,解决办法:
mainfeapplicationst 文件google服务框架配置

<provider
     android:name="androidx.core.content.FileProvider"   //模块中需要继承FileProvider创建新的
     android:authorities="你的包名.fileprovider"
     android:exported="false"
     android:grantUriPermissions="true"
     tools:replace="android:authorities">
        <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/file_paths" />
        </provider>

fappleile_paths.xml指定文件类型

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path
            name="root"
            path="" />
        <files-path
            name="files"
            path="" />
        <cache-path
            name="cache"
            path="" />
        <external-path
            name="external"
            path="" />
        <external-files-path
            name="external_file_path"
            path="" />
        <external-cache-path
            name="external_cache_path"
            path="" />
    </paths>
</resources>

③工具类

/**
 * 包含Uri转path
 * 包含path转uri
 * 兼容Android 10
 */
public class FileProviderUtils {
    /**
     * 根据Uri获取文件绝对路径,解决Android4.4以上版本Uri转换 兼容Android 10
     *
     * @param context
     * @param imageUri
     */
    public static String getFileAbsolutePath(Context context, Uri imageUri) {
        if (context == null || imageUri == null) {
            return null;
        }
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
            return getRealFilePath(context, imageUri);
        }
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && DocumentsContract.isDocumentUri(context, imageUri)) {
            if (isExternalStorageDocument(imageUri)) {
                String docId = DocumentsContract.getDocumentId(imageUri);
                String[] split = docId.split(":");
                String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            } else if (isDownloadsDocument(imageUri)) {
                String id = DocumentsContract.getDocumentId(imageUri);
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);
            } else if (isMediaDocument(imageUri)) {
                String docId = DocumentsContract.getDocumentId(imageUri);
                String[] split = docId.split(":");
                String type = split[0];
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                String selection = MediaStore.Images.Media._ID + "=?";
                String[] selectionArgs = new String[]{split[1]};
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        } // MediaStore (and general)
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            return uriToFileApiQ(context,imageUri);
        }
        else if ("content".equalsIgnoreCase(imageUri.getScheme())) {
            // Return the remote address
            if (isGooglePhotosUri(imageUri)) {
                return imageUri.getLastPathSegment();
            }
            return getDataColumn(context, imageUri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(imageUri.getScheme())) {
            return imageUri.getPath();
        }
        return null;
    }
    //此方法 只能用于4.4以下的版本
    private static String getRealFilePath(final Context context, final Uri uri) {
        if (null == uri) {
            return null;
        }
        final String scheme = uri.getScheme();
        String data = null;
        if (scheme == null) {
            data = uri.getPath();
        } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
            data = uri.getPath();
        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
            String[] projection = {MediaStore.Images.ImageColumns.DATA};
            Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
//            Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
            if (null != cursor) {
                if (cursor.moveToFirst()) {
                    int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                    if (index > -1) {
                        data = cursor.getString(index);
                    }
                }
                cursor.close();
            }
        }
        return data;
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    private static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        Cursor cursor = null;
        String column = MediaStore.Images.Media.DATA;
        String[] projection = {column};
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    private static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }
    /**
     * Android 10 以上适配 另一种写法
     * @param context
     * @param uri
     * @return
     */
    @SuppressLint("Range")
    private static String getFileFromContentUri(Context context, Uri uri) {
        if (uri == null) {
            return null;
        }
        String filePath;
        String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
        ContentResolver contentResolver = context.getContentResolver();
        Cursor cursor = contentResolver.query(uri, filePathColumn, null,
                null, null);
        if (cursor != null) {
            cursor.moveToFirst();
            try {
                filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
                return filePath;
            } catch (Exception e) {
            } finally {
                cursor.close();
            }
        }
        return "";
    }
    /**
     * Android 10 以上适配
     * @param context
     * @param uri
     * @return
     */
    @SuppressLint("Range")
    @RequiresApi(api = Build.VERSION_CODES.Q)
    private static String uriToFileApiQ(Context context, Uri uri) {
        File file = null;
        //android10以上转换
        if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
            file = new File(uri.getPath());
        } else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
            //把文件复制到沙盒目录
            ContentResolver contentResolver = context.getContentResolver();
            Cursor cursor = contentResolver.query(uri, null, null, null, null);
            if (cursor.moveToFirst()) {
                String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                try {
                    InputStream is = contentResolver.openInputStream(uri);
                    File cache = new File(context.getExternalCacheDir().getAbsolutePath(), Math.round((Math.random() + 1) * 1000) + displayName);
                    FileOutputStream fos = new FileOutputStream(cache);
                    FileUtils.copy(is, fos);
                    file = cache;
                    fos.close();
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return file.getAbsolutePath();
    }
    /**
     * path转uri
     * @param context
     * @param filePath
     * @return
     */
    public static Uri toUri(Context context,String filePath) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return FileProvider.getUriForFile(context, context.getApplicationInfo().packageName + ".fileprovider", new File(filePath));
        }
        return Uri.fromFile(new File(filePath));
    }
}

解决了适配问题,看如何获取文件:

获取单类型单文件

//获取单类型单文件
registerForActivityResult(new ActivityResultContracts.GetContent(), new ActivityResultCallback<Uri>() {
    @Override
    public void onActivityResult(Uri uri) {
        if (uri != null) {
            //获取文件真实路径
            String path = FileProviderUtils.getFileAbsolutePath(mContext, uri);
            Log.d(TAG, "文件真实路径:" + path);
        }
    }
}).launch("text/plain");   //具体多少中文件类型可以看MediaFile这个类
//打印日志
D/MainActivity: 文件真实路径:/storage/emulated/0/Android/data/com.cad/files/tbslog/tbslog.txt

获取多种文jvm优化件类型,可以使用OpenDocument。

三、自定义ActivityappleResultConjvm是什么tract

新建一个Contract类,继承自ActivityResultContract<I,O>,其中,I是输入的类jvm调优型,O是输出的类型。需要实现2个方法,createIntent和parseResult,输入类型I作appetite为createIntent的参数,输出类型O作为parseRes数据处理的最小单位ult方法的返回值。

自定义ActivityResultContract<I,O>

/**
 * 自定义ActivityResultContract
 */
public class CustomActivityResultContract extends ActivityResultContract<Integer, String> {
    //input为输入值,然后包装成Intent传递
    @NotNull
    @Override
    public Intent createIntent(@NotNull Context context, Integer input) {
        //Intent包装了跳转到SecondActivity
        Intent intent = new Intent(context, SecondActivity.class);
        intent.putExtra("in", input);
        return intent;
    }
    //返回的Intent拆解,变换成String作为返回值
    @Override
    public String parseResult(int result, @Nullable Intent intent) {
        //拿到SecondActivity返回的Intent,拆解出需要的数据并返回
        return intent.getStringExtra("out");
    }
}

SecondActivity的代码

Intent intent = new Intent();
intent.putExtra("out","2");
setResult(1001,intent);
finish();

在MainActivity中使用

registerForActivityResult(new CustomActivityResultContract(), new ActivityResultCallback<String>() {
    @Override
    public void onActivityResult(String result) {   //参数就是返回值
        Log.d(TAG, "out:" + result);
    }
}).launch(1);   //输入值就是在这里传入

日志

8058-8058/com.abc.rxjava3 D/MainActivity: out:2

四、初级封装

1、在Activity/Fragment中自动注册

Activity生命周期监googleplay听,有个接口类Application.ActivityLifecycleCallb数据处理acks,先看下面的简单示例:
示例的目录:

Android  Activity Results API

首先看AppLifeCallback类,实现Appjava是什么意思lication.ActivityLifecycleCallbacks接口:


class AppLifeCallback : Application.ActivityLifecycleCallbacks {
    companion object {
        const val TAG = "AppHelper"
    }
    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        //打印调用此方法的Activity的类的名称
        Log.d(TAG, "onActivityCreated:${activity.localClassName}")
    }
    override fun onActivityDestroyed(activity: Activity) {
        //打印调用此方法的Activity的类的名称
        Log.d(TAG, "onActivityDestroyed:${activity.localClassName}")
    }
    override fun onActivityStarted(activity: Activity) {}
    override fun onActivityResumed(activity: Activity) {}
    override fun onActivityPaused(activity: Activity) {}
    override fun onActivityStopped(activity: Activity) {}
    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
}

再看ApgooglepHelper类,重点在init方法:

object AppHelper {
    /**
     * 注冊Application的生命周期
     */
    fun init(application: Application) {
        //注册自定义的生命周期
        application.registerActivityLifecycleCallbacks(AppLifeCallback())
    }
}

java语言App中完成init初始化操作:

class App :Application() {
    override fun onCreate() {
        super.onCreate()
        //注册生命周期
        AppHelper.init(this)
    }
}

MainActivitySecondActivity的代码比较简单:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        registerForActivityResult(ActivityResultContracts.StartActivityForResult(), ActivityResultCallback {
            //JUST STARTACTIVITY  DO NOTHING
        }).launch(Intent(this, SecondActivity::class.java));
    }
}
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        //finish
        finish()
    }
}

打印日志:

D/AppHelper: onActivityCreated:MainActivity
D/AppHelper: onActivityCreated:SecondActivity
D/AppHelper: onActivityDestroyed:SecondActivity

可以google服务框架发现实现了Aappstorepplication.ActivityLifecycleCallbacks,所有Activity的生命周期都会在这里回调,那么就可以在这里完成Acti数据处理包括哪些内容vity跳转前的注jvm优化册。

2、初级封装

以下代码来自于DeMon-A数据处理包括哪些内容RA,详细注释都写在代码里
目录结构:

Android  Activity Results API

DemoActivjvm优化ityCallbacks类代码:

object DemoActivityCallbacks : Application.ActivityLifecycleCallbacks {
    private val TAG = "DemoActivityCallbacks"
    const val DEMON_ACTIVITY_KEY = "Demo_Activity_Key"
    val DEMON_FRAGMENT_KEY = "Demo_Fragment_Key"
    //临时存储FragmentCallbacks
    val callbackMap = mutableMapOf<String, DemoFragmentCallbacks>()
    //临时存储DeMonActivityResult
    val resultMap = mutableMapOf<String, DemoActivityResult<Intent, ActivityResult>>()
    override fun onActivityCreated(activity: Activity, p1: Bundle?) {
        if (activity is FragmentActivity) {
            val mapKey: String = activity.javaClass.simpleName + System.currentTimeMillis()
            Log.i(TAG, "onActivityCreated: mapKey=$mapKey")
            //Fragment生命周期的自定义实现类
            val fragmentCallbacks = DemoFragmentCallbacks()
            callbackMap[mapKey] = fragmentCallbacks
            //注册Fragment的生命周期
            activity.supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentCallbacks, false)
            //Activity注册获取ActivityResult对象
            val result = DemoActivityResult(activity, ActivityResultContracts.StartActivityForResult())
            //Activity生命周期持有Intent(getIntent),通过往Intent注入Key来获取ActivityResult对象(HashMap)
            //ActivityResult可以用于跳转
            activity.intent.putExtra(DEMON_ACTIVITY_KEY, mapKey)
            resultMap[mapKey] = result
        }
    }
    override fun onActivityDestroyed(activity: Activity) {
        if (activity is FragmentActivity) {
            val mapKey = activity.intent.getStringExtra(DEMON_ACTIVITY_KEY)
            Log.i(TAG, "onActivityDestroyed: mapKey=$mapKey")
            if (!mapKey.isNullOrEmpty()) {
                //反注册Fragment的生命周期
                callbackMap[mapKey]?.let {
                    activity.supportFragmentManager.unregisterFragmentLifecycleCallbacks(it) }
                //移除Fragment的生命周期的回调
                callbackMap.remove(mapKey)
                //移除ActivityResult
                resultMap.remove(mapKey)
            }
        }
    }
    override fun onActivityStarted(p0: Activity) {}
    override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}
    override fun onActivityResumed(p0: Activity) {}
    override fun onActivityPaused(p0: Activity) {}
    override fun onActivityStopped(p0: Activity) {}
}

DemoFragmentCallbacks类代码google谷歌搜索主页

class DemoFragmentCallbacks : FragmentManager.FragmentLifecycleCallbacks() {
    private val TAG = "DeMonFragmentCallbacks"
    override fun onFragmentAttached(fm: FragmentManager, fragment: Fragment, context: Context) {
        super.onFragmentAttached(fm, fragment, context)
        val mapKey: String = fragment.javaClass.simpleName + System.currentTimeMillis()
        Log.i(TAG, "onFragmentAttached: mapKey=$mapKey")
        //Fragment注册
        val result = DemoActivityResult(fragment, ActivityResultContracts.StartActivityForResult())
        //Activity生命周期持有Intent(getIntent),通过往Intent注入Key来获取ActivityResult对象(HashMap)
        //ActivityResult可以用于跳转
        fragment.requireActivity().intent.putExtra(DEMON_FRAGMENT_KEY, mapKey)
        DemoActivityCallbacks.resultMap[mapKey] = result
    }
    override fun onFragmentDetached(fm: FragmentManager, fragment: Fragment) {
        super.onFragmentDetached(fm, fragment)
        val mapKey = fragment.requireActivity().intent.getStringExtra(DEMON_FRAGMENT_KEY)
        Log.i(TAG, "onFragmentDetached: mapKey=$mapKey")
        if (!mapKey.isNullOrEmpty()) {
            DemoActivityCallbacks.resultMap.remove(mapKey)
        }
    }
}

DemoActiJVMvityResult类的代码:数据处理的最小单位


/**
 * 注册和跳转的具体实现
 * @author DeMon
 * Created on 2022/3/1.
 * E-mail idemon_liu@qq.com
 * Desc:
 */
class DemoActivityResult<I, O>(caller: ActivityResultCaller, contract: ActivityResultContract<I, O>) {
    /**
     * 直接点击返回键或者直接finish是否会触发回调
     * 用于处理一些特殊情况:如只要返回就刷新等
     * 注意此时回调返回的值或者{ActivityResult#getData()}应该为空,需要做好判空处理
     */
    private var isNeedBack = false
    private var launcher: ActivityResultLauncher<I>? = caller.registerForActivityResult(contract) {
        if (isNeedBack) {
            callback?.onActivityResult(it)
        } else {
            if (it != null) {
                if (it is ActivityResult) {
                    if (it.resultCode == Activity.RESULT_OK) callback?.onActivityResult(it)
                } else {
                    callback?.onActivityResult(it)
                }
            }
        }
        //回收单次的callback
        callback = null
    }
    private var callback: ActivityResultCallback<O>? = null
    @JvmOverloads
    fun launch(input: I, isNeedBack: Boolean = false, callback: ActivityResultCallback<O>?) {
        this.callback = callback
        this.isNeedBack = isNeedBack
        launcher?.launch(input)
    }
}

DemoResultHelper类的代码:


/**
 * 注册和具体跳转的调用处
 * @author DeMon
 * Created on 2022/3/2.
 * E-mail idemon_liu@qq.com
 * Desc: Activity Results API
 */
object DemoResultHelper {
    /**
     * 初始化,注册ActivityLifecycleCallbacks
     */
    @JvmStatic
    fun init(@NonNull application: Application) {
        application.registerActivityLifecycleCallbacks(DemoActivityCallbacks)
    }
    /**
     * 跳转使用的方法
     * 获取在Activity生命周期自动注册的ActivityResult
     * Activity中请使用此方法
     */
    @JvmStatic
    fun getActivityResult(@NonNull activity: FragmentActivity): DemoActivityResult<Intent, ActivityResult>? {
        activity.run {
            val mapKey = intent.getStringExtra(DemoActivityCallbacks.DEMON_ACTIVITY_KEY)
            return if (!mapKey.isNullOrEmpty()) {
                DemoActivityCallbacks.resultMap[mapKey]
            } else {
                null
            }
        }
    }
    /**
     * 跳转使用的方法
     * 获取在Fragment生命周期自动注册的ActivityResult
     * Fragment中请使用此方法
     */
    @JvmStatic
    fun getActivityResult(@NonNull fragment: Fragment): DemoActivityResult<Intent, ActivityResult>? {
        fragment.requireActivity().run {
            val mapKey = intent.getStringExtra(DemoActivityCallbacks.DEMON_FRAGMENT_KEY)
            return if (!mapKey.isNullOrEmpty()) {
                DemoActivityCallbacks.resultMap[mapKey]
            } else {
                null
            }
        }
    }
}

测试以上代码的跳转:
先在Application中注册

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        DemoResultHelper.init(this)
    }
}

MainActivity中点击按钮appstore跳转:


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var text = findViewById<TextView>(R.id.tv)
        //点击事件
        text.setOnClickListener {
            //获取提前注册好的ActivityResult
            var result: DemoActivityResult<Intent, ActivityResult>? = DemoResultHelper.getActivityResult(this@MainActivity)
            //跳转
            result?.launch(Intent(this, SecondActivity::class.java), true) {
                //解析回调的数据
                it?.data?.getStringExtra("value")?.let {
                        value -> Log.d("MainActivity", value)
                }
            }
        }
    }
}

SecondActivity直接SetResult后finish:

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        var intent = Intent().putExtra("value", "我是SecondActivity")
        setResult(RESULT_OK, intent)
        finish()
    }
}

打印结果:

D/MainActivity: 我是SecondActivity

成功跳转并带回了数据。

五、基于Kotlin进一步的封装

以下代码来自于DeMon-ARA

增加了二个Kotlin的扩展类:

Android  Activity Results API

Activijvm原理tyResultApi是个ktx大jvm垃圾回收机制杂烩,需要有点耐心才能完全看明白,代码如下:


/**
 * @author DeMon
 * Created on 2021/10/20.
 * E-mail idemon_liu@qq.com
 * Desc: ktx扩展
 */
/**
 * Activity中获取DeMonActivityResult
 */
fun FragmentActivity.getActivityResult(): DeMonActivityResult<Intent, ActivityResult>? {
    val mapKey = intent.getStringExtra(DeMonActivityCallbacks.DEMON_ACTIVITY_KEY)
    return if (!mapKey.isNullOrEmpty()) {
        DeMonActivityCallbacks.resultMap[mapKey]
    } else {
        null
    }
}
/**
 * Fragment中获取DeMonActivityResult
 */
fun Fragment.getActivityResult(): DeMonActivityResult<Intent, ActivityResult>? {
    val mapKey = requireActivity().intent.getStringExtra(DeMonActivityCallbacks.DEMON_FRAGMENT_KEY)
    return if (!mapKey.isNullOrEmpty()) {
        DeMonActivityCallbacks.resultMap[mapKey]
    } else {
        null
    }
}
/**
 * Activity跳转并在回调用获取返回结果
 *  <pre>
 *       val intent = Intent(this@MainActivity,JavaActivity::class.java)
 *       forActivityResult(intent) {
 *       val str = it?.getStringExtra("tag") ?: ""
 *       text.text = "跳转页面返回值:$str"
 *       }
 *  </pre>
 *
 * @param isCanBack 直接点击返回键或者直接finish是否会触发回调
 */
inline fun FragmentActivity.forActivityResult(
        data: Intent,
        isCanBack: Boolean = false,
        crossinline callback: ((result: Intent?) -> Unit)
) {
    getActivityResult()?.launch(data, isCanBack) {
        callback(it.data)
    }
}
/**
 * Activity跳转并在回调用获取返回结果
 *  <pre>
 *       forActivityResult<TestJumpActivity>(
 *       "tag" to TAG,
 *       "timestamp" to System.currentTimeMillis(),
 *       isCanBack = false
 *      ) {
 *      val str = it?.getStringExtra("tag") ?: ""
 *      text.text = "跳转页面返回值:$str"
 *      }
 *  </pre>
 *
 * @param extras 可变参数Pair键值对
 * @param isCanBack 直接点击返回键或者直接finish是否会触发回调
 */
inline fun <reified T : FragmentActivity> FragmentActivity.forActivityResult(
        vararg extras: Pair<String, Any?>,
        isCanBack: Boolean = false,
        crossinline callback: ((result: Intent?) -> Unit)
) {
    val intent = pairIntent<T>(*extras)
    forActivityResult(intent, isCanBack, callback)
}
/**
 * Fragment中使用
 * Activity跳转并在回调用获取返回结果
 *
 * @param isCanBack 直接点击返回键或者直接finish是否会触发回调
 */
inline fun Fragment.forActivityResult(
        data: Intent,
        isCanBack: Boolean = false,
        crossinline callback: ((result: Intent?) -> Unit)
) {
    getActivityResult()?.launch(data, isCanBack) {
        callback(it.data)
    }
}
/**
 * Fragment中使用
 * Activity跳转并在回调用获取返回结果
 *
 * @param extras 可变参数Pair键值对
 * @param isCanBack 直接点击返回键或者直接finish是否会触发回调
 */
inline fun <reified T : FragmentActivity> Fragment.forActivityResult(
        vararg extras: Pair<String, Any?>,
        isCanBack: Boolean = false,
        crossinline callback: ((result: Intent?) -> Unit)
) {
    val intent = pairIntent<T>(*extras)
    forActivityResult(intent, isCanBack, callback)
}
/**
 *  作用同[Activity.finish]
 *  <pre>
 *      finish(this, "Key" to "Value")
 *  </pre>
 *
 * @param params 可变参数Pair键值对
 */
fun FragmentActivity.finishResult(vararg params: Pair<String, Any?>) = run {
    setResult(Activity.RESULT_OK, Intent().putExtras(*params))
    finish()
}
fun FragmentActivity.finishResult(intent: Intent) = run {
    setResult(Activity.RESULT_OK, intent)
    finish()
}
/**
 * 普通跳转
 */
fun Context.toActivity(intent: Intent, vararg extras: Pair<String, Any?>) {
    startActivity(intent.putExtras(*extras))
}
fun Fragment.toActivity(intent: Intent, vararg extras: Pair<String, Any?>) {
    requireActivity().startActivity(intent.putExtras(*extras))
}
inline fun <reified T : FragmentActivity> Context.toActivity(vararg extras: Pair<String, Any?>) {
    startActivity(Intent(this, T::class.java).putExtras(*extras))
}
inline fun <reified T : FragmentActivity> Fragment.toActivity(vararg extras: Pair<String, Any?>) {
    requireActivity().run {
        startActivity(Intent(this, T::class.java).putExtras(*extras))
    }
}
/**
 * 泛型Activity获取一个Intent实例的扩展
 *  <pre>
 *      pairIntent<ActResultActivity>(
 *     "tag" to TAG,
 *    "timestamp" to System.currentTimeMillis()
 *   )
 *  </pre>
 */
inline fun <reified T : FragmentActivity> Context.pairIntent(vararg extras: Pair<String, Any?>) = Intent(this, T::class.java).putExtras(*extras)
inline fun <reified T : FragmentActivity> Fragment.pairIntent(vararg extras: Pair<String, Any?>) = requireActivity().pairIntent<T>(*extras)

IntentExt是Intent的扩展,主要是存取值,代码如下:


/**
 * @author DeMon
 * Created on 2020/7/22.
 * E-mail idemon_liu@qq.com
 * Desc:
 */
fun Intent.putExtras(vararg extras: Pair<String, Any?>): Intent {
    if (extras.isEmpty()) return this
    extras.forEach { (key, value) ->
        value ?: return@forEach
        when (value) {
            is Bundle -> this.putExtra(key, value)
            is Boolean -> this.putExtra(key, value)
            is BooleanArray -> this.putExtra(key, value)
            is Byte -> this.putExtra(key, value)
            is ByteArray -> this.putExtra(key, value)
            is Char -> this.putExtra(key, value)
            is CharArray -> this.putExtra(key, value)
            is String -> this.putExtra(key, value)
            is CharSequence -> this.putExtra(key, value)
            is Double -> this.putExtra(key, value)
            is DoubleArray -> this.putExtra(key, value)
            is Float -> this.putExtra(key, value)
            is FloatArray -> this.putExtra(key, value)
            is Int -> this.putExtra(key, value)
            is IntArray -> this.putExtra(key, value)
            is Long -> this.putExtra(key, value)
            is LongArray -> this.putExtra(key, value)
            is Short -> this.putExtra(key, value)
            is ShortArray -> this.putExtra(key, value)
            is Parcelable -> this.putExtra(key, value)
            is Serializable -> this.putExtra(key, value)
            is Array<*> -> {
                @Suppress("UNCHECKED_CAST")
                when {
                    value.isArrayOf<String>() -> {
                        this.putStringArrayListExtra(key, value as ArrayList<String?>)
                    }
                    value.isArrayOf<CharSequence>() -> {
                        this.putCharSequenceArrayListExtra(key, value as ArrayList<CharSequence?>)
                    }
                    value.isArrayOf<Parcelable>() -> {
                        this.putParcelableArrayListExtra(key, value as ArrayList<out Parcelable?>)
                    }
                }
            }
            else -> {
                throw IllegalArgumentException("Not support $value type ${value.javaClass}..")
            }
        }
    }
    return this
}
class ActivityExtras<T>(private val extraName: String, private val defaultValue: T) : ReadWriteProperty<Activity, T> {
    /**
     * getExtras字段对应的值
     */
    private var extra: T? = null
    override fun getValue(thisRef: Activity, property: KProperty<*>): T {
        // 如果extra不为空则返回extra
        // 如果extra是空的,则判断intent的参数的值,如果值不为空,则将值赋予extra,并且返回
        // 如果intent参数的值也为空,则返回defaultValue,并且将值赋予extra
        return extra ?: thisRef.intent?.get<T>(extraName)?.also { extra = it }
        ?: defaultValue.also { extra = it }
    }
    override fun setValue(thisRef: Activity, property: KProperty<*>, value: T) {
        extra = value
    }
}
/**
 * 获取Intent参数,Fragment
 * 示例同[ActivityExtras]
 */
class FragmentExtras<T>(private val extraName: String, private val defaultValue: T) : ReadWriteProperty<Fragment, T> {
    /**
     * getExtras字段对应的值
     */
    private var extra: T? = null
    override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
        // 如果extra不为空则返回extra
        // 如果extra是空的,则判断intent的参数的值,如果值不为空,则将值赋予extra,并且返回
        // 如果intent参数的值也为空,则返回defaultValue,并且将值赋予extra
        return extra ?: thisRef.arguments?.get<T>(extraName)?.also { extra = it }
        ?: defaultValue.also { extra = it }
    }
    override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
        extra = value
    }
}
fun <T> extraFrag(extraName: String): FragmentExtras<T?> = FragmentExtras(extraName, null)
fun <T> extraFrag(extraName: String, defaultValue: T): FragmentExtras<T> = FragmentExtras(extraName, defaultValue)
fun <T> extraAct(extraName: String): ActivityExtras<T?> = ActivityExtras(extraName, null)
fun <T> extraAct(extraName: String, defaultValue: T): ActivityExtras<T> = ActivityExtras(extraName, defaultValue)
/**
 * [Intent]的扩展方法,此方法可无视类型直接获取到对应值
 * 如getStringExtra()、getIntExtra()、getSerializableExtra()等方法通通不用
 * 可以直接通过此方法来获取到对应的值,例如:
 * <pre>
 *     var mData: List<String>? = null
 *     mData = intent.get("Data")
 * </pre>
 * 而不用显式强制转型
 *
 * @param key 对应的Key
 * @return 对应的Value
 */
fun <O> Intent?.get(key: String, defaultValue: O? = null) =
    this?.internalMap()?.get(key) as? O ?: defaultValue
/**
 * 作用同Intent.[get]
 */
fun <O> Bundle?.get(key: String, defaultValue: O? = null) =
    this?.internalMap()?.get(key) as? O ?: defaultValue
/**
 * 不报错执行
 */
inline fun <T, R> T.runSafely(block: (T) -> R) = try {
    block(this)
} catch (e: Exception) {
    e.printStackTrace()
    null
}
internal object IntentFieldMethod {
    private val bundleClass =
        (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) BaseBundle::class else Bundle::class).java
    private val mExtras: Field? by lazy {
        Intent::class.java.getDeclaredField("mExtras").also { it.isAccessible = true }
    }
    private val mMap: Field? by lazy {
        runSafely {
            bundleClass.getDeclaredField("mMap").also {
                it.isAccessible = true
            }
        }
    }
    private val unparcel: Method? by lazy {
        runSafely {
            bundleClass.getDeclaredMethod("unparcel").also {
                it.isAccessible = true
            }
        }
    }
    internal fun Intent.internalMap() = runSafely {
        mMap?.get((mExtras?.get(this) as? Bundle).also {
            it?.run { unparcel?.invoke(this) }
        }) as? Map<String, Any?>
    }
    internal fun Bundle.internalMap() = runSafely {
        unparcel?.invoke(it)
        mMap?.get(it) as? Map<String, Any?>
    }
}

进一步封装后的使用,还是上例中的二个Actiappstorevity:


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var text = findViewById<TextView>(R.id.tv)
        //点击事件
        text.setOnClickListener {
            //跳转
            forActivityResult(Intent(this@MainActivity, SecondActivity::class.java)) {
                //解析回调的数据
                it?.getStringExtra("value")?.let { value ->
                    Log.d("MainActivity", value)
                }
            }
        }
    }
}
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        //回传数据
        finishResult("value" to "我是SecondActivity")
    }
}

打印的日志:

D/MainActivity: 我是SecondActivity

跳转后成功拿到了回调数据。

appointment考了以下文章,表示感谢:

Android ActivityResultContract使用_xiapproveangxiongfly-程序员ITS301

Android Act数据处理包括哪些内容ivitgoogleplayyResultContract使用

registerForActivityResult()

再见!onActivityResult!你好,Activity Results API!

Uri与java培训真实路径转换File-全适配

丢掉onActivityResult,探索Activity Results API极简方案

DeMon-ARA

—个人学习笔记

发表评论

提供最优质的资源集合

立即查看 了解详情