8000 added check for UI Thread by rockytriton · Pull Request #605 · androidannotations/androidannotations · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Feb 26, 2023. It is now read-only.

Conversation

@rockytriton
Copy link
Contributor

What do you think about this for issue #604?

I don't know how you feel about using JExpr.direct(). I was trying to do it without it but couldn't figure it out immediately. I can look into it more if you don't like using direct().

@pyricau
Copy link
Contributor
pyricau commented May 27, 2013

Yes, direct() is bad, we should avoid using it. Other then that it's a pretty good idea.

@rockytriton
Copy link
Contributor Author

Ok, I'll find a better way than using direct()

@rockytriton
Copy link
Contributor Author

I think I got something working well. I'm not sure what (if any) test cases need to be updated for this though.

@adrianroos
Copy link
Contributor

This change may break existing code in pretty bad ways if the code relies on the current behaviour of always posting to the handler. Maybe you could introduce a flag to allow reusing like e.g.

@UiThread(reuse = true)
void runOnMainThread() {
}

@JoanZapata
Copy link
Contributor

@adrianroos If it was the other way around, I would agree immediately: a synchronous call becoming asynchronous can really break things.

But can you think of a usecase in which a code relies on the fact that the call is asynchronous in the UI thread ? I can't. I could if it was on a background thread (download things or doing any IO stuff) but on the main thread, I can't. Please tell me if I'm wrong.

@adrianroos
Copy link
Contributor

@JoanZapata posting to a Handler is not just asynchronous, if you do it on the same thread it guarantees that the execution will only happen once your current call stack has returned to the looper.

For example, suppose we have some kind of listener registry:

Set<Listener> listeners;

void register(Listener l) {
    listeners.add(l);
}

void unregister(Listener l) {
    listeners.remove(l);
}

void notifyListeners() {
    for (Listener l : listeners) {
        l.call();
    }
}

If you have a listener with this implementation:

@UiThread
void call() {
    registry.unregister(this);
}

Then currently this will work, because the listener only unregisters itself after the iteration over all listeners has finnished. After the change, there would be a ConcurrentModificationException.

Or even simpler:

@UIThread
void doSomeWorkWithoutBlockingUI() {
    // ... 
    if(!done) {
       doSomeWorkWithoutBlockingUI()
    }
}

@JoanZapata
Copy link
Contributor

Ok you're right, thanks.
(And I agree about the reuse attribute.)

@DayS
Copy link
Contributor
DayS commented May 28, 2013

I agree too. But I'm not sure for the field's name.
I propose a little brainstorming and my proposition is directCall :)

Any other idea ?

@rockytriton
Copy link
Contributor Author

How would this variable be used exactly and what would be the default if it's not there? I would think the default would be to check if it's in the UI thread already and skip the handler. If that's the case, maybe the variable would be alwaysUseHandler, or noCheck or something like that.

@DayS
Copy link
Contributor
DayS commented May 28, 2013

Nop. As we don't want to break the API the default behavior is to always put the runnable into the handler without any check.

@rockytriton
Copy link
Contributor Author

Understood, I agree. The only thing about directCall is I think it may be misleading, at least for people who don't read javadocs. Maybe something like uiThreadCheck or something to that effect. I guess any name really works as long as people read the docs, which of course they should.

@rockytriton
Copy link
Contributor Author

I added a fix where it would fail to compile if the @UiThread method had parameters. I haven't done anything about the new directCall property though, until a final name is decided.

@rockytriton
Copy link
Contributor Author

Ok I had some time today so I went ahead and added useDirect, we can always change it if we don't like the name.

@JoanZapata
Copy link
Contributor

@rockytriton I really think you should wait when a discussion is pending on a issue, except if you have an immediate need. :)

@DayS I liked reuse because it's self-explanatory when you call it from a @UIThread annotated method:

@UIThread
public void method1(){
  method2();
}

@UIThread(reuse = true)
public void method2(){ }

I agree it can be misleading if it's called from another UiThread method which is not annotated, but I think it's more appropriate than useDirect, because it becomes obvious that if we're not on the UI thread, there's nothing to reuse.

Another way that could make the code easier to read would be to use an enum:

@UIThread(REQUIRES_NEW) // default
@UIThread(NESTED)
or 
@UIThread(PROPAGATION_REQUIRES_NEW) // default
@UIThread(PROPAGATION_NESTED)
or
@UIThread(propagation = REQUIRES_NEW) // default
@UIThread(propagation = NESTED)

REQUIRES_NEW and NESTED come from the Spring world, it's not the same thing but the term "propagation" seems appropriate to me. I'm not so sure about NESTED though.

@DayS
Copy link
Contributor
DayS commented May 29, 2013

👍 for the enum. It also prepares the annotation for some future improvement

And yes, don't make changes or pull request before a solution has been validated. If you're solution doesn't fit the needs you'll have to make another commit to revert you code...

@JoanZapata
Copy link
Contributor

I think I like this one:

@UIThread(propagation = REQUIRES_NEW) // default
@UIThread(propagation = REUSE)

Any thought ? @DayS @pyricau @adrianroos @mathieuboniface

@DayS
Copy link
Contributor
DayS commented May 29, 2013

I think REQUIRES_NEW is quite odd... What about USE_HANDLER or something like that ?

@DayS
Copy link
Contributor
DayS commented Jun 6, 2013

Just to make a decision and go forward on this. I propose to use a UIThreadPropagation enum with two values :

  • ENQUEUE (default and current behavior)
  • REUSE

I think you can start to code :)

@JoanZapata
Copy link
Contributor

👍

@rockytriton
Copy link
Contributor Author

Hey, sorry I just figured I would throw some ideas out there in code while I had the chance before I went on vacation. I'm still new to github so I don't know the normal protocol or if some ways are better than others.

So just to be clear, should I go ahead and make these changes in this code fork or is someone already working on it on another?

@DayS
Copy link
Contributor
DayS commented Jun 9, 2013

You can make a new commit on your PR with this code for the @UIThread annotation :

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface UiThread {
    long delay() default 0;

    Propagation propagation() default Propagation.ENQUEUE;

    public enum Propagation {
        ENQUEUE, REUSE
    }
}

And modify your processor to handle this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you shouldn't clone the body of the method and you should use the method callSuperMethod from APTCodeModelHelper 8000 (here) instead.
Because we are not sure that the annotation will be processed if the content of the method changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I didn't know that method existed. I will replace it.

@rockytriton
Copy link
Contributor Author

@yDelouis I changed it to no longer use the clone. One question though, should that class APTCodeModelHelper be a static utility class instead? I don't feel it makes sense to instantiate it.

@yDelouis
Copy link
Contributor

I don't know why APTCodeModel methods are not static. Maybe it is to be consistent with other helpers.

@DayS
Copy link
Contributor
DayS commented Jun 18, 2013

I'll review your new changes after the droidcon :)
About APTCodeModel methods, they could be static. But we should think about that after PR #619

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't codeModel.ref(<a class>) because it messed up sometimes.
Have a look to EBeansHolder class instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EBeansHolder.refClass(Class) just calls codeModel.ref(). Should I be using EBeansHolder.refClass(String) instead and use the String name of the class? Do you know what situations that the ref() method doesn't work in? It works in my application at least. Just want to make sure I am using it correctly.

@DayS
Copy link
Contributor
DayS commented Jun 18, 2013

you should also add some unit tests in ThreadActivity

DayS added a commit that referenced this pull request Jul 12, 2013
@DayS DayS merged commit cfddf8d into androidannotations:develop Jul 12, 2013
@DayS
Copy link
Contributor
DayS commented Jul 12, 2013

Thanks for the feature 👍

@rockytriton rockytriton deleted the 604_check_currentThread_in_UiThread branch July 12, 2013 20:01
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

0