亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 超詳細深刨Activity Result API的使用

 更新時間:2022年03月26日 10:44:00   作者:guolin  
這篇文章主要介紹了Android開發(fā)中Activity Result API的使用,掌握了它以后你就可以放棄startActivityForResult了,感興趣的朋友一起來看看吧

如果你將項目中的appcompat庫升級到1.3.0或更高的版本,你會發(fā)現(xiàn)startActivityForResult()方法已經被廢棄了。

這個方法相信所有做過Android的開發(fā)者都用過,它主要是用于在兩個Activity之間交換數(shù)據(jù)的。

那么為什么這個如此常用的方法會被廢棄呢?官方給出的說法是,現(xiàn)在更加建議使用Activity Result API來實現(xiàn)在兩個Activity之間交換數(shù)據(jù)的功能。

我個人的觀點是,startActivityForResult()方法并沒有什么致命的問題,只是Activity Result API在易用性和接口統(tǒng)一性方面都做得更好。既然有更好的API,那么就不再建議去使用過去老舊的API,所以才把startActivityForResult()方法標為了廢棄。

其實除了startActivityForResult()方法之外,還有像requestPermissions()方法也被標為了廢棄??雌饋硭鼈儍烧咧g好像并沒有什么關聯(lián),但是到了Activity Result API中,它們就被歸屬到了統(tǒng)一的API模板當中。因此,我們可以使用非常類似的代碼去實現(xiàn)在兩個Activity之間交換數(shù)據(jù),以及請求運行時權限的功能。

另外,Activity Result API的用法非常簡單,一學就會。相信你看完本篇文章之后,就可以將自己項目中所有相關的代碼都升級成Activity Result API的用法。

那么我們開始吧。

在兩個Activity之間交換數(shù)據(jù)

如果想要在兩個Activity之間交換數(shù)據(jù),我們先回顧一下傳統(tǒng)的寫法:

class FirstActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        firstButton.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            startActivityForResult(intent, 1)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            1 -> {
                if (resultCode == RESULT_OK) {
                    val data = data?.getStringExtra("data")
                    // Handle data from SecondActivity
                }
            }
        }
    }
    
}

這里調用了startActivityForResult()方法去向SecondActivity請求數(shù)據(jù),然后在onActivityResult()方法中去解析SecondActivity返回的結果。

那么SecondActivity中的代碼是什么樣的呢?這里我們就簡單模擬一下,隨便返回一個數(shù)據(jù)即可:

class SecondActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        val secondButton = findViewById<Button>(R.id.second_button)
        secondButton.setOnClickListener {
            val intent = Intent()
            intent.putExtra("data", "data from SecondActivity")
            setResult(RESULT_OK, intent)
            finish()
        }
    }

}

如此一來,F(xiàn)irstActivity向SecondActivity請求數(shù)據(jù)的功能就通了,是不是感覺也挺簡單的?所以我剛才說了,startActivityForResult()方法并沒有什么致命的問題。

那么接下來我們學習一下如何使用Activity Result API來實現(xiàn)同樣的功能。

首先,SecondActivity中的代碼是不需要修改的。這部分代碼并沒有被廢棄,Activity Result API也與它無關。

FirstActivity中的代碼,我們需要使用Activity Result API來替代startActivityForResult()的寫法,如下所示:

class FirstActivity : AppCompatActivity() {

    private val requestDataLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
            val data = result.data?.getStringExtra("data")
            // Handle data from SecondActivity
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        firstButton.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            requestDataLauncher.launch(intent)
        }
    }
    
}

注意這里的代碼變更。我們完全移除了對onActivityResult()方法的重寫,而是調用registerForActivityResult()方法來注冊一個對Activity結果的監(jiān)聽。

registerForActivityResult()方法接收兩個參數(shù),第一個參數(shù)是一種Contract類型,由于我們是希望從另外一個Activity中請求數(shù)據(jù),因此這里使用了StartActivityForResult這種Contract。第二個參數(shù)是一個Lambda表達式,當有結果返回時則會回調到這里,然后我們在這里獲取并處理數(shù)據(jù)即可。

registerForActivityResult()方法的返回值是一個ActivityResultLauncher對象,這個對象當中有一個launch()方法可以用于去啟用Intent。這樣我們就不需要再調用startActivityForResult()方法了,而是直接調用launch()方法,并把Intent傳入即可。

這兩種寫法到底孰優(yōu)孰劣呢?我個人感覺還是Activity Result API的寫法更簡單一點,不過總體優(yōu)勢并沒有那么大。Activity Result API真正的優(yōu)勢在于我們接下來要講的內容。

請求運行時權限

除了startActivityForResult()方法之外,requestPermissions()方法也被廢棄了。至于理由都是一樣的,推薦使用Activity Result API。

那么要如何使用Activity Result API來請求運行時權限呢?不要驚訝,它將會出奇得簡單:

class FirstActivity : AppCompatActivity() {
    
    private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
        if (granted) {
            // User allow the permission.
        } else {
            // User deny the permission.
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        firstButton.setOnClickListener {
            requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
        }
    }
    
}

我們只需關注代碼變更的部分。

由于這次是請求運行時權限,因此不能再使用剛才的StartActivityForResult來作為Contract了,而是要使用RequestPermission這種Contract。

另外由于指定了不同的Contract類似,Lambda表達式的參數(shù)也會發(fā)生變化?,F(xiàn)在Lambda表達式會傳入一個布爾型的參數(shù),用于告訴我們用戶是否允許了我們請求的權限。

最后,launch()方法的參數(shù)也發(fā)生了變化,現(xiàn)在只需傳入要請求的權限名即可。

有沒有發(fā)現(xiàn),這兩段代碼的模板出奇得一致。我們使用了兩段差不多的代碼,實現(xiàn)了之前幾乎并沒有太大聯(lián)系的兩個功能。這就是Activity Result API的好處,它將一些API的接口統(tǒng)一化,使得我們在實現(xiàn)特定功能的時候能夠變得非常簡單。

內置Contract

剛才我們體驗了StartActivityForResult和RequestPermission這兩種Contract,分別用于在兩個Activity之間交換數(shù)據(jù),以及請求運行時權限。它們都是Activity Result API中內置的Contract。

那么除此之外,我們還有哪些內置Contract可以使用呢?

下面是我列出的appcompat 1.3.0版本所支持的所有內置Contract,以后還可能會繼續(xù)增加新的Contract:

StartActivityForResult()
StartIntentSenderForResult()
RequestMultiplePermissions()
RequestPermission()
TakePicturePreview()
TakePicture()
TakeVideo()
PickContact()
GetContent()
GetMultipleContents()
OpenDocument()
OpenMultipleDocuments()
OpenDocumentTree()
CreateDocument()

每個Contract的命名已經明確表示它們的作用是什么了,也就是說,當我們要實現(xiàn)以上Contract所包含的功能時,都不需要再自己手動費力去寫了,Activity Result API已經幫我們支持好了。

比如,我想要調用手機攝像頭去拍攝一張圖片,并且得到這張圖片的Bitmap對象,那么就可以使用TakePicturePreview這個Contract。

實現(xiàn)代碼如下:

class FirstActivity : AppCompatActivity() {

    private val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap ->
        // bitmap from camera
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        firstButton.setOnClickListener {
            takePictureLauncher.launch(null)
        }
    }

}

代碼非常簡單,就是換了一下Contract類型,然后Lambda表達式的參數(shù)會變成bitmap對象。另外由于TakePicturePreview這個Contract不需要輸入?yún)?shù),所以我們調用launch()方法的時候直接傳入null就可以了。

看到這里,可能有些讀者朋友會比較好奇。我怎么知道每種Contract要求什么輸入?yún)?shù),以及Lambda表達式中返回的參數(shù)是什么呢?

這個很簡單,只需要看一下這個Contract的源碼即可。比如TakePicturePreview的源碼如下:

/**
 * An {@link ActivityResultContract} to
 * {@link MediaStore#ACTION_IMAGE_CAPTURE take small a picture} preview, returning it as a
 * {@link Bitmap}.
 * <p>
 * This can be extended to override {@link #createIntent} if you wish to pass additional
 * extras to the Intent created by {@code super.createIntent()}.
 */
public static class TakePicturePreview extends ActivityResultContract<Void, Bitmap> {
    ...
}

我們暫時不用關心TakePicturePreview內部的具體實現(xiàn),只要看一下它在繼承父類時指定的泛型類型即可。其中第一個參數(shù)就是要求的輸入?yún)?shù),而第二個參數(shù)就是Lambda表達式返回的輸出參數(shù)。

只要掌握這個小技巧,每種Contract你就都能輕松運用自如了。那么我就不再多做演示,剩下這些Contract的用法等待你自己去探索。

自定義Contract

除了以上內置Contract之外,我們確實也可以定義自己的Contract類型。

雖然我覺得這個必要性并不是很強,因為內置Contract已經可以幫助我們應對絕大多數(shù)場景了。

不過,自定義Contract并不是一件復雜的事情。相反,它非常簡單,所以這里還是簡略提一下吧。

剛才我們大概看到了TakePicturePreview的源碼實現(xiàn),它必須繼承自ActivityResultContract類,并通過泛型來指定當前Conract類型的輸入?yún)?shù)和輸出參數(shù)。

ActivityResultContract是一個抽象類,它的內部定義了兩個抽象方法,如下所示:

public abstract class ActivityResultContract<I, O> {

    public abstract @NonNull Intent createIntent(@NonNull Context context, I input);

    public abstract O parseResult(int resultCode, @Nullable Intent intent);
    ...
}

也就是說,任何一個繼承自ActivityResultContract的Contract,都需要重寫createIntent()和parseResult()這兩個方法。

而這兩個方法的作用也非常明顯。createIntent()就是用于創(chuàng)建一個Intent,后續(xù)會使用這個Intent來發(fā)起動作,比如啟動另外一個Activity去獲取數(shù)據(jù),或者打開相機去拍照等等。而parseResult()則是用于解析響應的結果,并把解析出來的結果作為輸出參數(shù)返回到Lambda表達式當中。

每一個內置的Contract都是使用的這種規(guī)則來封裝的自己的邏輯。

那么我們要自定義一個什么樣的Contract來進行演示呢?

我想了一下,剛才在編寫兩個Activity之間交換數(shù)據(jù)的時候,我們需要顯示地啟動SecondActivity,并手動將SecondActivity返回的數(shù)據(jù)從Intent中解析出來,這就稍微有些麻煩。而借助自定義Contract就可以對此處進行優(yōu)化。

新建一個叫做GetDataFromSecondActivity的Contract,代碼如下所示:

class GetDataFromSecondActivity : ActivityResultContract<Void, String?>() {

    override fun createIntent(context: Context, input: Void?): Intent {
        return Intent(context, SecondActivity::class.java)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                return intent.getStringExtra("data")
            }
        }
        return null
    }
    
}

我們通過泛型指定,這個Contract的輸入?yún)?shù)是Void,輸出參數(shù)是一個字符串。

然后在createIntent()方法中,我們手動創(chuàng)建了一個Intent,并將它的用途設置為打開SecondActivity。

最后在parseResult()方法中,我們對SecondActivity返回的結果進行解析,并將解析出來的字符串作為輸出參數(shù)返回。

這樣一個自定義的Contract就完成了,而我們使用這個Contract再去實現(xiàn)最開始的在兩個Activity之間交換數(shù)據(jù)的功能,就會變得更加簡單:

class FirstActivity : AppCompatActivity() {

    private val getDataLauncher = registerForActivityResult(GetDataFromSecondActivity()) { data ->
        // Handle data from SecondActivity
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        firstButton.setOnClickListener {
            getDataLauncher.launch(null)
        }
    }

}

可以看到,借助GetDataFromSecondActivity這個Contract,我們不需要再顯式地聲明去啟動SecondActivity,launch()方法直接傳入null即可。另外,我們也不需要再去手動解析SecondActivity返回的數(shù)據(jù),lambda表達式上的參數(shù)就是解析出來的結果了。

最后一個小問題

到這里,我們基本就將Activity Result API的所有內容都學完了。

在本篇文章的最后,我想再回答一個小問題。因為我自己當初在使用Activity Result API的時候產生過這樣的疑惑,所以我猜或許也會有朋友有同樣的問題,那么在這里就順手解答了。

現(xiàn)在你已經知道,Activity Result API是可以完全取代startActivityForResult()方法的。但是我們在調用startActivityForResult()方法時,除了傳入Intent之外,還需要再傳入一個requestCode,用于在多個任務之間進行區(qū)分。比如如下代碼:

class FirstActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        val secondButton = findViewById<Button>(R.id.second_button)
        firstButton.setOnClickListener {
            val intent = Intent(Intent.ACTION_VIEW)
            startActivityForResult(intent, 1)
        }
        secondButton.setOnClickListener {
            val intent = Intent(Intent.ACTION_DIAL)
            startActivityForResult(intent, 2)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            1 -> {
                // Handle result for ACTION_VIEW
            }
            2 -> {
                // Handle result for ACTION_DIAL
            }
        }
    }

}

這里我們分別在兩處調用了startActivityForResult()方法,它們各自用于處理不同的任務,因此需要給它們設置不同的requestCode。

在onActivityResult()方法當中,我們?yōu)榱藚^(qū)分這個結果是來自之前的哪個任務的,所以要在這里再對requestCode進行判斷。

這是以前使用startActivityForResult()方法時的傳統(tǒng)寫法。

而Activity Result API是沒有地方讓你傳入requestCode的。

我在剛接觸Activity Result API的時候受思維慣性的影響被這個問題困擾了一下,沒有地方傳入requestCode該怎么辦呢?

后來思維轉過來彎之后發(fā)現(xiàn),原來Activity Result API根本就不需要requestCode這種東西,我們可以使用如下寫法來實現(xiàn)和剛才完全一樣的功能:

class FirstActivity : AppCompatActivity() {

    private val actionViewLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        // Handle result for ACTION_VIEW
    }

    private val actionDialLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        // Handle result for ACTION_DIAL
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        val firstButton = findViewById<Button>(R.id.first_button)
        val secondButton = findViewById<Button>(R.id.second_button)
        firstButton.setOnClickListener {
            val intent = Intent(Intent.ACTION_VIEW)
            actionViewLauncher.launch(intent)
        }
        secondButton.setOnClickListener {
            val intent = Intent(Intent.ACTION_DIAL)
            actionDialLauncher.launch(intent)
        }
    }

}

由此也可以看出,Activity Result API的設計更加合理,不需要借助requestCode這種魔術數(shù)字也能對多個任務進行區(qū)分。

一切都更加簡單和清晰。

到此這篇關于Android 超詳細深刨Activity Result API的使用的文章就介紹到這了,更多相關Android Activity Result 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論