日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Jetpack:使用 ActivityResult 处理 Activity 之间的数据通信

發布時間:2024/4/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Jetpack:使用 ActivityResult 处理 Activity 之间的数据通信 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

前言?

ActivityResult使用?

ActivityResultContract

原理

總結


前言?

本文先介紹ActivityResult的基本使用,最后會通過源碼來探討背后的原理。

在Android中,我們如果想在Activity之間雙向傳遞數據,需要使用startActivityForResult啟動,然后在onActivityResult中處理返回,另外申請權限也是類似的步驟。

但是這樣的處理方式會讓我們的代碼變得非常復雜,并且也無法保證在 Activity 發送或接收數據時參數的類型安全。

ActivityResult是Jetpack提供的一個功能,可以簡化Activity直接的數據傳遞(包括權限申請)。它通過提供類型安全的 contract (協定) 來簡化處理來自 Activity 的數據。這些協定為一些常見操作 (比如: 拍照或請求權限) 定義了預期的輸入和輸出類型,除此之外您還能夠自定義協定來滿足不同場景的需求。

ActivityResult API 提供了一些組件用于注冊 Activity 的處理結果、發起請求以及在系統返回結果后立即進行相應處理。您也可以在啟動 Activity 的地方使用一個獨立的類接收返回結果,這樣依然能夠保證類型安全。

ActivityResult使用?

使用ActivityResult先添加依賴:

dependencies {// 在 https://developer.android.google.cn/jetpack/androidx/releases/activity 獲得最新版本號def activity_version = "1.2.0"// 在 https://developer.android.google.cn/jetpack/androidx/releases/fragment 獲得最新版本號def fragment_version = "1.3.0"implementation "androidx.activity:activity:$activity_version"implementation "androidx.fragment:fragment:$fragment_version” }

然后先看看最簡單的使用方式,比如打開系統文件管理器選擇一個圖片,代碼如下:

val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->// 處理返回的 Uri }getContent.launch("image/*") //過濾圖片

這里涉及幾個重要的類和函數:

(1)registerForActivityResult:是ComponentActivity的一個函數,注意這里的ComponentActivity是androidx.activity.ComponentActivity而不是androidx.core.app.ComponentActivity,androidx.core中的對應類(截止1.3.0)還不支持這項功能。

public final <I, O> ActivityResultLauncher<I> registerForActivityResult(@NonNull ActivityResultContract<I, O> contract,@NonNull ActivityResultCallback<O> callback)

可以看到這個函數接收兩個參數,分別是ActivityResultContract和回調ActivityResultCallback,ActivityResultContract是封裝啟動所需要的各項參數(組成Intent,后面會細說)。函數返回ActivityResultLauncher,可以看到后面通過他的launch函數就可以啟動activity。

(2)GetContent:ActivityResultContracts.GetContent類是一個繼承ActivityResultContract的具體實現類,封裝了調用系統文件管理器的功能。Jetpack提供了一些常用的ActivityResultContract,比如選取圖片,拍照等等,如果我們需要拉起自己的Activity,就需要自定義一個ActivityResultContract。

(3)launch:ActivityResultLauncher的函數,啟動activity,代替了之前的startActivity。

ActivityResultContract

下面我們來看看GetContent是如何實現的,代碼如下:

public static class GetContent extends ActivityResultContract<String, Uri> {@CallSuper@NonNull@Overridepublic Intent createIntent(@NonNull Context context, @NonNull String input) {return new Intent(Intent.ACTION_GET_CONTENT).addCategory(Intent.CATEGORY_OPENABLE).setType(input);}@Nullable@Overridepublic final SynchronousResult<Uri> getSynchronousResult(@NonNull Context context,@NonNull String input) {return null;}@Nullable@Overridepublic final Uri parseResult(int resultCode, @Nullable Intent intent) {if (intent == null || resultCode != Activity.RESULT_OK) return null;return intent.getData();}}

可以看到實現來兩個關鍵的接口:

  • createIntent就是用于將傳入的參數封裝成intent,用于啟動activity,GetContent的該函數就是封裝一個打開系統文件的Intent;

  • parseResult則是將返回的intent進行解析,整理成我們需要的格式返回,GetContent中我們只需要返回的文件uri即可。

上面我們提到的回調ActivityResultCallback,它的參數就是parseResult的返回值。

所以如果我們自己的頁面間通信,則自定義ActivityResultContract即可,與GetContent類似,根據自己的需求實現這兩個函數即可,當然還可以直接使用jetpack提供的StartActivityForResult(見下面)即可。

在Jetpack提供的已封裝好的ActivityResultContract有(都是ActivityResultContracts的子類):

(1)StartActivityForResult

public static final class StartActivityForResultextends ActivityResultContract<Intent, ActivityResult>

最簡單的,相當于傳統方式的startActivityForResult,只不過將onActivityResult的幾個參數封裝成一個ActivityResult

public ActivityResult(int resultCode, @Nullable Intent data)

(2)StartIntentSenderForResult

相當于Activity.startIntentSender(IntentSender, Intent, int, int, int),與PendingIntent配合使用

(3)RequestMultiplePermissions

用于批量申請權限

public static final class RequestMultiplePermissionsextends ActivityResultContract<String[], java.util.Map<String, Boolean>>

以Map形式返回每個權限的情況。

(4)RequestPermission

申請單個權限

public static final class RequestPermission extends ActivityResultContract<String, Boolean>

通過這兩個來申請權限就可以很方便的進行后續處理。

(5)TakePicturePreview

拉起拍照預覽

public static class TakePicturePreview extends ActivityResultContract<Void, Bitmap>

直接返回bitmap數據。(跟傳統方式一樣,這個bitmap只是一個圖片預覽,因為intent中不能傳輸過大的數據)

注意雖然輸入是Void,但是執行ActivityResultLauncher的lanch函數是還需要傳入一個null才行。

(6)TakePicture

拉起拍照

public static class TakePicture extends ActivityResultContract<Uri, Boolean>

輸入圖片要保存的位置uri

(7)TakeVideo

錄制視頻

public static class TakeVideo extends ActivityResultContract<Uri, Bitmap>

輸入視頻要保存的位置uri,返回視頻的縮略圖。

(8)PickContact

選取聯系人

public static final class PickContact extends ActivityResultContract<Void, Uri>

(9)GetContent

獲取單個文件

public static class GetContent extends ActivityResultContract<String, Uri>

輸入過濾類型,返回文件uri

(10)GetMultipleContents

文件多選

public static class GetMultipleContents extends ActivityResultContract<String, List<Uri>>

同上

(11)OpenDocument

打開單個文檔(拉起的是系統文檔管理器)

@TargetApi(19)public static class OpenDocument extends ActivityResultContract<String[], Uri>

對應Intent.ACTION_OPEN_DOCUMENT,輸入的是類型過濾(如image/*),輸出uri

(12)OpenMultipleDocuments

打開多個文檔,與上面類似

(13)OpenDocumentTree

打開文檔tree,對應Intent.ACTION_OPEN_DOCUMENT_TREE

(14)CreateDocument

新建一個文檔,對應Intent.ACTION_CREATE_DOCUMENT

可以看到Android已經將常用的功能都封裝了,基本可以滿足我們的開發使用。

原理

那么ActivityResult的原理是什么,為什么可以這樣實現?

launch應該很好理解,就是通過ActivityResultContract的createIntent得到的intent去啟動即可。

那么怎么實現result的回調的?

先看registerForActivityResult源碼:

@NonNull@Overridepublic final <I, O> ActivityResultLauncher<I> registerForActivityResult(@NonNull final ActivityResultContract<I, O> contract,@NonNull final ActivityResultRegistry registry,@NonNull final ActivityResultCallback<O> callback) {return registry.register("activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);}@NonNull@Overridepublic final <I, O> ActivityResultLauncher<I> registerForActivityResult(@NonNull ActivityResultContract<I, O> contract,@NonNull ActivityResultCallback<O> callback) {return registerForActivityResult(contract, mActivityResultRegistry, callback);}

最終調用ActivityResultRegistry(mActivityResultRegistry)的register函數:

@NonNullpublic final <I, O> ActivityResultLauncher<I> register(@NonNull final String key,@NonNull final LifecycleOwner lifecycleOwner,@NonNull final ActivityResultContract<I, O> contract,@NonNull final ActivityResultCallback<O> callback) {Lifecycle lifecycle = lifecycleOwner.getLifecycle();if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "+ "attempting to register while current state is "+ lifecycle.getCurrentState() + ". LifecycleOwners must call register before "+ "they are STARTED.");}final int requestCode = registerKey(key);LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);if (lifecycleContainer == null) {lifecycleContainer = new LifecycleContainer(lifecycle);}LifecycleEventObserver observer = new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,@NonNull Lifecycle.Event event) {if (Lifecycle.Event.ON_START.equals(event)) {mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));if (mParsedPendingResults.containsKey(key)) {@SuppressWarnings("unchecked")final O parsedPendingResult = (O) mParsedPendingResults.get(key);mParsedPendingResults.remove(key);callback.onActivityResult(parsedPendingResult);}final ActivityResult pendingResult = mPendingResults.getParcelable(key);if (pendingResult != null) {mPendingResults.remove(key);callback.onActivityResult(contract.parseResult(pendingResult.getResultCode(),pendingResult.getData()));}} else if (Lifecycle.Event.ON_STOP.equals(event)) {mKeyToCallback.remove(key);} else if (Lifecycle.Event.ON_DESTROY.equals(event)) {unregister(key);}}};lifecycleContainer.addObserver(observer);mKeyToLifecycleContainers.put(key, lifecycleContainer);return new ActivityResultLauncher<I>() {@Overridepublic void launch(I input, @Nullable ActivityOptionsCompat options) {onLaunch(requestCode, contract, input, options);}@Overridepublic void unregister() {ActivityResultRegistry.this.unregister(key);}@NonNull@Overridepublic ActivityResultContract<I, ?> getContract() {return contract;}};}

首先可以看到這個函數的調用是有時機限制的,需要在Activity的start生命周期之前(包含start)才可以,否則會拋出異常。

往下可以看到是通過lifecycle這個功能實現的,為啟動的context(如activity)添加一個Observer,在Observer中發現是在onStart這個事件里處理的返回。但是實際上返回是在onActivityResult函數中,這里就需要關注mPendingResults,在ActivityResultRegistry中的doDispatch函數中為它賦予了數據,而doDispatch則被dispatchResult函數調用。那么在那里執行了dispatchResult?

@MainThreadpublic final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {String key = mRcToKey.get(requestCode);if (key == null) {return false;}doDispatch(key, resultCode, data, mKeyToCallback.get(key));return true;}private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,@Nullable CallbackAndContract<O> callbackAndContract) {if (callbackAndContract != null && callbackAndContract.mCallback != null) {ActivityResultCallback<O> callback = callbackAndContract.mCallback;ActivityResultContract<?, O> contract = callbackAndContract.mContract;callback.onActivityResult(contract.parseResult(resultCode, data));} else {// Remove any parsed pending resultmParsedPendingResults.remove(key);// And add these pending results in their placemPendingResults.putParcelable(key, new ActivityResult(resultCode, data));}}

答案是在ComponentActivity中,ComponentActivity中持有一個ActivityResultRegistry的對象,即上面提到的mActivityResultRegistry。在ComponentActivity的onActivityResult和onRequestPermissionsResult中都會調用dispatchResult函數。這樣就實現了結果(包括申請權限)的回調。

@CallSuper@Override@Deprecatedprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {super.onActivityResult(requestCode, resultCode, data);}}@CallSuper@Override@Deprecatedpublic void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) {if (!mActivityResultRegistry.dispatchResult(requestCode, Activity.RESULT_OK, new Intent().putExtra(EXTRA_PERMISSIONS, permissions).putExtra(EXTRA_PERMISSION_GRANT_RESULTS, grantResults))) {if (Build.VERSION.SDK_INT >= 23) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}}}

總結

通過上面的介紹可以看到ActivityResult其實是對之前startActivityForResult模式的一次封裝,在簡化使用的同時增加了安全性。但是我們需要提前注冊回調,并生成ActivityResultLauncher對象,而且這一步需要ComponentActivity對象,而且有時機的限制,所以還不是特別靈活(尤其在權限管理這塊)。

?

總結

以上是生活随笔為你收集整理的Jetpack:使用 ActivityResult 处理 Activity 之间的数据通信的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。