我在一个android应用程序中使用google sheets api,并试图在一个单独的线程中获取凭据。GoogleSheets
是我创建的一个类,用于获取电子表格的凭据和单元格值private lateinit var sheets: GoogleSheets
是一个示例变量,我在类的开头声明了这个变量。
load.setOnClickListener(View.OnClickListener {
Thread {
sheets = GoogleSheets(requireContext(), "1fs1U9-LMmkmQbQ2Kn-rNVHIQwh6_frAbwaTp7MSyDIA")
}.start()
println(sheets)
println(sheets.getValues("A1"))
})
但是它告诉我sheets变量还没有初始化:
kotlin.UninitializedPropertyAccessException: lateinit property sheets has not been initialized
下面是完整的类:
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.example.frcscout22.GoogleSheets
import com.example.frcscout22.R
// TODO: AUTOMATICALLY SWITCH TO DATA TAB AFTER LOAD OR CREATE NEW
class Home: Fragment(R.layout.fragment_home) {
private lateinit var sheets: GoogleSheets
private val STORAGE_PERMISSION_CODE = 100
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
@RequiresApi(Build.VERSION_CODES.P)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View = inflater.inflate(R.layout.fragment_home, container, false)
val load = view.findViewById<Button>(R.id.button3)
val new = view.findViewById<Button>(R.id.button4)
val editText = view.findViewById<EditText>(R.id.editTextTextPersonName)
if (!checkPermission()) {
println("requested")
requestPermission()
}
new.setOnClickListener(View.OnClickListener {
val sheets = GoogleSheets(requireContext(),"1fs1U9-LMmkmQbQ2Kn-rNVHIQwh6_frAbwaTp7MSyDIA")
sheets.setValues("A1", "this is a test", "USER_ENTERED")
println(sheets.getValues("A1").values)
})
load.setOnClickListener(View.OnClickListener {
Thread {
sheets = GoogleSheets(requireContext(), "1fs1U9-LMmkmQbQ2Kn-rNVHIQwh6_frAbwaTp7MSyDIA")
}.start()
println(sheets)
println(sheets.getValues("A1"))
})
return view
}
private fun requestPermission(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
//Android is 11(R) or above
try {
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
val uri = Uri.fromParts("package", requireActivity().packageName, "Home")
intent.data = uri
storageActivityResultLauncher.launch(intent)
}
catch (e: Exception){
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
storageActivityResultLauncher.launch(intent)
}
}
else{
//Android is below 11(R)
ActivityCompat.requestPermissions(requireActivity(),
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE),
STORAGE_PERMISSION_CODE
)
}
}
private val storageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
//here we will handle the result of our intent
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
//Android is 11(R) or above
if (Environment.isExternalStorageManager()){
//Manage External Storage Permission is granted
}
}
else{
//Android is below 11(R)
}
}
private fun checkPermission(): Boolean{
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
//Android is 11(R) or above
Environment.isExternalStorageManager()
}
else{
//Android is below 11(R)
val write = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
val read = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
write == PackageManager.PERMISSION_GRANTED && read == PackageManager.PERMISSION_GRANTED
}
}
}
我不知道为什么变量没有被初始化。它是否与它在线程中有关?我如何解决这个问题?谢谢!!
2条答案
按热度按时间63lcw9qa1#
你正在启动另一个线程来初始化它,所以如果你立即检查它,另一个线程还没有时间初始化属性。这是
lateinit
的误用,你也没有利用线程同步,所以它很容易受到其他bug的影响。我建议加载带有协程的工作表,当需要在任何地方使用它时,使用suspend函数来检索示例。当只使用协程来访问属性时,不需要担心线程同步。
实际上,这应该放在一个比Fragment更长寿的类中,这样你就不必在每次重新创建Fragment时都重新加载它,但是为了简单起见,我在这个例子中只将它放在Fragment中。
zxlwwiss2#
我猜错误是在告诉你,当你这样做的时候,错误就发生了:
lateinit
是您向编译器承诺,在任何东西试图读取sheets
之前,您已经为它赋值了,您正在对println(sheets)
执行此操作。您在刚刚启动的线程中对它赋值--但在当前线程上运行println
语句之前,该操作不太可能完成!如果你涉及到这样的线程,你也必须担心同步--仅仅因为你的工作线程设置了
sheets
的值,并不意味着 current 线程会看到更新的值。如果你不熟悉整个问题和你必须采取的步骤来确保事情保持一致,你可以看一下here。你最好的办法就是在
sheets
赋值后,在线程内做println
的事情。如果你做的事情比这更复杂,并且需要回到主线程上,你可以在视图上做post
和Runnable
。老实说,如果你可以用协程来代替,从长远来看,这可能会让你的生活更容易