Just upgrade to Glide 4.9.0,
In kotlin extension ImageView.loadImg, with .asBitmap() it crashes, but without .asBitmap() it works fine for same imageUrl (which loads the image fine in browser)
fun ImageView.loadImg(imageUrl: String) {
var requestOptions : RequestOptions = RequestOptions()
requestOptions.diskCacheStrategy(DiskCacheStrategy.ALL)
requestOptions.error(R.mipmap.ic_launcher)
if (!TextUtils.isEmpty(imageUrl)) {
GlideApp.with(this.context)
.setDefaultRequestOptions(requestOptions)
.asBitmap() //this one causes crash
.load(imageUrl)
.into(this)
}
}
which happened at the setImageBitmap() of a iamgeView derived from AppCompatImageView, when call customImageView?.loadImg(imageUrl):
class CustomImageView : AppCompatImageView {
... ...
override fun setImageBitmap(bm: Bitmap) {//<== crash at here
super.setImageBitmap(bm)
bitmap = bm
}
}
Is there problem to not using the .asBitmap()?
any idea?
Related
From past developers, I got a large library, which is built on the basis of ExoPlayer, which allows you to listen to audio books.
Everything worked fine until the library itself had to update the version of ExoPlayer to a newer one (this was very important)
In short: there is a small interface that has a ProgressBar showing how many seconds the audio has already been playing. Here is the code for this:
private lateinit var mExoPlayer : ExoPlayer
private val compositeDisposable = CompositeDisposable()
private val currentTrackProgress = MutableLiveData<Long>()
private var currentTrackProgressDisposable : Disposable? = null
private val playbackProgressObservable : Observable<Long> =
Observable.interval(1, TimeUnit.MILLISECONDS).map {
mExoPlayer.currentPosition
}.observeOn(AndroidSchedulers.mainThread())
////////////////////// So many code here
////////////////////// Somewhere there is a call to the "createExoPlayer()" function
private fun createExoPlayer() {
mExoPlayer = ExoPlayer.Builder(this).build()
mExoPlayer.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying : Boolean) {
if (isPlaying) {
currentTrackProgressDisposable = playbackProgressObservable.subscribeBy(
onError = { error ->
Log.d("error", error.toString())
},
onNext = { progress ->
currentTrackProgress.value = progress
}
)
compositeDisposable.add(currentTrackProgressDisposable!!)
}
}
})
mExoPlayer.playWhenReady = true
}
Before updating ExoPlayer version, everything worked fine, but after updating to the latest version, an error began to appear in onError:
java.lang.IllegalStateException: Player is accessed on the wrong thread.
Current thread: 'RxComputationThreadPool-1'
Expected thread: 'main'
What could be the reason for this and how to fix it? After all, I didn’t even update the RxJava2 version, but only updated ExoPlayer
My debug output is showing this;
D/EventBus: No subscribers registered for event class com.me.MyEvent
My concern is the index is not working fully and as a result EventBus is using reflection to find the subscriber/s.
The event is published from button click inside a RecyclerView's adapter, and is received by the Fragment containing the RecyclerView. Send and receive work fine. As I said, my concern is reflection is being used.
The index seems to be built correctly. My build.gradle
def eventbus_version = '3.3.1'
implementation "org.greenrobot:eventbus:$eventbus_version"
kapt "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
and
kapt {
arguments {
arg('eventBusIndex', 'com.me.MyEventBusIndex')
}
Here's part of the generated index;
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MyFragment.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("handleEvent", com.me.MyEvent.class,
ThreadMode.POSTING, 0, true),
}));
}
and some fragment code;
override fun onStart() {
super.onStart()
EventBus.builder().addIndex(MyEventBusIndex()).build().register(this)
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
and finally a fragment method;
#Subscribe(sticky = true)
fun handleEvent(event: MyEvent) {
//dostuff
EventBus.getDefault().removeStickyEvent(event)
}
I'm building an object detection application (in Kotlin, for Android). The application uses CameraX to build a camera preview and Google ML to provide machine learning expertise. Just for reference; I used this CameraX documentation and this this Google ML Kit documentation.
I'm currently attempting to print Log.d("TAG", "onSuccess" + it.size) to my IDE console in order to determine if .addonSuccessListener is actually running. If it does, it should print something along the lines of onSuccess1. However, this isn't the case. Infact, it isn't even printing the Log statement from the .addOnFailureListener either, which really confuses me as I'm not even entirely sure the objectDetector code is even running. What really puzzles me is that I have relatively completed the same project in Java and have not faced this issue.
I did have someone point out that within my YourImageAnalyzer.kt class, if mediaImage is null, then I won't see anything logging. However, upon my own debugging (this is actually my very first time debugging), I was unable to find out if my first sentence of this paragraph is true or not. I suppose this issue may provide a lead into how I'll resolve this issue, and also learn how to properly debug.
Here is my YourImageAnalyzer.kt class, and I will also add the code for my MainActivity.kt class below as well.
YourImageAnalyzer.kt
private class YourImageAnalyzer : ImageAnalysis.Analyzer {
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val localModel = LocalModel.Builder()
.setAssetFilePath("mobilenet_v1_0.75_192_quantized_1_metadata_1.tflite")
.build()
val customObjectDetectorOptions =
CustomObjectDetectorOptions.Builder(localModel)
.setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
.enableClassification()
.setClassificationConfidenceThreshold(0.5f)
.setMaxPerObjectLabelCount(3)
.build()
val objectDetector =
ObjectDetection.getClient(customObjectDetectorOptions)
objectDetector //Here is where the issue stems, with the following listeners
.process(image)
.addOnSuccessListener {
Log.i("TAG", "onSuccess" + it.size)
for (detectedObjects in it)
{
val boundingBox = detectedObjects.boundingBox
val trackingId = detectedObjects.trackingId
for (label in detectedObjects.labels) {
val text = label.text
val index = label.index
val confidence = label.confidence
}
}
}
.addOnFailureListener { e -> Log.e("TAG", e.getLocalizedMessage()) }
.addOnCompleteListener { it -> imageProxy.close() }
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
override fun onCreate(savedInstanceState: Bundle?) {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
bindPreview(cameraProvider)
}, ContextCompat.getMainExecutor(this))
}
fun bindPreview(cameraProvider: ProcessCameraProvider) {
val previewView = findViewById<PreviewView>(R.id.previewView)
var preview : Preview = Preview.Builder()
.build()
var cameraSelector : CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
preview.setSurfaceProvider(previewView.surfaceProvider)
var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)
}
}
You are not binding your ImageAnalysis use case. Something in the line of:
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(1280, 720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
and then;
imageAnalysis.setAnalyzer(executor, YourImageAnalyzer())
cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
Also a suggestion as a bonus:
You should get your LocalModel.Builder() out of analyze as this is called each time an image arrives. You do not need to execute this code piece each time as it will make your analysis slower.
So move this code:
val localModel = LocalModel.Builder()
.setAssetFilePath("mobilenet_v1_0.75_192_quantized_1_metadata_1.tflite")
.build()
to just below of the class private class YourImageAnalyzer : ImageAnalysis.Analyzer {.
I have followed the instructions in Google Codelab about the Saved state module.
My gradle dependency is:
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-rc03"
My View Model factory is:
class MyViewModelFactory(val repository: AppRepository, owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle): T {
return MyViewModel(repository, handle) as T
}
}
My View model:
class MyViewModel constructor(val repository: AppRepository, private val savedStateHandle: SavedStateHandle): ViewModel() {
fun getMyParameter(): LiveData<Int?> {
return savedStateHandle.getLiveData(MY_FIELD)
}
fun setMyParameter(val: Int) {
savedStateHandle.set(MY_FIELD, val)
}
}
My Fragment:
class MyFragment : androidx.fragment.app.Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
arguments?.let {
var myField = it.getInt(MY_FIELD)
activitiesViewModel.setMySavedValue(myField ?: 0)
}
}
}
When the app is being used, the live data for the saved state field updates correctly. But as soon as I put the app in background (and I set "don't keep activities" in developer options), and then re open app, the live data shows a value as if it was never set. In other words, the saved state handle seems to forget the field I am trying to save.
Any thoughts?
I had the same problem. It was solved by the fact that I stopped requesting saved values from SavedStateHandle in the onRestoreInstanceState method. This call is correctly used in the onCreate method.
The main question is: is lparams simply gone from Anko, or am I doing something terribly wrong? The following snippet fails to compile:
verticalLayout {
}.lparams(width = matchParent, height = matchParent) {
topMargin = dip(10)
}
While this works without any problems:
verticalLayout {
layoutParams = LinearLayout.LayoutParams(matchParent, matchParent).apply {
topMargin = dip(10)
}
}
I wouldn't mind the second option too much, but you have to specify the layout type when generating the params, which can get a bit tiresome (and is also more brittle than the original solution).
I haven't found anything on the Anko GitHub page, the changelog, or by skimming recent commits. Here's the full UI class for reference:
class ReviewsFragmentUi(ctx: AnkoContext<ReviewsFragment>) : AnkoComponent<ReviewsFragment> {
override fun createView(ui: AnkoContext<ReviewsFragment>) = ui.apply {
verticalLayout {
layoutParams = LinearLayout.LayoutParams(matchParent, matchParent).apply {
topMargin = dip(10)
}
}
}.view
}
Relevant Gradle entries (I'm using Kotlin 1.0.0-beta-3595):
ext.versions = [
anko : '0.8.1',
]
compile "org.jetbrains.anko:anko-common:$versions.anko",
compile "org.jetbrains.anko:anko-sdk21:$versions.anko",
compile "org.jetbrains.anko:anko-support-v4:$versions.anko",
compile "org.jetbrains.anko:anko-design:$versions.anko",
compile "org.jetbrains.anko:anko-appcompat-v7:$versions.anko",
compile "org.jetbrains.anko:anko-cardview-v7:$versions.anko",
compile "org.jetbrains.anko:anko-recyclerview-v7:$versions.anko",
compile "org.jetbrains.anko:anko-gridlayout-v7:$versions.anko",
As a follow-up question: if lparams is indeed gone, then is there a more elegant replacement than what I'm already doing?
Apparently lparams is still there, but cannot be used as an extension function for the outermost layout:
So the following code fails:
override fun createView(ui: AnkoContext<ReviewsFragment>) = ui.apply {
verticalLayout {
// Layout elements here
}.lparams {
// Layout params here
}
}.view
But this compiles fine:
override fun createView(ui: AnkoContext<ReviewsFragment>) = ui.apply {
verticalLayout {
lparams {
// Layout params here
}
// Layout elements here
verticalLayout { }.lparams {
// lparams works fine if there is a parent layout
}
}
}.view
It's worth noting that using the non-tailing version of lparams is discouraged for inner layouts: it will create the wrong sublass of LayoutParams when the nested layouts are of different types. For a complete discussion, see this GitHub Issue.
Why don't you use the most recent way to write createView() method?
I think the following solves your problem:
override fun createView(ui: AnkoContext<ReviewsFragment>) : View = with(ui) {
return verticalLayout {
// Layout elements here
}.lparams {
// Layout params here
}
}