package com.example.absolutelydumb
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.InputType
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
//sets var to some random number and asks for a guess, giving feedback on proximity to answer
class MainActivity : AppCompatActivity() {
private val startButton: Button = findViewById(R.id.startButton)
private val submitButton: Button = findViewById(R.id.submit)
private val instructionsText: TextView = findViewById(R.id.instructionView)
private val response: EditText = findViewById(R.id.answerInput)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val startText: String = getString(R.string.startText)
startButton.text = startText
//TODO("Add autofillHints")
startButton.setOnClickListener {
numberGuess()
}
//TODO("Set text back to start after guessing is done")
}
fun numberGuess() {
response.inputType = InputType.TYPE_CLASS_NUMBER
val clickText: String = getString(R.string.clickText)
startButton.text = clickText
//TODO("Add autofillHints")
instructionsText.text = getString(R.string.instructions)
val randomNumber: Int = rand(0,10)
submitButton.setOnClickListener {
val responseText = Integer.parseInt(answerInput.text.toString())
if(responseText == randomNumber) {
instructionsText.text = getString(R.string.correctGuess)
} else {
instructionsText.text = getString(R.string.badGuess)
}
}
}
fun rand(start: Int, end: Int): Int {
//require(start <= end) { "Illegal Argument" }
return (start..end).random()
}
}
Above is the code that I've been writing. This is my first project, so I don't know what went wrong. I fixed a variable type problem in the numberGuess function but when I finished fixing it the app wouldn't open and the virtual device stated a "app keeps stopping" error.
Here is the Run log that lists off errors. The build log didn't have any errors.
07/09 19:58:05: Launching 'app' on phone1.
Install successfully finished in 1 s 266 ms.
$ adb shell am start -n "com.example.absolutelydumb/com.example.absolutelydumb.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Connected to process 5935 on device 'phone1 [emulator-5554]'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.absolutelydumb, PID: 5935
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.absolutelydumb/com.example.absolutelydumb.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2843)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:159)
at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:157)
at android.content.Context.obtainStyledAttributes(Context.java:675)
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:692)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:659)
at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:479)
at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:214)
at com.example.absolutelydumb.MainActivity.<init>(MainActivity.kt:13)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2831)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
You cannot call findViewById before onCreate() or setContentView() is called. Since you are using findViewById at the declaration site of your four properties, it is being called at the time your Activity class is being instantiated, which is before onCreate is called.
There are two ways to handle this.
Use lateinit var and set the properties right after your call to setContentView():
private lateinit var startButton: Button
//...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.startButton)
//...
Use by lazy so it is only called when the property is first accessed, which will be after setContentView() is called:
private val startButton: Button by lazy { findViewById(R.id.startButton) }
Or you can use view binding, which gets rid of most of this boilerplate and is less error-prone. By error-prone, I mean you can call findViewById and accidentally search for a view that isn't present in the current layout and it will crash at runtime. View binding makes this impossible.
Related
I have a MainActivity using a DrawerLayout and tabs with 2 fragments.
My first fragment contains a list of elements in a RecyclerView, and I can click on each element to "select" it (which calls a SDK function to login to a hardware device). When selected, this triggers a change on the Fragment's ViewModel:
// Selected device changes when an item is clicked
private val _devices = MutableLiveData<List<DeviceListItemViewModel>>()
private val _selectedDevice = MutableLiveData<ConnectedDevice>()
val devices: LiveData<List<DeviceListItemViewModel>> by this::_devices
val selectedDevice: LiveData<ConnectedDevice> by this::_selectedDevice
Then I have a shared ViewModel between both fragments, which also has a currentDevice variable like this:
private val _currentDevice = MutableLiveData<ConnectedDevice>()
val currentDevice: LiveData<ConnectedDevice> by this::_currentDevice
So in the Fragment that contains the list, I have the following code to update the shared ViewModel variable:
private val mViewModel: DeviceManagementViewModel by viewModels()
private val mSharedViewModel: MainActivityViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Log.d(classTag, "Fragment view created")
val binding = ActivityMainDevicesManagementFragmentBinding.bind(view)
binding.apply {
viewModel = mViewModel
lifecycleOwner = viewLifecycleOwner
}
// Observe fragment ViewModel
// If any device is clicked on the list, do the login on the shared ViewModel
mViewModel.selectedDevice.observe(this, {
mSharedViewModel.viewModelScope.launch {
if (it != null) {
mSharedViewModel.setCurrentDevice(videoDevice = it)
} else mSharedViewModel.unsetCurrentDevice()
}
})
}
The problem is that if the shared ViewModel's currentDevice variable is set, I get exceptions whenever I try to open a Dialog or start a new activity. If I modify the setCurrentDevice function in the shared ViewModel, then it works fine (or if I don't select any device).
The exceptions I see are this when starting a new Activity:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.placeholder.easyview/com.example.myapp.activities.settings.SettingsActivity}: java.lang.IllegalArgumentException: display must not be null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3594)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
Caused by: java.lang.IllegalArgumentException: display must not be null
at android.app.ContextImpl.createDisplayContext(ContextImpl.java:2386)
at android.content.ContextWrapper.createDisplayContext(ContextWrapper.java:977)
at com.android.internal.policy.DecorContext.<init>(DecorContext.java:50)
at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2348)
at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2683)
at com.android.internal.policy.PhoneWindow.getDecorView(PhoneWindow.java:2116)
at androidx.appcompat.app.AppCompatActivity.initViewTreeOwners(AppCompatActivity.java:219)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:194)
at com.example.myapp.activities.settings.SettingsActivity.onCreate(SettingsActivity.kt:34)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1310)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3403)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3594)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
And this if I try to open a Dialog:
java.lang.ArrayIndexOutOfBoundsException: length=16; index=2448
at android.view.InsetsState.peekSource(InsetsState.java:374)
at android.view.InsetsSourceConsumer.updateSource(InsetsSourceConsumer.java:291)
at android.view.InsetsController.updateState(InsetsController.java:654)
at android.view.InsetsController.onStateChanged(InsetsController.java:621)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1058)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:110)
at android.app.Dialog.show(Dialog.java:340)
at android.app.AlertDialog$Builder.show(AlertDialog.java:1131)
at com.example.myapp.activities.main.fragments.DeviceManagementFragment.showAddDeviceMethodDialog(DeviceManagementFragment.kt:151)
at com.example.myapp.activities.main.fragments.DeviceManagementFragment.access$showAddDeviceMethodDialog(DeviceManagementFragment.kt:33)
at com.example.myapp.activities.main.fragments.DeviceManagementFragment$onViewCreated$$inlined$apply$lambda$1.onClick(DeviceManagementFragment.kt:52)
at android.view.View.performClick(View.java:7448)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28309)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
EDIT: Looks like the problem actually lies in the other fragment, where I have the following code (in onViewCreated method):
// If the shared view model device changes, this must change too
mSharedViewModel.currentDevice.observe(this, {
if (it != null) {
mViewModel.setCurrentDevice(it)
} else mViewModel.unsetCurrentDevice()
})
mViewModel.currentDevice.observe(this, {
if (it != null) {
mViewModel.fetchChannels()
}
})
If I comment out the second part (where the fetchChannels occurs), it works well. Even if I comment out the fetchChannels call only, it works.
This is the code of the fetchChannels function:
fun fetchChannels() = viewModelScope.launch {
Log.d(classTag, "Getting channels for device ${currentDevice.value}")
currentDevice.value?.let {
val fetchedChannels = deviceLibManager.getChannels(it.videoDevice)
_currentDevice.value?.channels?.clear()
_currentDevice.value?.channels?.addAll(fetchedChannels)
if (fetchedChannels.isNotEmpty()) {
_currentDevice.value?.currentChannel = fetchedChannels[0]
}
}
}
The following line is the one giving me trouble:
val fetchedChannels = deviceLibManager.getChannels(it.videoDevice)
That function is just this:
suspend fun getChannels(videoDevice: VideoDevice): List<VideoChannel> {
try {
Log.i(classTag, "Getting channels from device ${videoDevice}")
val channels = videoDevice.getChannelsAsync()
return channels
} catch (exception: Exception) {
when (exception) {
is UnknownVendorException -> {
Log.w(classTag, "Device ${videoDevice} cannot get channels because the vendor is unknown")
}
is NetworkException -> {
Log.w(classTag, "Device ${videoDevice} cannot get channels because it is unreachable")
}
else -> {
Log.w(classTag, "Device ${videoDevice} cannot get channels, reason: ${exception.message}")
}
}
return emptyList()
}
}
And the implementation in the SDK is this:
override suspend fun getChannelsAsync(): List<VideoChannel> = withContext(Dispatchers.IO) {
Log.i(classTag, "Trying to get channels for device: $logName")
val channels = ArrayList<VideoChannel>()
getZeroChannel()?.let {
channels.add(it)
}
channels.addAll(getAllChannels())
if (channels.isNotEmpty()) {
Log.i(classTag, "Successfully retrieved ${channels.size} channels for device: $logName")
return#withContext channels
} else {
Log.w(classTag, "Error retrieving channels for device $logName or no channels exist")
throw Exception()
}
}
The other functions just make a network call and retrieve some data, it should not be messing with the UI at all.
I am testing with a Xiaomi Mi A3 using Android 10.
Can someone help me? Thank you.
So I don't really know why but I found the answer.
In the SDK, the functions getZeroChannel and getAllChannels were not suspending functions, although they make a network call. So what I did is:
Move the withContext(Dispatchers.IO) part to those two functions (the ones who actually make the network call), and make them suspend functions.
Remove the withContext(Dispatchers.IO) part from getChannelsAsync function. Keep it as a suspend function though.
After these changes everything works as expected. I still don't know why, so if someone could shed some light, that would be very much appreciated.
private fun setupGeckoView() {
val runtime = GeckoRuntime.create(this) // crashes on this line
geckoSession.open(runtime)
geckoView.setSession(geckoSession)
val url = String(Base64.decode(MYURL, Base64.DEFAULT))
geckoSession.loadUri(url)
geckoSession.progressDelegate = createProgressDelegate()
geckoSession.settings.allowJavascript = true
}
i call setUpGeckoView methon in onCreat() but when i click back and
reopen the app then app crashes with IllegalStateException saying
"Failed to initialize GeckoRuntime. It works first time only crashed when i click back and then open app again"
Logs are given below
Process: arholding.kargoshop.mk, PID: 16444
java.lang.RuntimeException: Unable to start activity ComponentInfo{arholding.kargoshop.mk/arholding.kargoshop.mk.SeckoActivity}: java.lang.IllegalStateException: Failed to initialize GeckoRuntime
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3447)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3594)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2146)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7762)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1047)
Caused by: java.lang.IllegalStateException: Failed to initialize GeckoRuntime
at org.mozilla.geckoview.GeckoRuntime.create(GeckoRuntime.java:458)
at org.mozilla.geckoview.GeckoRuntime.create(GeckoRuntime.java:333)
at arholding.kargoshop.mk.SeckoActivity.setupGeckoView(SeckoActivity.kt:23)
at arholding.kargoshop.mk.SeckoActivity.onCreate(SeckoActivity.kt:19)
at android.app.Activity.performCreate(Activity.java:7981)
at android.app.Activity.performCreate(Activity.java:7970)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
This exception will be thrown if there is already an active Gecko instance running. There are many ways to resolve this problem.
Solution 1: Get the default runtime for the given context.
Change your code from
val runtime = GeckoRuntime.create(this)
to
val runtime = GeckoRuntime.getDefault(this)
Solution 2: Kill the process when exit app by finishing activity, add this code into your activity.
override fun onDestroy() {
Process.killProcess(Process.myPid())
super.onDestroy()
}
Solution 3: Only create a new instance if there is no active instance running
private fun setupGeckoView() {
if (geckoRuntime == null) {
geckoRuntime = GeckoRuntime.create(this)
}
geckoSession.open(geckoRuntime!!)
geckoView.setSession(geckoSession)
val url = String(Base64.decode(MYURL, Base64.DEFAULT))
geckoSession.loadUri(url)
geckoSession.progressDelegate = createProgressDelegate()
geckoSession.settings.allowJavascript = true
}
companion object {
var geckoRuntime: GeckoRuntime? = null
}
SO I am trying to observe some LiveData in my repository, so I can work with this data and update it's items according to my operations. But I can't figure out how to access the data. When I try to observe it, the ~Observeris asking for a LifecycleOwner. Reading about similar problems I've seen a suggestion to extendLifecycleService, but when I do I get the error sayingOnly one class may appear in a supertype list`.
How do I go around this?
This is my service:
class DetectJobIntentService : JobIntentService() {
private val TAG = "DetectJobIntentServi22"
fun enqueueWork(context: Context, work: Intent) {
enqueueWork(context, DetectJobIntentService::class.java, 12, work)
}
override fun onHandleWork(intent: Intent) {
Log.d(TAG, "onHandleWork")
val options = FirebaseVisionFaceDetectorOptions.Builder()
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
.setMinFaceSize(0.15f)
.build()
val detector = FirebaseVision.getInstance()
.getVisionFaceDetector(options)
val repo = PhotoRepository(application)
val allPhotos = repo.getAllPhotos()
allPhotos.observe(SomeLifeCycleOwner, Observer {
for (file in it) {
val image = FirebaseVisionImage.fromFilePath(application, Uri.parse(file.uri))
detector.detectInImage(image).addOnSuccessListener { list ->
if (list.isNotEmpty()) {
file.hasFaces = 1
repo.update(file)
} else {
file.hasFaces = 2
repo.update(file)
}
}
}
})
}
}
EDIT: Following Sina's suggestion which sounds right, I've tried implementing a query that fetches the data into a non-LiveData object my app crashes.
This is the quesry I've added in my Dow:
#Query("SELECT * FROM photos_table")
fun getAllPhotosStatic(): MutableList<Photo>
If I run at at this point it's all good.
Then in my repository I've added this:
val allPhotosStatic = photoDao.getAllPhotosStatic()
And as soon as I've done that and try to run the app it crashes, and I get this stack:
2019-11-13 23:49:37.079 20720-20720/tech.levanter.anyvision E/AndroidRuntime: FATAL EXCEPTION: main
Process: tech.levanter.anyvision, PID: 20720
java.lang.RuntimeException: Unable to start activity ComponentInfo{tech.levanter.anyvision/tech.levanter.anyvision.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class tech.levanter.anyvision.viewModels.AllPhotosViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2991)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6882)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Caused by: java.lang.RuntimeException: Cannot create an instance of class tech.levanter.anyvision.viewModels.AllPhotosViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:238)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130)
at tech.levanter.anyvision.MainActivity.onCreate(MainActivity.kt:66)
at android.app.Activity.performCreate(Activity.java:7232)
at android.app.Activity.performCreate(Activity.java:7221)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2971)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6882)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:230)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130)
at tech.levanter.anyvision.MainActivity.onCreate(MainActivity.kt:66)
at android.app.Activity.performCreate(Activity.java:7232)
at android.app.Activity.performCreate(Activity.java:7221)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2971)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6882)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:267)
at androidx.room.RoomDatabase.query(RoomDatabase.java:323)
at androidx.room.util.DBUtil.query(DBUtil.java:83)
at tech.levanter.anyvision.room.PhotoDao_Impl.getAllPhotosStatic(PhotoDao_Impl.java:154)
at tech.levanter.anyvision.room.PhotoRepository.<init>(PhotoRepository.kt:37)
at tech.levanter.anyvision.viewModels.AllPhotosViewModel.<init>(AllPhotosViewModel.kt:12)
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:230)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130)
at tech.levanter.anyvision.MainActivity.onCreate(MainActivity.kt:66)
at android.app.Activity.performCreate(Activity.java:7232)
at android.app.Activity.performCreate(Activity.java:7221)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2971)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6882)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
I thought maybe I can just get a copy of the data into a static list in my repository, but for that I'd need to observe it in the repository and I'm stuck at the same place again because I don't have a lifecycleOwner.
The idea in MVVM is to observe from view if you are doing an action that you want to be sure is finished do not wrap it in a livedata. Basically you don't need any life cycle aware component. Your query should return list of photos without any need to observe it. I mean in your dao if you don't wrap data in a livedata you don't need to observe anything and getAllPhotos return photos. You can do this kind of thing anywhrere just do everything from background, in a thread or something. For both kind of designs you can have 1 livedata wraped method in dao and 1 method without livedata for jobs that need to be done serially.
Update: put your code in a background thread:
new Thread(new Runnable() {
#Override
public void run() {
val allPhotosStatic = photoDao.getAllPhotosStatic()
for (file in it) {
val image = FirebaseVisionImage.fromFilePath(application, Uri.parse(file.uri))
detector.detectInImage(image).addOnSuccessListener { list ->
if (list.isNotEmpty()) {
file.hasFaces = 1
repo.update(file)
} else {
file.hasFaces = 2
repo.update(file)
}
}
}
}
}).start();
or:
AsyncTask.execute(new Runnable() {
#Override
public void run() {
val allPhotosStatic = photoDao.getAllPhotosStatic()
for (file in it) {
val image = FirebaseVisionImage.fromFilePath(application, Uri.parse(file.uri))
detector.detectInImage(image).addOnSuccessListener { list ->
if (list.isNotEmpty()) {
file.hasFaces = 1
repo.update(file)
} else {
file.hasFaces = 2
repo.update(file)
}
}
}
}
});
I am learning kotlin. i set button click handler in the xml as shown below in the code..when the button is clicked i call the method beginSearch(), but then the App crash and i receive the below posted
error.
please let me know how to fix this error.
Activity
private fun clickHandler(v : View?) : Unit {
when(v?.id) {
R.id.btnCheckSearchResult -> beginSearch("Trump")
}
}
button:
<Button
android:id="#+id/btnCheckSearchResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Check Search Result"
android:layout_below="#id/etSearchEntry"
android:onClick="clickHandler"/>
error:
--------- beginning of crash
2019-07-09 15:02:06.146 14883-14883/com.example.retrofitkotlin_v10 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.retrofitkotlin_v10, PID: 14883
java.lang.IllegalStateException: Could not find method clickHandler(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton with id 'btnCheckSearchResult'
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.resolveMethod(AppCompatViewInflater.java:424)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:381)
at android.view.View.performClick(View.java:6256)
at android.view.View$PerformClick.run(View.java:24701)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
java.lang.IllegalStateException: Could not find method
clickHandler(View) in a parent or ancestor Context for android:onClick
attribute defined on view class
android.support.v7.widget.AppCompatButton with id
'btnCheckSearchResult'
Remove android:onClick="clickHandler" from XML
And try with
btnCheckSearchResult.setOnClickListener {
beginSearch("Trump")
}
And
import kotlinx.android.synthetic.main.your_layout.*
Function must be public (or protected).
public fun clickHandler(v : View?) : Unit {
when(v?.id) {
R.id.btnCheckSearchResult -> beginSearch("Trump")
}
}
P.S - You could also use setOnClickListener as the other answers have pointed out.
Just use
btnCheckSearchResult.setOnClickListener {
beginSearch("Trump")
}
or
btnCheckSearchResult.setOnClickListener({
beginSearch("Trump")
})
make your function public
public fun clickHandler(v : View?) : Unit {
when(v?.id) {
R.id.btnCheckSearchResult -> beginSearch("Trump")
}
}
When click button on TradersActivity start JsonViewActivity
I need to check is success started JsonViewActivity. Here Espresso test:
#RunWith(AndroidJUnit4::class)
class TradersActivityTest {
val context = InstrumentationRegistry.getInstrumentation().getContext()
val targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext()
#Rule
#JvmField
var tradersActivitytRule = ActivityTestRule(TradersActivity::class.java)
#Test
fun itemList_viewJsonButton_click_check() {
//scroll
onView(withId(R.id.tradersRecyclerView))
.perform(scrollToPosition<RecyclerView.ViewHolder>(checkItemCount));
// click
onView(withRecyclerView(R.id.tradersRecyclerView).atPositionOnView(checkItemCount, R.id.viewJsonButton))
.perform(click())
// check is start JsonViewActivity activity
intended(hasComponent(JsonViewActivity::class.java.getName()))
}
But test is fail:
Started running tests
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.test.espresso.intent.Intents.internalIntended(org.hamcrest.Matcher, androidx.test.espresso.intent.VerificationMode, java.util.List)' on a null object reference
at androidx.test.espresso.intent.Intents$2.check(Intents.java:194)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAssertion.check(ViewInteraction.java:419)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:282)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:268)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
You should use IntentsTestRule instead of ActivityTestRule.