我是靠谱客的博主 可爱早晨,这篇文章主要介绍Android Activity Results API-,现在分享给大家,希望可以做个参考。

一、Activity Results API介绍

Activity Results API 是 Google官方推荐的Activity、Fragment获取数据的方式。Activity Results API 中两个重要的组件:ActivityResultContractActivityResultLauncher

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

ActivityResultLauncher: 启动器,调用ActivityResultLauncherlaunch方法来启动页面跳转,作用相当于原来的startActivity()

二、调用预定义的Contract

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

引入库

复制代码
1
2
3
implementation 'androidx.activity:activity:1.4.0' implementation 'androidx.fragment:fragment:1.4.1'

1、预定义Contract介绍

预定义Contract释义
StartActivityForResult()通用的Contract,不做任何转换,Intent作为输入,ActivityResult作为输出,这也是最常用的一个协定。
RequestMultiplePermissions()用于请求一组权限
RequestPermission()用于请求单个权限
TakePicturePreview()调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回值为Bitmap图片
TakePicture()调用MediaStore.ACTION_IMAGE_CAPTURE拍照,并将图片保存到给定的Uri地址,返回true表示保存成功。
TakeVideo()调用MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,保存到给定的Uri地址,返回一张缩略图。
PickContact()从通讯录APP获取联系人
CreateDocument()提示用户选择一个文档,返回一个(file:/http:/content:)开头的Uri。
OpenDocumentTree()提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。
OpenMultipleDocuments()提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式。
OpenDocument()提示用户选择文档,返回它的Uri
GetContent()提示用选择一条内容,返回一个通过ContentResolver#openInputStream(Uri)访问原生数据的Uri地址(content://形式) 。默认情况下,它增加了 Intent#CATEGORY_OPENABLE, 返回可以表示流的内容。

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

2、简单使用

ActivityResultLauncher必需在activity的onCreate()方法或fragment的onCreate()、onAttach()前先注册,然后在需要调用的地方调用launch方法

1)示例一:页面跳转

从MainActivity跳转到TwoActicity

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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返回数据

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)示例二:申请单个或多个权限

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//申请单个权限 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)示例三:获取联系人信息

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

复制代码
1
2
<uses-permission android:name="android.permission.READ_CONTACTS"/>
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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()); } } }); } }

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

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

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//申请多个权限 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)示例五:获取文件

在Android Q以上获取文件比较困难,存在适配的问题,解决办法:
mainfest 文件配置

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
<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>

file_paths.xml指定文件类型

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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>

③工具类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/** * 包含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)); } }

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

获取单类型单文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//获取单类型单文件 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

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

三、自定义ActivityResultContract

新建一个Contract类,继承自ActivityResultContract<I,O>,其中,I是输入的类型,O是输出的类型。需要实现2个方法,createIntent和parseResult,输入类型I作为createIntent的参数,输出类型O作为parseResult方法的返回值。

自定义ActivityResultContract<I,O>

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** * 自定义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的代码

复制代码
1
2
3
4
5
6
Intent intent = new Intent(); intent.putExtra("out","2"); setResult(1001,intent); finish();

在MainActivity中使用

复制代码
1
2
3
4
5
6
7
8
registerForActivityResult(new CustomActivityResultContract(), new ActivityResultCallback<String>() { @Override public void onActivityResult(String result) { //参数就是返回值 Log.d(TAG, "out:" + result); } }).launch(1); //输入值就是在这里传入

日志

复制代码
1
2
3
8058-8058/com.abc.rxjava3 D/MainActivity: out:2

四、初级封装

1、在Activity/Fragment中自动注册

Activity生命周期监听,有个接口类Application.ActivityLifecycleCallbacks,先看下面的简单示例:
示例的目录:

image.png

首先看AppLifeCallback类,实现Application.ActivityLifecycleCallbacks接口:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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) {} }

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
object AppHelper { /** * 注冊Application的生命周期 */ fun init(application: Application) { //注册自定义的生命周期 application.registerActivityLifecycleCallbacks(AppLifeCallback()) } }

App中完成init初始化操作:

复制代码
1
2
3
4
5
6
7
8
9
class App :Application() { override fun onCreate() { super.onCreate() //注册生命周期 AppHelper.init(this) } }

MainActivitySecondActivity的代码比较简单:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
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)); } }
复制代码
1
2
3
4
5
6
7
8
9
10
class SecondActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_second) //finish finish() } }

打印日志:

复制代码
1
2
3
4
5
D/AppHelper: onActivityCreated:MainActivity D/AppHelper: onActivityCreated:SecondActivity D/AppHelper: onActivityDestroyed:SecondActivity

可以发现实现了Application.ActivityLifecycleCallbacks,所有Activity的生命周期都会在这里回调,那么就可以在这里完成Activity跳转前的注册。

2、初级封装

以下代码来自于DeMon-ARA,详细注释都写在代码里
目录结构:

image.png

DemoActivityCallbacks类代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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类代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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) } } }

DemoActivityResult类的代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/** * 注册和跳转的具体实现 * @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类的代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/** * 注册和具体跳转的调用处 * @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中注册

复制代码
1
2
3
4
5
6
7
8
class App : Application() { override fun onCreate() { super.onCreate() DemoResultHelper.init(this) } }

MainActivity中点击按钮跳转:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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() } }

打印结果:

复制代码
1
2
3
D/MainActivity: 我是SecondActivity

成功跳转并带回了数据。

五、基于Kotlin进一步的封装

以下代码来自于DeMon-ARA

增加了二个Kotlin的扩展类:

image.png

ActivityResultApi是个ktx大杂烩,需要有点耐心才能完全看明白,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/** * @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的扩展,主要是存取值,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/** * @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?> } }

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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) } } } } }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
class SecondActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_second) //回传数据 finishResult("value" to "我是SecondActivity") } }

打印的日志:

复制代码
1
2
3
D/MainActivity: 我是SecondActivity

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

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

Android ActivityResultContract使用_xiangxiongfly-程序员ITS301

Android ActivityResultContract使用

registerForActivityResult()

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

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

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

DeMon-ARA

—个人学习笔记

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的直接点击文末小卡片可以领取哦!我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址
点击下方卡片免费领取。

最后

以上就是可爱早晨最近收集整理的关于Android Activity Results API-的全部内容,更多相关Android内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(91)

评论列表共有 0 条评论

立即
投稿
返回
顶部