一、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
: 启动器,调用ActivityResultLauncher
的launch
方法来启动页面跳转,作用相当于原来的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环境变量配置ult
和RequestMultiplePermissions
之外,基本都是处理的appetite与其他APP交互,返java环境变量配置回数据的场景,比如,拍照,选择图片,选择联系人,打开文档等等。使用最多的就是StartActivityForResult
和RequestMultiplePermissions
了。
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,先看下面的简单示例:
示例的目录:
首先看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)
}
}
MainActivity
和SecondActivity
的代码比较简单:
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,详细注释都写在代码里
目录结构:
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的扩展类:
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