Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Taffer
Oct 15, 2010


Don't use Async tasks. Use literally any other form of threading, preferably Rx or something robust and not-leaky.

Adbot
ADBOT LOVES YOU

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

Without looking at or caring about any of your code, I'd like to point out that you can start a new thread from an Activity, it doesn't actually need to live in a service unless you'd like it to persist when the Activity goes away for some reason.

Create a static class that implements Runnable. Have it's run method check for some member value like isRunning (give it the volatile keyword) and return when false. Have a method that sets isRunning to false, call it in onPause. Pass a Handler reference to your constructor, and pass messages to the handler when you need to update the UI. Create an instance and start a new thread with it in onResume (keep a reference to it so that you can end it in onPause)

Your Handler will update on the UI thread when it receives a message, so update your text fields with whatever.

Refactoring this to be fault tolerant is left as an exercise to the reader.

So I've done this and as it turns out sending messages to the UI every 1/100th of a second will crash the UI thread no matter what I do. The listening thread is running fine and it processes all of the data thrown at it just how i'd like. I've been trying to figure out a way to update the UI every 1 second (since this is just a monitoring app to make sure that a vehicle is sending the correct messages and we're not testing for timing)

I tried using a while loop when ToggleButton.isChecked = true to call the method in the MainActivity that fetches mListenerService.getParsedMessage and updates the TextView but that obviously locked the UI thread due to the while loop constantly looping while the button isChecked, then I tried putting the while loop in its own thread so that I can sleep it for a second but surprise surprise that didnt work either since now I can't call a method that works on the UI thread from a different thread. I've been messing with Handlers for a little bit but it still crashes the UI thread

Java code:

mToggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        while(isChecked) {
                            readFromService();
                        }
                    }
                }, 1000);
            }
        });

How should I got about updating the UI without crashing it?

Instead of trying to call the method that works on the UI thread, should set a global String to the value of the mListenerService.getParsedMessage and update the objects from that? I feel like I'm on the cusp of fully understanding this after reading about all this stuff last night when I got home

Edit: better way to phrase it: how can I call a method that works on the UI less often than however quickly it's currently being called

FAT32 SHAMER fucked around with this message at 23:12 on Jul 19, 2017

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...
Posting to the UI thread every 10ms won't cause a crash. You're doing something else wrong. Post the stack trace and relevant code along with line numbers.

In case you're trying to update the UI from the background thread, don't. That's what the Handler is for. You post a message to the handler, and the handler's method that you override (you read the docs right?) will run on the UI thread.

Edit: it's possible that your device is some sort of uniquely bad car console turd that is programmed to crash if you update too frequently but that would be someone going out of their way to make something bad. I wouldn't put it past an OEM but I would be shocked.

Volmarias fucked around with this message at 23:46 on Jul 19, 2017

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

Posting to the UI thread every 10ms won't cause a crash. You're doing something else wrong. Post the stack trace and relevant code along with line numbers.

In case you're trying to update the UI from the background thread, don't. That's what the Handler is for. You post a message to the handler, and the handler's method that you override (you read the docs right?) will run on the UI thread.

There's no stack trace, the UI simply stops responding then five seconds later I get the "your app is unresponsive" dialog box

MainActivity
Java code:
public class MainActivity extends Activity {

    AlertAssignments mAlertAssignments;



    Button startListeningButton;

    boolean started;
    int counter;
    boolean mBound = false;
    Context context;
    ListenerService mListenerService;

    TextView mTextView;
    TextView mBlinkView;
    ImageView mImageView;
    ToggleButton mToggleButton;


    private StartListening _StartListeningTask;
    String messageFromService = "";




    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //start listener service
        Intent listenerServiceIntent = new Intent(MainActivity.this, ListenerService.class);
        this.bindService(listenerServiceIntent, mConnection, Context.BIND_AUTO_CREATE);

       
        mImageView = (ImageView) findViewById(R.id.image_view);
        mTextView = (TextView) findViewById(R.id.alert_text);
        mBlinkView = (TextView) findViewById(R.id.blinking_text);
        Animation mAnimation = new AlphaAnimation(0.0f, 1.0f);
        mAnimation.setDuration(50);
        mAnimation.setStartOffset(20);
        mAnimation.setRepeatCount(Animation.INFINITE);
        mAnimation.setRepeatMode(Animation.REVERSE);
        mBlinkView.startAnimation(mAnimation); //animation value
        mAlertAssignments = new AlertAssignments();


        mToggleButton = (ToggleButton) findViewById(R.id.toggle_button);
        mToggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
		while(isChecked) {
			readFromService(); // <---THIS is where I believe it's hanging up: the while loop is causing the UI/Main thread to lock while it continuously checks that the ToggleButton is pressed
            }
        });



    }
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
            mListenerService = binder.getService();
            mBound = true;
            if(mBound) {
                Log.e("UDP", "Service has been bound successfully");
            }
            else {
                Log.e("UDP", "Service has not been bound");
            }

            readFromService();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBound = false;
        }
    };

    @Override
    protected void onStart() {
        super.onStart();

    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
        //unbind from service
        if(mBound) {
            this.unbindService(mConnection);
            mBound = false;
        }

    }

    private void readFromService() {
        try {
            Log.e("Main UDP", "MainActivity received message from ListenerService of " + mListenerService.getParsedMessage());
            Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());

            mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);

            if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
                mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
            } else {
                mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
            }


        } catch(NumberFormatException e) {
            e.printStackTrace();
        } catch(NullPointerException e) {
            e.printStackTrace();
        }

    }


}


I commented where I believe the issue is coming from.

ListenerService.
Java code:
public class ListenerService extends Service{
    public String the_alarm_S;
    public String parsedMessage = "";
    private final IBinder mBinder = new LocalBinder();


    public class LocalBinder extends Binder {
        ListenerService getService() {
            return ListenerService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    DatagramSocket socket;

    Thread UDPBroadcastThread;

    void startListenForUDPBroadcast() {
        UDPBroadcastThread = new Thread(new Runnable() {
            public void run() {
                try {
                    while (shouldRestartSocketListen) {
                        try {
                            socket = new DatagramSocket(12001);
                            socket.setReuseAddress(true);
                            String message = "";

                            byte[] recvBuf = new byte[1024];
                            DatagramPacket packet = new DatagramPacket(recvBuf, 1024);
                            Log.e("Listener UDP", "Waiting for UDP broadcast");
                            try {
                                socket.receive(packet);
                                Log.e("Listener UDP", "Received Packet");
                            } catch (IOException e) {
                                e.printStackTrace();

                            }
                            message = new String(packet.getData());

                            Log.e("Listener UDP", "Got UDB broadcast message: " + message);

                            setParsedMessage(message);

                            if(socket != null) {
                                socket.close();
                            }
                        } catch (SocketException e) {
                            e.printStackTrace();
                        }
                    }
                    //if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
                } catch (Exception e) {
                    Log.i("Listener UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
                }
            }
        });
        UDPBroadcastThread.start();
    }

    private Boolean shouldRestartSocketListen = true;

    private void setParsedMessage(String messageContents) {
        the_alarm_S = messageContents;
        String parseMessage[] = the_alarm_S.split("!!!");
        Log.e("Listener UDP", "Parsed message with value " + parseMessage[1]);
        parsedMessage = parseMessage[1];

    }
    public String getParsedMessage() {
        return parsedMessage;
    }

    private void stopListen() {
        shouldRestartSocketListen = false;
        if(socket != null) {
            socket.close();
        }
    }

    @Override
    public void onCreate() {
        startListenForUDPBroadcast();
    }

    @Override
    public void onDestroy() {
        stopListen();
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        shouldRestartSocketListen = true;
        startListenForUDPBroadcast();
        Log.i("UDP", "Service started");
        return START_STICKY;
    }
}

ListenerService receives every UDP packet and prints them in logcat. Maybe it's the way i implemented my readFromService() method? instead of retrieving the value parsed from the message via mListenerService.getParsedMessage() should I be passing the string via a bundle or a handler or something similar?

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...
You have a while loop in your listener. Why? :negative:

You've diagnosed the issue. The problem is that you need to return from the listener call so that the rest of the work on the UI thread, like the drawing, can happen.

Using a service overcomplicates this for no tangible gain. Get rid of it, just use a background thread.

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

You want something like this
Java code:
    static class MagicCarBrain implements Runnable {

        volatile boolean isRunning = false;
        final Handler uiThreadHandler;

        MagicCarBrain(Handler uiThreadHandler) {
            this.uiThreadHandler = uiThreadHandler;
        }

        @Override
        public void run() {
            while (isRunning) {
                String data = readFromService();
                uiThreadHandler.post(() -> myTextView.setText(data));
                Thread.sleep(MESSAGE_DELAY);
            }
        }
    }
So you create that, giving it a handler for your main thread, which you can get with new Handler(Looper.getMainLooper()). (A Looper maintains a message queue for a thread, a Handler handles delivering messages to it and running them.) When you run this on another thread you create (new Thread(myMagicCarBrain) and then start() it), it keeps running that code, posting messages to the UI thread

How you want to throttle it honestly depends on what the hell you're doing. I'm guessing actually sleeping the thread is bad because you need to be constantly watching for messages coming in, never missing any. I don't know if you'll actually overflow the message queue doing this (?), but it's probably a safer option to add some kind of throttling policy just in case you get a deluge of UDP packets for some reason. The message pool only has 50 message objects by default too, I think, and the system uses them too, which might be why you're crashing

But what do you do if you're producing messages faster than you consume them? How do you prioritise them, how long does an OH poo poo message stay around for, are there any others that would override it? Do you drop the original message or show it later?

Honestly I'd be tempted to get a basic polling thing going on in your activity like this
Java code:
volatile boolean isRunning = false;
Handler uiThreadHandler = new Handler(Looper.getMainLooper());
CarInquirer updater = new CarInquirer();

private class MagicCarBrain implements Runnable {

    volatile CarState currentCarState;

    @Override
    public void run() {
        while (isRunning) {
            Object data = readFromService();
            // update currentCarState
        }
    }

    CarState getLatestData() {
        // maybe do some logic here, since you know stuff has been seen now
        return currentCarState;
    }
}

private class CarInquirer implements Runnable {

    @Override
    public void run() {
        updateUi(magicCarBrain.getLatestData());
        if (isRunning) {
            ((Handler)uiThreadHandler).postDelayed(this, UPDATE_DELAY);
        }
    }
}

// starting
if (!isRunning) {
    isRunning = true;
    new Thread(new MagicCarBrain()).start();
    updater.run();
}

// stopping
isRunning = false; // both runnables stop looping, which ends the thread and the handler re-post
It's quick and hacky but I'm tired, basically you have your service on a thread reading and storing data at its own speed. The UI thread keeps posting a runnable to itself (through a handler for the main thread) that makes it poll the service for data every so often. When you need to stop everything, you can turn off the running flag which ends the looping behaviour for both.

I mean it's buggy (stop and start quick enough and you'll add another runnable to the polling queue, the first won't have seen the flag going false) and you'll have to worry about atomicity if you start doing complicated stuff with the data you're providing, but it's all fixable if you care enough. Just trying to give an example here

This might help too

I typed bolleazn while writing this, I think it's my best typo yet

kitten smoothie
Dec 29, 2001

Volmarias posted:

You have a while loop in your listener. Why? :negative:

Even after you get that off the main thread, why are you closing and re-opening the socket every time you get a message? Just leave it open and keep reading infinitely. It'll block the listener thread while waiting for data to come in, so when you're done close the socket from outside that thread. Ignore the resulting IOException, because you'll get one from closing the socket out from under a blocking receive call.

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

You have a while loop in your listener. Why? :negative:
i am not a clever man :saddowns:

Volmarias posted:

You've diagnosed the issue. The problem is that you need to return from the listener call so that the rest of the work on the UI thread, like the drawing, can happen.

Using a service overcomplicates this for no tangible gain. Get rid of it, just use a background thread.

I'm gonna mess around with this idea and do a runnable class since making it a service seems to also make it a pain in the dick

kitten smoothie posted:

Even after you get that off the main thread, why are you closing and re-opening the socket every time you get a message? Just leave it open and keep reading infinitely. It'll block the listener thread while waiting for data to come in, so when you're done close the socket from outside that thread. Ignore the resulting IOException, because you'll get one from closing the socket out from under a blocking receive call.
Where is it opening and closing the socket? I thought once the thread was running, as long as the socket != null it would stay open, unless calling the public method from the service opens and closes it every time...

baka kaba posted:

You want something like this
Java code:
    static class MagicCarBrain implements Runnable {

        volatile boolean isRunning = false;
        final Handler uiThreadHandler;

        MagicCarBrain(Handler uiThreadHandler) {
            this.uiThreadHandler = uiThreadHandler;
        }

        @Override
        public void run() {
            while (isRunning) {
                String data = readFromService();
                uiThreadHandler.post(() -> myTextView.setText(data));
                Thread.sleep(MESSAGE_DELAY);
            }
        }
    }
So you create that, giving it a handler for your main thread, which you can get with new Handler(Looper.getMainLooper()). (A Looper maintains a message queue for a thread, a Handler handles delivering messages to it and running them.) When you run this on another thread you create (new Thread(myMagicCarBrain) and then start() it), it keeps running that code, posting messages to the UI thread

How you want to throttle it honestly depends on what the hell you're doing. I'm guessing actually sleeping the thread is bad because you need to be constantly watching for messages coming in, never missing any. I don't know if you'll actually overflow the message queue doing this (?), but it's probably a safer option to add some kind of throttling policy just in case you get a deluge of UDP packets for some reason. The message pool only has 50 message objects by default too, I think, and the system uses them too, which might be why you're crashing

But what do you do if you're producing messages faster than you consume them? How do you prioritise them, how long does an OH poo poo message stay around for, are there any others that would override it? Do you drop the original message or show it later?

Honestly I'd be tempted to get a basic polling thing going on in your activity like this
Java code:
volatile boolean isRunning = false;
Handler uiThreadHandler = new Handler(Looper.getMainLooper());
CarInquirer updater = new CarInquirer();

private class MagicCarBrain implements Runnable {

    volatile CarState currentCarState;

    @Override
    public void run() {
        while (isRunning) {
            Object data = readFromService();
            // update currentCarState
        }
    }

    CarState getLatestData() {
        // maybe do some logic here, since you know stuff has been seen now
        return currentCarState;
    }
}

private class CarInquirer implements Runnable {

    @Override
    public void run() {
        updateUi(magicCarBrain.getLatestData());
        if (isRunning) {
            ((Handler)uiThreadHandler).postDelayed(this, UPDATE_DELAY);
        }
    }
}

// starting
if (!isRunning) {
    isRunning = true;
    new Thread(new MagicCarBrain()).start();
    updater.run();
}

// stopping
isRunning = false; // both runnables stop looping, which ends the thread and the handler re-post
It's quick and hacky but I'm tired, basically you have your service on a thread reading and storing data at its own speed. The UI thread keeps posting a runnable to itself (through a handler for the main thread) that makes it poll the service for data every so often. When you need to stop everything, you can turn off the running flag which ends the looping behaviour for both.

I mean it's buggy (stop and start quick enough and you'll add another runnable to the polling queue, the first won't have seen the flag going false) and you'll have to worry about atomicity if you start doing complicated stuff with the data you're providing, but it's all fixable if you care enough. Just trying to give an example here

This might help too

I typed bolleazn while writing this, I think it's my best typo yet
I'll also play around with this

FAT32 SHAMER
Aug 16, 2012



So idk how terrible this is, but I figured out a stupidly simple way to redraw the textviews and imageview all the time and it works extremely well. I simply used Animation's applyTransformation. I've had the app running for about an hour with no issue, so fingers crossed it is good to go.

edit: here's the code

Java code:
startListeningButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSoundAlertService.playIt(Integer.valueOf(mListenerService.getParsedMessage()));
                final Animation objectAnimation = new Animation() {
                    @Override
                    protected void applyTransformation(float interpolatedTime, Transformation t) {
                        super.applyTransformation(interpolatedTime, t);

                        try {
                            Log.e("Main UDP", "MainActivity received message from ListenerService of " + mListenerService.getParsedMessage());
                            Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());

                            mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);


                            if(parsedMessage >= 10 && parsedMessage <= 19) {
                                mBlinkView.setText("");
                                mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
                            } else {
                                mTextView.setText("");
                                mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
                            }

                        } catch(NumberFormatException e) {
                            e.printStackTrace();
                        } catch(NullPointerException e) {
                            e.printStackTrace();
                        }

                    }
                };

                objectAnimation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        Animation blinkTextAnimation = new AlphaAnimation(0.0f, 1.0f);
                        blinkTextAnimation.setDuration(50);
                        blinkTextAnimation.setStartOffset(20);
                        blinkTextAnimation.setRepeatCount(Animation.INFINITE);
                        blinkTextAnimation.setRepeatMode(Animation.REVERSE);
                        mBlinkView.startAnimation(blinkTextAnimation);
                    }
                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                    @Override
                    public void onAnimationEnd(Animation animation) {

                    }

                });
                objectAnimation.setRepeatCount(Animation.INFINITE);
                objectAnimation.setDuration(1000);

                mImageView.startAnimation(objectAnimation);
                mTextView.startAnimation(objectAnimation);
                mBlinkView.startAnimation(objectAnimation);

            }
        });
it just works

FAT32 SHAMER fucked around with this message at 18:33 on Jul 20, 2017

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...
:psyduck:

It's not like you need to apply some sort of trick to make UI things update, just post things on the UI thread. You're creating some sort of bizarre horror, and I hope you at least comment this callback with "// I don't actually understand how to update the UI, this callback isn't necessary"


If you're really having trouble putting things on the UI thread, use Activity.runOnUiThread with a Runnable of things to do in the UI thread (not networking, don't make it a loop)

Like, would you like to just cheat and have us write this for you?

Volmarias fucked around with this message at 20:41 on Jul 20, 2017

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

:psyduck:

It's not like you need to apply some sort of trick to make UI things update, just post things on the UI thread. You're creating some sort of bizarre horror, and I hope you at least comment this callback with "// I don't actually understand how to update the UI, this callback isn't necessary"


If you're really having trouble putting things on the UI thread, use Activity.runOnUiThread with a Runnable of things to do in the UI thread (not networking, don't make it a loop)

Like, would you like to just cheat and have us write this for you?

nonono it's all good, I realized that while this works it's a terrible way to do it and I implemented the handler as was shown by baka. I have much to learn :negative:

kitten smoothie
Dec 29, 2001

FAT32 SHAMER posted:

Where is it opening and closing the socket? I thought once the thread was running, as long as the socket != null it would stay open, unless calling the public method from the service opens and closes it every time...

Your listen thread is a while loop.

At the top of the loop it opens a socket and blocks until a message comes in. When a message comes in, it dispatches the data received, then you close the socket. Back to the top and repeat. You're closing and reopening for every message.

The socket != null check in that loop is kind of useless where it is, because if the socket were null it would've NPE'd earlier on when you called receive on it.

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Honestly after the initial :eyepop: about doing it through animations, I realised you're basically updating views on a tick and one of them is animating a fade anyway. So you're really polling for data when the animation has faded out, so you can switch the content while it's invisible.

From a purely informational UI standpoint I don't think that's too bad? It's basically a self-updating display that's pulling data when it's convenient, based on the animation state. It'd be more awkward to coordinate that if you're pushing data instead

FAT32 SHAMER
Aug 16, 2012



kitten smoothie posted:

Your listen thread is a while loop.

At the top of the loop it opens a socket and blocks until a message comes in. When a message comes in, it dispatches the data received, then you close the socket. Back to the top and repeat. You're closing and reopening for every message.

The socket != null check in that loop is kind of useless where it is, because if the socket were null it would've NPE'd earlier on when you called receive on it.
:doh:
I'm not sure why i didnt recognize this but that explains a few questions i had in the back of my mind

baka kaba posted:

Honestly after the initial :eyepop: about doing it through animations, I realised you're basically updating views on a tick and one of them is animating a fade anyway. So you're really polling for data when the animation has faded out, so you can switch the content while it's invisible.

From a purely informational UI standpoint I don't think that's too bad? It's basically a self-updating display that's pulling data when it's convenient, based on the animation state. It'd be more awkward to coordinate that if you're pushing data instead

This was my initial thinking as well. I'm not sure how terrible it is either but i'd rather do something correctly and learn it so I know for the future instead of do a potentially insane hack to make it work

Do you guys have any resources with projects I can work through in my spare time? I got about halfway through the big nerd ranch android book i bought about two years ago and while it was insightful, I wasnt sure how useful some of the stuff would be due to a lot of deprecated things (like apparently SoundPool)

kitten smoothie
Dec 29, 2001

FAT32 SHAMER posted:

Do you guys have any resources with projects I can work through in my spare time? I got about halfway through the big nerd ranch android book i bought about two years ago and while it was insightful, I wasnt sure how useful some of the stuff would be due to a lot of deprecated things (like apparently SoundPool)

The BNR book is still probably one of the best resources for "I can write some Java but need to understand the Android framework." They put out a new edition in January so I'd imagine the outdated stuff is gone now (for instance it looks like the only thing deprecated about SoundPool is the constructor method, not the framework class itself).

I'd also start looking through some of Dan Lew's RxJava posts (start from the very beginning) so you can avoid ever touching an AsyncTask ever again.

FAT32 SHAMER
Aug 16, 2012



kitten smoothie posted:

The BNR book is still probably one of the best resources for "I can write some Java but need to understand the Android framework." They put out a new edition in January so I'd imagine the outdated stuff is gone now (for instance it looks like the only thing deprecated about SoundPool is the constructor method, not the framework class itself).

I'd also start looking through some of Dan Lew's RxJava posts (start from the very beginning) so you can avoid ever touching an AsyncTask ever again.

Nice! Thanks for this, and thanks for all the help and insight you guys have given me in the last two weeks. Pray for me because i'm the only person on my team familiar with Java and Android (they all did C# in school and I did Java datastructures/android apps and iOS apps) :pray:

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...
Java is pretty similar to C# (I think of C# as Java With Hindsight), so transitioning isn't terrible.

The official Android guides and tutorials aren't terrible anymore.

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

Java is pretty similar to C# (I think of C# as Java With Hindsight), so transitioning isn't terrible.

The official Android guides and tutorials aren't terrible anymore.

That's what I told them. I guess they'll learn it if they absolutely have to, but since we work in automotive their big focus is C/C++ and C#

Taffer
Oct 15, 2010


FAT32 SHAMER posted:

Nice! Thanks for this, and thanks for all the help and insight you guys have given me in the last two weeks. Pray for me because i'm the only person on my team familiar with Java and Android (they all did C# in school and I did Java datastructures/android apps and iOS apps) :pray:

Definitely frequent places like this too. You learn a lot by osmosis if you stay in communities of developers. It's :reddit: but r/androiddev is extremely good as well. I've learned a ton from there.

kitten smoothie
Dec 29, 2001

Also community conferences like Droidcon events, or at least watching the videos after the fact.

FAT32 SHAMER
Aug 16, 2012



Ok guys I have a super dumb question (again). The last time I built a [complicated multi-activity] app, Fragments didn't exist and everything was straight forward. I've built a NavDrawer based on this tutorial that i found that isn't garbage and it works great A++. Now my question is, do i build my UI in fragment_layouts for each fragment or do I have to do some kind of interface so that the fragment "becomes", for lack of a better word, an activity.

For instance, here is the drawer that I've built.


(i'm fixing the padding at the top pls ignore)

If I click on "Completed", it crashes if it extends AppCompatActivity but it is fine when it extends Fragment, which I guess makes sense. The tutorial has my MainActivity extending AppCompatActivity, however based on the android developer I should be extending FragmentActivity. Should I be extending both? Should I do some kind of fuckery to make the navdrawer launch activities instead of fragments?

Thanks :)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

AppCompatActivity inherits from FragmentActivity - use the AppCompat version because it basically gives you a lot of the modern look and other stuff in older versions of Android.

If you want to launch an Activity you do it the usual way
Java code:
Intent intent = new Intent(context, NextActivity.class);
getActivity().startActivity(myIntent);
where if you're in a fragment you'll need to get its activity (you can use that for the context too). You can add an Extra to the intent if you want to say pass a fragment name (if your activity is meant to be reused and host different fragments)


The way you're meant to use Activities and Fragments is you have an Activity, and you inflate a layout in it as usual. That layout can include Fragments in the XML, or you can include a container like a FrameLayout with an id you can find, and stick the fragment in there programmatically. If you do it that way you can swap the fragments in and out without having to go to a new Activity

So a fragment is like a component you can add to the activity as and when you need it, and maybe reuse it in multiple places. It can have its own layout (so it provides part of the UI) or it can just be a 'headless' fragment that has some useful functionality but doesn't appear visually. What you'll generally do is have an Activity with some main UI (like the toolbar at the top), define your fragments separately, and then add them into the Activity to complete your UI. So you can have a fragment that mostly (or completely) fills the whole Activity's view, yeah!

Read this basically
http://guides.codepath.com/android/Creating-and-Using-Fragments
Also some people hate fragments and say you should never use them, yolo

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

AppCompatActivity inherits from FragmentActivity - use the AppCompat version because it basically gives you a lot of the modern look and other stuff in older versions of Android.

If you want to launch an Activity you do it the usual way
Java code:
Intent intent = new Intent(context, NextActivity.class);
getActivity().startActivity(myIntent);
where if you're in a fragment you'll need to get its activity (you can use that for the context too). You can add an Extra to the intent if you want to say pass a fragment name (if your activity is meant to be reused and host different fragments)


The way you're meant to use Activities and Fragments is you have an Activity, and you inflate a layout in it as usual. That layout can include Fragments in the XML, or you can include a container like a FrameLayout with an id you can find, and stick the fragment in there programmatically. If you do it that way you can swap the fragments in and out without having to go to a new Activity

So a fragment is like a component you can add to the activity as and when you need it, and maybe reuse it in multiple places. It can have its own layout (so it provides part of the UI) or it can just be a 'headless' fragment that has some useful functionality but doesn't appear visually. What you'll generally do is have an Activity with some main UI (like the toolbar at the top), define your fragments separately, and then add them into the Activity to complete your UI. So you can have a fragment that mostly (or completely) fills the whole Activity's view, yeah!

Read this basically
http://guides.codepath.com/android/Creating-and-Using-Fragments
Also some people hate fragments and say you should never use them, yolo

Awesome, thanks a lot :)

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

:tipshat:

Has anyone done much with ConstraintLayout and used the beta? Does it work better? I've been trying to use the stable version with the layout editor and holy crap it's maddening sometimes. Stuff randomly flying around, creating a chain makes an unselected object vanish into zero height, padding values changing and things acting like they don't have controls. Sometimes I'm scared to touch anything in case it suddenly goes wacky

Taffer
Oct 15, 2010


baka kaba posted:

Also some people hate fragments and say you should never use them, yolo

It's me, I'm that guy. Fragments suck and you should never use them.

baka kaba posted:

:tipshat:

Has anyone done much with ConstraintLayout and used the beta? Does it work better? I've been trying to use the stable version with the layout editor and holy crap it's maddening sometimes. Stuff randomly flying around, creating a chain makes an unselected object vanish into zero height, padding values changing and things acting like they don't have controls. Sometimes I'm scared to touch anything in case it suddenly goes wacky

I have. ConstraintLayout is super awesome but the editor is still extremely buggy and frustrating to use. At best I use it to throw together a few basics, then I use XML for everything else.

kitten smoothie
Dec 29, 2001

Taffer posted:

It's me, I'm that guy. Fragments suck and you should never use them.

Samesies. I guess if you get off on dealing with bugs from multiple layers of lifecycles they're cool.

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

^^^ lol literally just fixed a bug recently by enjoying the [fixed]isAdded[fixed] method on a fragment. That method is my buddy

Taffer posted:

It's me, I'm that guy. Fragments suck and you should never use them.


I have. ConstraintLayout is super awesome but the editor is still extremely buggy and frustrating to use. At best I use it to throw together a few basics, then I use XML for everything else.

*no quilted toilet paper, bart punches a wall*

Well I mean it can't be worse, at least? :ohdear: it's fine and great when it works, if it can just hold it together to let me build a simple layout then that'll do

I'm one of those people who XMLs everything because it's easier and neater to just define it, but all the constraint stuff really benefits from a GUI, until it doesn't of course

FAT32 SHAMER
Aug 16, 2012



kitten smoothie posted:

Samesies. I guess if you get off on dealing with bugs from multiple layers of lifecycles they're cool.



ok holy poo poo am i glad I just finished converting my fragments to activities :eyepop:

Tunga
May 7, 2004

Grimey Drawer

Taffer posted:

ConstraintLayout is super awesome but the editor is still extremely buggy and frustrating to use.
Yeah, exactly this. We've started converting some more complicated layouts to it at work. It's conceptually fantastic and when it works it's so much easier than nesting a bunch of poo poo. But sometimes the editor gets stuck in a weird state that you have to unfuck manually in the XML. Also it seems to have some kind of memory leak or similar as sometimes AS just grinds to snail pace after using the ConstraintLayout editor for a while and you have to restart the whole app (this is on Mac, may vary per platform). Also connverting existing layouts can be a bit tedious. I hope they keep working on it as the chains and barriers stuff is so much more awkward to do well in other layout ViewGroups.

Also, yes, gently caress Fragments. I kind of want to like them but in reality it's just a massive lifecycle mess and the real benefits are pretty niche (easier transition animations is probably the main one).

kitten smoothie
Dec 29, 2001

I think there'd be a lot fewer fragments in the world if the docs were more clear that a ViewPager doesn't require Fragments (they didn't call it a ViewPager for their health). But every example implementation delivers a pile of fragments so that's what everyone does.

kitten smoothie fucked around with this message at 05:09 on Aug 23, 2017

FAT32 SHAMER
Aug 16, 2012



My only complaint with activities over fragments is that the activity comes from the bottom of the screen and (as far as I can tell) I can't make the drawer highlight + make the current activity unclickable/not destroy+create itself when clicked

w/e though I'm not about to spend three weeks figuring out UI bullshit when I have a fuckload of stuff I have to figure out on the backend

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

FAT32 SHAMER posted:

My only complaint with activities over fragments is that the activity comes from the bottom of the screen and (as far as I can tell) I can't make the drawer highlight + make the current activity unclickable/not destroy+create itself when clicked


I have tried but simply cannot figure out what you mean by this.

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

I have tried but simply cannot figure out what you mean by this.

I'll record it tomorrow

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

FAT32 SHAMER posted:

My only complaint with activities over fragments is that the activity comes from the bottom of the screen and (as far as I can tell) I can't make the drawer highlight + make the current activity unclickable/not destroy+create itself when clicked

You just need to set up the menu for each activity, once the navigation drawer is initialised you can do getMenu() to poke around in there and find the item you want to change. You probably just want to look it up by id and then do setEnabled() to false. Usually you'd do something like setItemTextColor() to define what things should look like in states like disabled (or you can set that in XML)

Here's some people talking about old activity transitions - you can basically either set it on the activity when it starts and do some lifecycle janitoring, or do it app-wide by defining an animation in your app theme

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

You just need to set up the menu for each activity, once the navigation drawer is initialised you can do getMenu() to poke around in there and find the item you want to change. You probably just want to look it up by id and then do setEnabled() to false. Usually you'd do something like setItemTextColor() to define what things should look like in states like disabled (or you can set that in XML)

Here's some people talking about old activity transitions - you can basically either set it on the activity when it starts and do some lifecycle janitoring, or do it app-wide by defining an animation in your app theme

Ahhhhh ok derp.

Here's a gif of what i was talking about
https://giant.gfycat.com/AnnualColorlessKakarikis.gifv

edit: ok the world of animations is fun and exciting and the normies are going to love these

FAT32 SHAMER fucked around with this message at 15:41 on Aug 23, 2017

baka kaba
Jul 19, 2003

PLEASE ASK ME, THE SELF-PROFESSED NO #1 PAUL CATTERMOLE FAN IN THE SOMETHING AWFUL S-CLUB 7 MEGATHREAD, TO NAME A SINGLE SONG BY HIS EXCELLENT NU-METAL SIDE PROJECT, SKUA, AND IF I CAN'T PLEASE TELL ME TO
EAT SHIT

Looks like I forgot to actually link the stack overflow page but sounds like you found them anyway!

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

Looks like I forgot to actually link the stack overflow page but sounds like you found them anyway!

Yeah, cheers :)

John F Bennett
Jan 30, 2013

I always wear my wedding ring. It's my trademark.

Unrelated to things in this thread but what software do you use to create such a nice gif of your screen?

edit: oh never mind, you were filming your screen. I didn't notice it on my very cropped browser windows.

John F Bennett fucked around with this message at 10:59 on Aug 24, 2017

FAT32 SHAMER
Aug 16, 2012



John F Bennett posted:

Unrelated to things in this thread but what software do you use to create such a nice gif of your screen?

edit: oh never mind, you were filming your screen. I didn't notice it on my very cropped browser windows.

haha yeah, i built a new computer at work and havent installed all my software and didn't realize it until i went to record my screen

Adbot
ADBOT LOVES YOU

FAT32 SHAMER
Aug 16, 2012



Completely unrelated, is UnboundID the best LDAP lib for connecting my app to my company's LDAP server? I checked out something called Auth0 but they seem to be offering to host a new LDAP server instead of just giving me APIs to use to handle auth stuff

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply