Lecture #3 activities and intents

Post on 12-Apr-2017

188 views 8 download

transcript

Android AcademySession #3

Yonatan Levin

Activities and Intents3

First,

Yonatan Levin

levin.yonatanparahall

Campus TLVCampus TLV

Campus TLV

Campus TLV

~ 2000 members Largest Android Active Community

Jonathan Yarkoni

Android Developer & Advocate Ironsource

Android Academy Staff

Yonatan LevinGoogle Developer

Expert & Android @ Gett

Britt BarakAndroid Lead

Figure8

Yossi SegevAndroid Developer

Crave

What Do We Do?

●Android Fundamentals

●Android UI / UX

●Community Hackathon

●Android Performance

●Mentors Program●Active community

Online Lessons

Important:

Watch online lesson

before the meetup!

- Our course: “Developing

Android Apps”goo.gl/u1pxZv

- Optional: Nano Degree

- Optional: “Android Basics” courses

The Course Plan

- Online lesson @ home!

- Lecture @ Campus- Hands-on @ Campus- Questions @ Facebook

facebook.com/groups/android.academy.ils

Community Mentors

Roman Smirnov

Questions ?

What’s For Today?● Listeners● Toasts ● Intents, StartActivity,

Navigation● SharedPreferences● Broadcast Receivers

I ❤ Today’s Lecture!In this lecture, besides the Android stuff, we will show-case 2 design patterns:

Hidden Agenda for Today

The Observer PatternThe Static Factory Method Pattern

Design Patterns will be in Purple slides.

Design patterns will get this background. If you don’t know design patterns, it’s never too late to learn.

Sources

a CLASSic

A Java Moment-

OuterClass.InnerClass innerObject =

outerObject.new

InnerClass();

Consider this Crazy java codeline

Nested Classes

In Java, a class (or interface) can be declared inside another class.class A{ class B{ // ... }}

Read more: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

Inside C, you can use A.B:class C{ A.B abMember; A.B doSomething(){ /* … */ }}

Nested Classes

There are 2 types of nested classes: class A{

// This one is called a static nested class. static class B{ // ... }

// This one is called an inner class - because there’s no static. class B{ // ... }}Read more: https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html

Nested Classes - a Map

Read more: https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

Nested Class

Static Nested Class

Inner Class

Local class Anonymous Class

Examples:ViewHolderAsyncTask

Examples:RunnableOnClick

OuterClass.InnerClass innerObject =

outerObject.new

InnerClass();

Consider this Crazy java codeline

Our Starting Point

in activity_main.xml: <Button android:text="Hooking to buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="goToButtonsDemo" />

In MainActivity.java:public void goToButtonsDemo(View view) { Intent i = new Intent(this, ButtonsDemoActivity.class); startActivity(i);}

Handling Events

3 ways to listen(er)s

Way #1: android:onClick=”...” in xml

In activity_buttons_demo.xml declare the button with an OnClick: <Button android:text="Red +" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="increaseRed" android:background="@color/lightred" />

In ButtonsDemoActivity.java, implement the method that will be calledpublic void increaseRed(View view) { // ...}

Way #2: Inline OnClickListener

In the Activity’s onCreate method, grab the buttons and set their OnClickListener with an inline implementation@Overrideprotected void onCreate(Bundle savedInstanceState) { // ... Button increaseGreen = (Button)findViewById(R.id.buttonsdemo_incGreen); increaseGreen.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ... } });}

Way #3: Single OnClickListener

(1) Make the activity implement View.OnClickListener: public class ButtonsDemoActivity extends ActionBarActivity implements View.OnClickListener

(2) In OnCreate, set the button’s listener to the activity (with this):@Overrideprotected void onCreate(Bundle savedInstanceState) { // … Button decreaseRed = (Button)findViewById(R.id.buttonsdemo_decRed); decreaseRed.setOnClickListener(this);}

Way #3: Single OnClickListener

(3) Implement the interface, use a switch to tell which button view was clicked:@Overridepublic void onClick(View v) { switch (v.getId()){ case R.id.buttonsdemo_decRed: red = calcNewValue(red, -1); break; case R.id.buttonsdemo_decGreen: green = calcNewValue(green, -1); break; case R.id.buttonsdemo_decBlue: blue = calcNewValue(blue, -1); break; } refreshDisplay();}

Demo: The Color Mixer

1 Button: Way #12 Buttons: Way #23 Buttons: Way #3

Not really 3 ways...

In fact, there’s only one way to have a button do something:

Set a View.OnClickListener.Way #1 does this implicitly, and ways 2,3 does this explicitly.

What is a listener?

Listener: an interface that contains a single callback method.

When the user interacts with the UI, the Views trigger a call to the listener (if it’s not null) and calls the callback.As long as the view and the listener agrees on the contract between them (~ The method’s signature), the view doesn’t care what the implementation is.This is a great example of the Observer design pattern.Source: http://developer.android.com/guide/topics/ui/ui-events.html#EventListeners

The Observer Design Pattern

A Behavioral design pattern, commonly used in UI (but not only).

Read a lot more: https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Observer

View.OnClickListener

View

Your Listener

How does xml onClick (way #1) work

When you use the xml’s onClick way, the View uses a View.DeclaredOnClickListener, It uses reflection (which is slow on android),but It’s Lazy and Cached,and is not validated at Compile-Time.Since android is Open-Source, check out the implementation at the link below.https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/View.java Line 4429

Which is better?

Way #1: onClick=”...” → DeclaredOnClickListener

Way #2: inline listener implementation

Way #3: activity interface implementationhttp://stackoverflow.com/questions/21319996/android-onclick-in-xml-vs-onclicklistener

Pros: Clean code, no findViewById at allCons: Reflection, No Compile-Time validation, API Level ≥ 4, not readable

Pros: Readable codeCons: +1 class, +1 method, ~500 bytes, +Boilerplate code, if you have a lot of clicks becomes mess

Pros: No object allocationsCons: Felix: The horrible switch (over view.getId())

What other listeners are available?

Read more: http://developer.android.com/guide/topics/ui/ui-events.html

Event HandleronClickView.OnClickListener onLongClickView.OnLongClickListeneronFocusChangeView.OnFocusChangeListeneronKeyView.OnKeyListeneronTouchView.OnTouchListeneronCreateContextMenuView.OnCreateContextMenuListener

AdapterView.OnItemClickListener

Non-void Listeners

Some flows requires listeners to tell when they take care of the event.These listeners return a boolean:true if the event is consumed, or false if it isn’t.onTouchListener is a good example.

OnClick || OnTouch?

OnTouch gets called on any change in the touch detection,and contains touch data. The MotionEvent is passed to the root view, and it travels until it finds an OnTouchListener that would handle it. Some may react to the event without consuming it - common practice for when using Gesture Detectors.For “Everyday tapping” - You usually should go with onClick.Read More: http://codetheory.in/understanding-android-input-touch-events/

and here: http://developer.android.com/training/gestures/detector.html

Take Aways - Listeners

1.UI Interaction is implemented with Listeners.

2.Some listeners returns a boolean value to indicate handling.

3.OnTouch is really important, and in most cases, you won’t need it.

And the Static Factory Method pattern

ToastsB

Toasts

provides simple feedback about an operation in a small popup. The current activity remains visible and interactive - But the toast itself is not interactive.Toast disappear after a short time.

http://developer.android.com/guide/topics/ui/notifiers/toasts.html

Make a Toast

Toast.makeText(this,"I'm your father,Luke!",Toast.LENGTH_LONG)

.show();

Toast Positioning

toast.setGravity(Gravity.CENTER|Gravity.RIGHT, 0, 0);

Static Factory Method

●The best ways to create an object.●Have a name●Can cache and not always create new class●They return an object●Reduce the verbosity of creating parameterized

type instancesRead more: http://www.informit.com/articles/article.aspx?p=1216151

Hear more: http://fragmentedpodcast.com/episodes/14/

Static Factory Methodpublic static Toast makeText(Context context, CharSequence text, @Duration int duration) { Toast result = new Toast(context);

LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration;

return result;}

Read more: http://www.informit.com/articles/article.aspx?p=1216151

Most important thing about Toasts:

show()

Not in course: Snackbars

Defined in the Material Design spec, SnackBars also allow user interaction - and also work great with FABs - Both are out-of-scope from our course.

http://developer.android.com/reference/android/support/design/widget/Snackbar.html

If you need user interaction, you can use a notification (which we’ll see in Session #6)

and if you really need a custom toast design, you can - but it’s not covered here.

Also not talking about...

- It lets newly created objects understand what has been going on.

- Global information about an application environment- Creating New objects: Creating new views, adapters,

listeners:TextView tv = new TextView(getContext());ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);

- Accessing Standard Common Resources: Services like LAYOUT_INFLATER_SERVICE, SharedPreferences:context.getSystemService(LAYOUT_INFLATER_SERVICE);getApplicationContext().getSharedPreferences(*name*, *mode*);

- Accessing Components Implicitly: Regarding content providers, broadcasts, intent: getApplicationContext().getContentResolver().query(uri, ...);

Context - #1 reason of memory leaks

- Further reads: https://possiblemobile.com/2013/06/context/

View svButton = findViewById(R.id.sv_button);svButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticView(); }});

public static View view;

void setStaticView() { view = findViewById(R.id.sv_button);}

and also a bit about Navigation

Intents and StartActivity

C

Intents

messaging object you can to request an action from another app component.Main Use-cases:

- Starting an activity- Broadcasting a message- Starting a service (wait for

Session 6)http://developer.android.com/guide/components/intents-filters.html

Intent Types

Explicit Intent - have a ComponentName - so Android knows exactly what to call.

Implicit Intent, doesn’t have a ComponentName - so Android uses Intent Filters to know what to do.

used for Explicit Intents

used for Implicit Intents

used to tell things to the recipient

used to tell things to the messenger

Intents have...

Component NameActionData (and Type)CategoryExtrasFlags

Intent Filters

Intent filters are used to advertise what intents your app can handle. We’ve already seen MainActivity’s intent filter in Session #1.

Read more: http://developer.android.com/guide/components/intents-filters.html#Receiving

ExplicitlyIntent intent = new Intent(this, TargetActivity.class);

startActivity(intent);

or

Intent intent = new Intent(getApplicationContext(), TargetActivity.class);

getApplicationContext().startActivity(intent);

ImplicitlyIntent intent = new Intent(android.content.Intent.ACTION_VIEW,

Uri.parse("geo:37.7749,-122.4194"));

startActivity(intent);

But what if?public class MainActivity extends AppCompatActivity {

public static final String TARGET_ACTIVITY_DATA_KEY = "KEY1"; public static final String TARGET_ACTIVITY_MORE_DATA_KEY = "KEY2"; public static final String TARGET_ACTIVITY_EVEN_MORE_DATA_KEY = "KEY3";

@Override protected void onCreate(Bundle savedInstanceState) { Intent intent = new Intent(this, TargetActivity.class); intent.putExtra(TARGET_ACTIVITY_DATA_KEY,"Luke Skywalker"); intent.putExtra(TARGET_ACTIVITY_MORE_DATA_KEY,"Darth Vader"); intent.putExtra(TARGET_ACTIVITY_EVEN_MORE_DATA_KEY,"Han Solo"); startActivity(intent); }

But what if?public class TargetActivity extends AppCompatActivity {

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

Bundle extras = getIntent().getExtras(); String first = (String) extras.get(MainActivity.TARGET_ACTIVITY_DATA_KEY); String second = extras.getString(MainActivity.TARGET_ACTIVITY_MORE_DATA_KEY); int error = extras.getInt(MainActivity.TARGET_ACTIVITY_EVEN_MORE_DATA_KEY); initViews(first,second,error); }

What wrong?

- Non readable- Mix of static variables- Other activities should know what target expect- Run-time error

Solutionpublic class MainActivity extends AppCompatActivity {

@Override protected void onCreate(Bundle savedInstanceState) {

Intent intent = TargetActivity .createIntent(this, "Luke Skywalker", "Darth Vader", "Han Solo"); startActivity(intent);

}}

Solutionpublic class TargetActivity extends AppCompatActivity {

public static final String TARGET_ACTIVITY_DATA_KEY = "KEY1"; public static final String TARGET_ACTIVITY_MORE_DATA_KEY = "KEY2"; public static final String TARGET_ACTIVITY_EVEN_MORE_DATA_KEY = "KEY3";

public static Intent createIntent(Context context,String first,String second, String third){ Intent intent = new Intent(context, TargetActivity.class); intent.putExtra(TARGET_ACTIVITY_DATA_KEY,first); intent.putExtra(TARGET_ACTIVITY_MORE_DATA_KEY,second); intent.putExtra(TARGET_ACTIVITY_EVEN_MORE_DATA_KEY,third); return intent; }

SharedPreferences SettingsActivity

D

SharedPreferences

- Great thing to store persistent various. - Not accessible by other apps*- Used to save things between sessions- Sometimes could be great communication tool

(like intent) using “onSharedPreferenceChangeListener”

MainActivitypublic static final String TEXT_SHARED_PREF_KEY = "TEXT_SHARED_PREF_KEY";public static final String SHARED_PREF_KEY = "KEY_FOR_SHARED_PREF";

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

Button saveBtn = (Button) findViewById(R.id.btn_am_save_data); Button restoreBtn = (Button) findViewById(R.id.btn_am_restore_data); saveBtn.setOnClickListener(this); restoreBtn.setOnClickListener(this);}

MainActivity@Overridepublic void onClick(View v) { SharedPreferences preferences = getSharedPreferences(SHARED_PREF_KEY, MODE_PRIVATE); EditText editText = (EditText) findViewById(R.id.et_am_text); switch (v.getId()) { case R.id.btn_am_save_data: SharedPreferences.Editor editor = preferences.edit(); editor.putString(TEXT_SHARED_PREF_KEY, editText.getText().toString()); editor.apply(); finish(); break; case R.id.btn_am_restore_data: String text = preferences.getString(TEXT_SHARED_PREF_KEY,""); editText.setText(text); break; }}

And...

- Don’t forget that each commit() is I/O operation.- use Apply() instead

Last Part:Broadcast Receivers

E

Broadcast Receivers

Great guy :)- Loose Coupling- 1-to-n relationship- The onReceive() method is always executed on the

main thread- You can notify components in your entire

application, so the communicating components do not have to "see" each other.

But...

- Marshaling data via intent really hard- Register/Unregister it when you do not needed him

(BaseActivity)- Not build to transfer large objects

Receiverpublic class ResponseReceiver extends BroadcastReceiver { public static final String ACTION_RESP = "com.example.intent.action.PROGRESS_DOWNLOAD";

@Override public void onReceive(Context context, Intent intent) { TextView result = (TextView) findViewById(R.id.tv_am_progress); String text = intent.getStringExtra(DownloadService.PROGRESS); result.setText(text + "%"); }}

Receiver@Overridepublic void onClick(View v) { Intent intent = new Intent(this, DownloadService.class); startService(intent); IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP); filter.addCategory(Intent.CATEGORY_DEFAULT); receiver = new ResponseReceiver(); registerReceiver(receiver, filter);}@Overrideprotected void onStop() { unregisterReceiver(receiver); super.onStop();}

Receiver<application... > ... <receiver android:name=".MainActivity$ResponseReceiver"/></application>

BroadcastIntent broadcastIntent = new Intent();

broadcastIntent.setAction(MainActivity.ResponseReceiver.ACTION_RESP);

broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);

broadcastIntent.putExtra(PROGRESS, ""+i);

sendBroadcast(broadcastIntent);

*sendStickyBroadcast() - no longer available

Security hole!!!Security Threat!!!

How to treat?

Use LocalBroadcastManagerValidate the intent that you gotValidate permissions