I have the following AIDL file
package com.mindtherobot.samples.tweetservice;
interface TweetCollectorListener {
void handleTweetsUpdated();
}
I tried to make generic works in AIDL so far. It doesn't work. The following code will flag error.
package com.mindtherobot.samples.tweetservice;
interface TweetCollectorListener<E> {
E handleTweetsUpdated();
}
It seems that generic doesn't work in AIDL. However, that's my guess, as Android Interface Definition Language doesn't talk much on generic.
Just want to confirm, is it true generic doesn't work in AIDL? Is there any workaround?
From The official AIDL docs :
List
All elements in the List must be one of the supported data types
in this list or one of the other AIDL-generated interfaces or
parcelables you've declared. A List may optionally be used as a
"generic" class (for example, List). The actual concrete class
that the other side receives is always an ArrayList, although the
method is generated to use the List interface. Map All elements in the
Map
must be one of the supported data types in this list or one of the
other AIDL-generated interfaces or parcelables you've declared.
Generic maps, (such as those of the form Map are not
supported. The actual concrete class that the other side receives is
always a HashMap, although the method is generated to use the Map
interface.
So, as you can see there is only limited support for generics using Lists, not even Maps, so no a custom parametrized types is not supported.
Related
I develop the flutter package and in this package one of class get function argument.
When I use this package in another flutter app, I can send function as prop to this package .
Now I just want to learn if I use the flutter module and build the aar , how can I send function as prop in native android ?
Thanks
This is not possible because you can send only serializable types
By default, AIDL supports the following data types:
All primitive types in the Java programming language (such as int, long, char, boolean, and so on)
Arrays of primitive types such as int[]
String
CharSequence
List
All elements in the List must be one of the supported data types in
this list or one of the other AIDL-generated interfaces or parcelables you've declared. A List may optionally be used as a parameterized type class (for example, List). The actual concrete class that the other side receives is always an ArrayList, although the method is generated to use the List interface.
Map
.
All elements in the Map must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you've declared. Parameterized type maps, (such as those of the form Map<String,Integer>) are not supported. The actual concrete class that the other side receives is always a HashMap, although the method is generated to use the Map interface. Consider using a Bundle as an alternative to Map.
For more detailed information you can refer to the official AIDL documentation
I've been playing recently with KSP and have managed to develop interesting capabilities (such as automatic recyclerview and view holder generators based on their layout id), and so far all is well.
However, I am now trying to do something different than creating files based on the annotations I design. Instead of creating files, I would only want to populate a list with the classes/objects annotated by me.
Example:
ClassA.kt
#MyAnnotation
class ClassA(context: Context): SomeBaseClass(context) {
override fun baseClassFunction() {
// custom code goes here
}
}
ClassB.kt
#MyAnnotation
class ClassB(context: Context): SomeBaseClass(context) {
override fun baseClassFunction() {
// custom code goes here
}
}
MyListAgregator.kt
object MyListAgregator {
const val classList: List<SomeBaseClass> = mutableListOf()
}
Where my custom KSP would do the following
Collect all classes/objects (the usual) that are annotated by my
#MyAnnotation
Create an instance of them with the appropriate
parameters (in this case just a context)
Add each one to the classList in the MyListAgregator
I can always get to the point of the class collection (step 1) but the rest is a complete mystery to me, and feels like KSP always expects to create code, not execute it? Perhaps I am incorrect on the later one, but I could not find anything related to that explained in the available documentation.
Another alternative I considered, but would rather avoid out of concern for any negative hit (performance, for example) is to actually modify that MyListAgregator file directly to include all the instances in the list, as if I had written them myself. However, I would still prefer to go with the previous option instead if it is at all possible.
First, you need to establish a ruleset that will be applied to the classes annotated with your annotation (symbol in KSP glossary). For example, they must contain one argument, that argument must be a member property and of type Context and must be subclass of SomeBaseClass. I suggest first look up for correct inheritance then look up for argument count and type.
You are still within reading and exploring all files with this symbol. Filtering based on this ruleset you will land with a set of classes at point 2.
Here, KSP can provide you with the interface to generate your code. However, KSP will not let you edit the source file, but generate new one based on your conditions. Here you have to write your implementation for the overriden function, by visiting it
You can preserve the output (newly generated classes at step 2) and generate your MyListAggregator object.
Recently at my company a debate started after reviewing a different approach for writing heavy duty classes.
A big Java class holding component specific logic (no standard OOP principles made sense) had to be rewritten in Kotlin. The solution provided was splitting the logic in categories and the categories into separate files with internal extension functions to the main class.
Example:
Main.kt
class BigClass {
// internal fields exposed to the extension functions in different files
// Some main logic here
}
BusinessLogic.kt
internal fun BigClass.handleBussinessCase() {
// Complex business logic handled here accessing the exposed internal fields from BigClass
}
What are your thoughts on this? I haven't seen it used anywhere maybe for a good reason, but the alternative of thousand lines classes seems worse.
You have to consider that an extension function is nothing more than a function with an implicit first parameter which is referenced with this.
So in your case you'd have something like:
internal fun handleBussinessCase(ref: BigClass)
which would translate to Java as:
static void handleBussinessCase(BigClass ref)
But this could be assumed to be a delegate pattern, which could be encapsulated much cleaner in Kotlin as well.
Since the properties have to be internal anyhow, you could just inject these as a data class into smaller use-cases. If you define an interface around these (which would make the properties public though), you could create a delegate pattern with it and still reference each property with this in your implementation.
Here are some thoughts on making extension functions for the class:
It will be a utility function that will operate with the object you're extending, it will not be an object function, meaning that it will have access to only public methods and properties;
If you're planning to use class that being extended in unit tests, these methods (extensions) will be harder to mock;
Most likely they wont behave as you expect when used with inherited objects.
Maybe I missed something, so please read more about extensions here.
Trying to remote some interfaces from an Android service app to another client app, and using the AIDL approach..
So I have an interface IJobController in IJobController.aidl:
import com.example.jobs.api.IJobExecutionContext;
interface IJobController {
List<IJobExecutionContext> getCurrentlyExecutingJobs();
...
}
where IJobExecutionContext is defined in its own AIDL:
interface IJobExecutionContext {
Bundle getJobResult();
...
long getJobStartTime();
long getJobEndTime();
long getJobRunTime();
}
When those definitions are compiled in the gen folder, I get errors like these in the generated IJobController.java file:
Type mismatch: cannot convert from ArrayList<IBinder> to List<IJobExecutionContext>
The method writeBinderList(List<IBinder>) in the type Parcel is not applicable for the arguments (List<IJobExecutionContext>)
From what I understand such interfaces can be used in a List (or Map) along with primitive types, Parcelable, etc.
In another method where I return just an AIDL interface, everything is ok, but it seems that in the list container it is not.
Do I need to just return a raw List, or a list of an implementation of the IJobExecutionContext which is also Parcelable?
Thanks.
The problem is that Binder knows nothing about your interface IJobExecutionContext, it does not know how to deal with it. Binder knows how to marshal and unmarshal only primitive types. In other cases, you need to explain it how to perform these operations implementing Parcelable interface. Thus, to my knowledge, you cannot work with the interfaces in AIDL files. You need to substitute IJobExecutionContext with the class that implements this interface. Additionally, this class must also implement Parcelable interface.
I am getting into Inversion of Control, specifically using Guice and RoboGuice for Android and I have a question.
I have a method call that returns a Resource (which is essentially an XML or JSON String).
public Resource getResource(){
// Some implementation details that call a web service and throw the result in a string...
String resource = ........
}
The Resource class is really just a wrapped String, so I figured it made sense to pass it in in the constructor, since it is an essential part of a Resource object.
public class Resource{
Resource(String theXMLorJSON){
...
}
}
A couple of questions:
How do I construct a new Resource in the getResource call? I would think that I want to use IoC and not call new in the method.
If another class takes a Resource in the constructor, how can I use the Guice container to construct it when I need a dynamic String at construction time? I just asked a similar question and believe there may be a specific way to handle this using Guice.
Thanks so much!
I think you may be misunderstanding something about dependency injection. You don't need to try to avoid using new in all cases... you primarily want to avoid using new to create anything that you might want to be able to mock out for testing, and it's generally best to allow the container to wire up any class that depends on such an object.
Your Resource class, though, sounds like a simple value object that you can easily create manually in any testing you do. It also doesn't depend on any kind of services... it just contains a String. So there's no reason to try to have the container create it.
The class containing the getResource() method, on the other hand, you definitely want the container to create, because you'd like to be able to use something that depends on that class in testing without having to actually call a web service.
Note that if you have a class with a constructor that takes both dependencies you want injected by the container and parameters that are only known at runtime, you need to create an intermediate factory of some kind with a method that only takes the runtime parameters. With Guice you can automatically create such a factory from an interface using the Assisted Inject (not sure if that works with RoboGuice, but it's easy to create such a factory implementation manually too).