Why does the dex methods count add 3 when I add one method? - android-proguard

I add a method
public static void cancelAll(RequestQueue queue,#NonNull final Object tag){
queue.cancelAll(new RequestQueue.RequestFilter() {
#Override
public boolean apply(Request<?> request) {
Object oldTag = request.getTag();
if(tag == null || oldTag == null){
return false;
}
if(oldTag instanceof Integer && tag instanceof Integer){
return (int)oldTag == (int)tag;
}else if(oldTag instanceof String && tag instanceof String){
return ((String) tag).equalsIgnoreCase((String)oldTag);
}
return false;
}
});
}
And the dex method count add 3.
I think they are cancelAll and apply.Where is the third?
BuildType is release.And I use proguard.
It's same when I set buildType to debug.

The dex method count that you are referring to specifies the total number of methods that were referenced within a single dex, not only the declared ones!
Therefore your question cannot be easily answered.
Why not just dump all the method names using a tool such as dextra with and without this method and then diff the outputs?
For example:
./dextra -M classes.dex
will output all the referenced method_ids list in following format:
Method (0): <init> Access Flags: 0x0 Params: () Returns: V
Method (1): attachBaseContext Access Flags: 0x0 Params: (Landroid/content/Context;) Returns: V
Method (2): getClassLoader Access Flags: 0x0 Params: () Returns: Ljava/lang/ClassLoader;
...

Related

How to handle keys pressed almost in the same time?

I'm trying to resolve a problem with the search bar. It works but the problem is that if I press two keys almost at the same time, the app will only search the words with the first key pressed.
Here are the logs:
In this one, it works when I press the P then R:
[EDT] 0:4:9,283 - p
[EDT] 0:4:9,348 - 10
[EDT] 0:4:9,660 - pr
[EDT] 0:4:9,722 - 3
The second one doesn't because I press P and R nearly at the same time:
[EDT] 0:4:35,237 - p
[EDT] 0:4:35,269 - pr
[EDT] 0:4:35,347 - 0
[EDT] 0:4:35,347 - 10
The logs here are generated to show the String searched and the result size. As you can see, the first case get results before typing the next char and the second case got all results when the two chars are typed.
The main problem is that in the second case, results from the 'p' String are shown instead of those of 'pr'.
I'm using the searchbar from the Toolbar API with addSearchCommand and an InfiniteContainer to show result data.
Could it be a problem in the order of the events from the addSearchCommand are treated ?
EDIT: Here is the client side code. Server side it's just a simple rest service call which fetch the data from the database.
public static ArrayList<Patient>getSearchedPatient(int index,int amount, String word)
{
ArrayList<Patient> listPatient = null;
Response reponse;
try {
reponse = RestManager.executeRequest(
Rest.get(server + "/patients/search")
.queryParam("index", String.valueOf(index))
.queryParam("amount", String.valueOf(amount))
.queryParam("word", word),
RequestResult.ENTITIES_LIST,
Patient.class);
listPatient = (ArrayList<Patient>)reponse.getResponseData();
Log.p(""+listPatient.size());
} catch (RestManagerException e) {
LogError("", e);
}
return listPatient;
}
private static Response executeRequest(RequestBuilder req, RequestResult type, Class objectClass) throws RestManagerException
{
Response response = null;
try {
switch (type) {
case BYTES:
response = req.getAsBytes();
break;
case JSON_MAP:
response = req.acceptJson().getAsJsonMap();
break;
case ENTITY:
response = req.acceptJson().getAsProperties(objectClass);
break;
case ENTITIES_LIST:
response = req.acceptJson().getAsPropertyList(objectClass);
break;
default:
case STRING:
response = req.getAsString();
break;
}
} catch (Exception e) {
log().error("Erreur à l'exécution de la requête", e);
response = null;
}
if(response == null)
return null;
return response;
}
So the trick here is a simple one. Don't make a request... Most users type fast enough to saturate your network connection speed so you will see completion suggestions referring to things that are no longer relevant.
This is a non-trivial implementation which I discuss in-depth in the Uber book where such a feature is implemented.
The solution is to send a request after a delay while caching responses to avoid double requests and ideally canceling request in progress when applicable. The solution in the Uber book does all 3 I'll try to cover just the basics in this mockup code. First you need a field for the timer and current request. Ideally you would also have a Map containing cached data:
private UITimer delayedRequest;
private String currentSearch;
private Map<String, String> searchCache = new HashMap<>();
Then you need to bind a listener like this:
tb.addSearchCommand(e -> {
String s = (String)e.getSource();
if(s == null) {
if(delayedRequest != null) {
delayedRequest.cancel();
delayedRequest = null;
}
return;
}
if(currentSearch != null && s.equals(currentSearch)) {
return;
}
if(delayedRequest != null) {
delayedRequest.cancel();
delayedRequest = null;
}
currenSearch = s;
delayedRequest = UITimer.timer(100, false, () -> {
doSearchCode();
});
});
I didn't include here usage of the cache which you need to check within the search method and fill up in the result code. I also didn't implement canceling requests already in progress.

angular.js:14195 Error: [$resource:badmember] Dotted member path "#" is invalid

I'm using angular.resource js
There I'm trying to access get service which has query param triggered from a form search.
If user search with only "#" in input field, it goes as query param which starts with "#" character then getting above exception
Thanks in advance.
Because of below code in angular.resource js
// Helper functions and regex to lookup a dotted path on an object
// stopping at undefined/null. The path must be composed of ASCII
// identifiers (just like $parse)
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$#][0-9a-zA-Z_$#]*)+$/;
function isValidDottedPath(path) {
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
MEMBER_NAME_REGEX.test('.' + path));
}
function lookupDottedPath(obj, path) {
if (!isValidDottedPath(path)) {
throw $resourceMinErr('badmember', 'Dotted member path "#{0}" is invalid.', path);
}
var keys = path.split('.');
for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
var key = keys[i];
obj = (obj !== null) ? obj[key] : undefined;
}
return obj;
}
If you really want ot make this as your url params. Try escape the # char as this has special meaning in $resource library
Normally it happens when we have only # as the value, where it searches for the value of string after "#" in the request body.
As it is a get method, where angular resource ignores it. Very weird behavior.
AngularJs doc: https://code.angularjs.org/1.5.11/docs/api/ngResource/service/$resource
If the parameter value is prefixed with #, then the value for that parameter will be extracted from the corresponding property on the data object (provided when calling a "non-GET" action method). For example, if the defaultParam object is {someParam: '#someProp'} then the value of someParam will be data.someProp. Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action method that does not accept a request body)
So before calling any method in resource try escape the # symbol:
paramValue = paramValue.replace(/#/gi, '\\#');
And again you can remove the scape before api call happens in request method of interceptor service for $httpProvider.
configParams = configParams.replace(/\\#/gi, '#');
Let me know if need any more help.
Thanks

Equals method for data class in Kotlin

I have the following data class
data class PuzzleBoard(val board: IntArray) {
val dimension by lazy { Math.sqrt(board.size.toDouble()).toInt() }
}
I read that data classes in Kotlin get equals()/hashcode() method for free.
I instantiated two objects.
val board1 = PuzzleBoard(intArrayOf(1,2,3,4,5,6,7,8,0))
val board2 = PuzzleBoard(intArrayOf(1,2,3,4,5,6,7,8,0))
But still, the following statements return false.
board1 == board2
board1.equals(board2)
In Kotlin data classes equality check, arrays, just like other classes, are compared using equals(...), which compares the arrays references, not the content. This behavior is described here:
So, whenever you say
arr1 == arr2
DataClass(arr1) == DataClass(arr2)
...
you get the arrays compared through equals(), i.e. referentially.
Given that,
val arr1 = intArrayOf(1, 2, 3)
val arr2 = intArrayOf(1, 2, 3)
println(arr1 == arr2) // false is expected here
println(PuzzleBoard(arr1) == PuzzleBoard(arr2)) // false too
To override this and have the arrays compared structurally, you can implement equals(...)+hashCode() in your data class using Arrays.equals(...) and Arrays.hashCode(...):
override fun equals(other: Any?): Boolean{
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as PuzzleBoard
if (!Arrays.equals(board, other.board)) return false
return true
}
override fun hashCode(): Int{
return Arrays.hashCode(board)
}
This code is what IntelliJ IDEA can automatically generate for non-data classes.
Another solution is to use List<Int> instead of IntArray. Lists are compared structurally, so that you won't need to override anything.
Kotlin implementation:
override fun equals(other: Any?): Boolean {
when (other) {
is User -> {
return this.userId == other.userId &&
this.userName == other.userName
}
else -> return false
}
}
For Data classes in Kotlin, hashcode() method will generate and return the same integer if parameters values are same for both objects.
val user = User("Alex", 1)
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)
println(user.hashCode().equals(secondUser.hashCode()))
println(user.hashCode().equals(thirdUser.hashCode()))
Running this code will return True and False as when we created secondUser object we have passed same argument as object user, so hashCode() integer generated for both of them will be same.
also if you will check this:
println(user.equals(thirdUser))
It will return false.
As per hashCode() method docs
open fun hashCode(): Int (source)
Returns a hash code value for the object. The general contract of
hashCode is:
Whenever it is invoked on the same object more than once, the hashCode
method must consistently return the same integer, provided no
information used in equals comparisons on the object is modified.
If two objects are equal according to the equals() method, then calling
the hashCode method on each of the two objects must produce the same
integer result.
For more details see this discussion here
In Kotlin, equals() behaves differently between List and Array, as you can see from code below:
val list1 = listOf(1, 2, 3)
val list2 = listOf(1, 2, 3)
val array1 = arrayOf(1, 2, 3)
val array2 = arrayOf(1, 2, 3)
//Side note: using a==b is the same as a.equals(b)
val areListsEqual = list1 == list2// true
val areArraysEqual = array1 == array2// false
List.equals() checks whether the two lists have the same size and contain the same elements in the same order.
Array.equals() simply does an instance reference check. Since we created two arrays, they point to different objects in memory, thus not considered equal.
Since Kotlin 1.1, to achieve the same behavior as with List, you can use Array.contentEquals().
Source: Array.contentEquals() docs ;
List.equals() docs
The board field in the PuzzleBoard class is an IntArray, when compiled it is turned into a primitive integer array. Individual array elements are never compared when checking the equality of primitive integer arrays. So calling equals on int array returns false as they are pointing to different objects. Eventually, this results in getting false in the equals() method, even though array elements are the same.
Byte code check
Looking at the decompiled java byte code, the Kotlin compiler generates some functions of data classes for us.
This includes,
copy() function
toString() function - takes form ClassName(var1=val1, var2=val2, ...)
hashCode() function
equals() function
Hash code is generated by adding the hash code of individual variables and multiplying by 31. The reason for multiplying is that it can be replaced with the bitwise operator and according to experimental results, 31 and other numbers like 33, 37, 39, 41, etc. gave fever clashes when multiplied.
Take a look at decompiled java byte code of the Kotlin class PuzzleBoard which reveals the secrets of data classes.
#Metadata(
mv = {1, 7, 1},
k = 1,
d1 = {"\u0000(\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u0015\n\u0002\b\u0004\n\u0002\u0010\b\n\u0002\b\u0007\n\u0002\u0010\u000b\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\b\u0086\b\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\t\u0010\r\u001a\u00020\u0003HÆ\u0003J\u0013\u0010\u000e\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u0003HÆ\u0001J\u0013\u0010\u000f\u001a\u00020\u00102\b\u0010\u0011\u001a\u0004\u0018\u00010\u0001HÖ\u0003J\t\u0010\u0012\u001a\u00020\bHÖ\u0001J\t\u0010\u0013\u001a\u00020\u0014HÖ\u0001R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006R\u001b\u0010\u0007\u001a\u00020\b8FX\u0086\u0084\u0002¢\u0006\f\n\u0004\b\u000b\u0010\f\u001a\u0004\b\t\u0010\n¨\u0006\u0015"},
d2 = {"Lcom/aureusapps/androidpagingbasics/data/PuzzleBoard;", "", "board", "", "([I)V", "getBoard", "()[I", "dimension", "", "getDimension", "()I", "dimension$delegate", "Lkotlin/Lazy;", "component1", "copy", "equals", "", "other", "hashCode", "toString", "", "androidpagingbasics_debug"}
)
public final class PuzzleBoard {
#NotNull
private final Lazy dimension$delegate;
#NotNull
private final int[] board;
public final int getDimension() {
Lazy var1 = this.dimension$delegate;
Object var3 = null;
return ((Number)var1.getValue()).intValue();
}
#NotNull
public final int[] getBoard() {
return this.board;
}
public PuzzleBoard(#NotNull int[] board) {
Intrinsics.checkNotNullParameter(board, "board");
super();
this.board = board;
this.dimension$delegate = LazyKt.lazy((Function0)(new Function0() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
return this.invoke();
}
public final int invoke() {
return (int)Math.sqrt((double)PuzzleBoard.this.getBoard().length);
}
}));
}
#NotNull
public final int[] component1() {
return this.board;
}
#NotNull
public final PuzzleBoard copy(#NotNull int[] board) {
Intrinsics.checkNotNullParameter(board, "board");
return new PuzzleBoard(board);
}
// $FF: synthetic method
public static PuzzleBoard copy$default(PuzzleBoard var0, int[] var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var1 = var0.board;
}
return var0.copy(var1);
}
#NotNull
public String toString() {
return "PuzzleBoard(board=" + Arrays.toString(this.board) + ")";
}
public int hashCode() {
int[] var10000 = this.board;
return var10000 != null ? Arrays.hashCode(var10000) : 0;
}
public boolean equals(#Nullable Object var1) {
if (this != var1) {
if (var1 instanceof PuzzleBoard) {
PuzzleBoard var2 = (PuzzleBoard)var1;
if (Intrinsics.areEqual(this.board, var2.board)) {
return true;
}
}
return false;
} else {
return true;
}
}
}

Kotlin: For-loop must have an iterator method - is this a bug?

I have the following code:
public fun findSomeLikeThis(): ArrayList<T>? {
val result = Db4o.objectContainer()!!.queryByExample<T>(this as T) as Collection<T>
if (result == null) return null
return ArrayList(result)
}
If I call this like:
var list : ArrayList<Person>? = p1.findSomeLikeThis()
for (p2 in list) {
p2.delete()
p2.commit()
}
It would give me the error:
For-loop range must have an 'iterator()' method
Am I missing something here?
Your ArrayList is of nullable type. So, you have to resolve this. There are several options:
for (p2 in list.orEmpty()) { ... }
or
list?.let {
for (p2 in it) {
}
}
or you can just return an empty list
public fun findSomeLikeThis(): List<T> //Do you need mutable ArrayList here?
= (Db4o.objectContainer()!!.queryByExample<T>(this as T) as Collection<T>)?.toList().orEmpty()
try
for(p2 in 0 until list.count()) {
...
...
}
I also face this problem when I loop on some thing it is not an array.
Example
fun maximum(prices: Array<Int>){
val sortedPrices = prices.sort()
for(price in sortedPrices){ // it will display for-loop range must have iterator here (because `prices.sort` don't return Unit not Array)
}
}
This is different case to this question but hope it help
This can also happen in Android when you read from shared preferences and are getting a (potentially) nullable iterable object back like StringSet. Even when you provide a default, the compiler is not able to determine that the returned value will never actually be null. The only way I've found around this is by asserting that the returned expression is not null using !! operator, like this:
val prefs = PreferenceManager.getDefaultSharedPreferences(appContext)
val searches = prefs.getStringSet("saved_searches", setOf())!!
for (search in searches){
...
}

Does MyBatis not allow more than 1 argument in SelectProvider?

I have a mapper method like this :
#InsertProvider(class=com.something.class, method="doSomething")
public void insertSomething(Set<Integer> set, int guideId);
and in the something class, I have a method :
public String doSomething(Set<Integer> set, int guideId){
// do something and returna a query
}
It gives me an error :
Error creating SqlSource for SqlProvider. Method 'doSomething' not
found in SqlProvider 'com.something.class'
When I debugged the issue.. I found that in the constructor of ProviderSqlResource, it throws this exception if the no. of arguments are 2 or more. I can't think of any reason why they would do that. What's the workaround ?
Here is the method :
public ProviderSqlSource(Configuration config, Object provider) {
String providerMethodName = null;
try {
this.sqlSourceParser = new SqlSourceBuilder(config);
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
for (Method m : this.providerType.getMethods()) {
if (providerMethodName.equals(m.getName())) {
if (m.getParameterTypes().length < 2
&& m.getReturnType() == String.class) {
this.providerMethod = m;
this.providerTakesParameterObject = m.getParameterTypes().length == 1;
}
}
}
} catch (Exception e) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Cause: " + e, e);
}
if (this.providerMethod == null) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
}
}
It turns out that we can pass any number of arguments in methods annotated with SelectProvider (or any other provider). But the method actually providing the query (doSomething, in my case) will actually receive a single argument i.e. a map wrapper around all the arguments. For example, if the arguments were as in the questions above (a set and an integer), we can access them from the map (called parametersMap) as follows :
Set<Integer> nums = (Set<Integer>) parametersMap.get("0");
int groupId = (Integer) parametersMap.get("1");
The first parameter is keyed with "0" and the second with "1" and so on.
IMHO, the arguments should have been keyed with their names so that we could do something like :
parametersMap.get("set");
parametersMap.get("guideId")
It would probably have been more clean. But that's how its implemented.
For providing multiple arguments use #Param tag in the arguments.

Resources