from newbie to

Post on 12-Apr-2017

24 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Wifi: campus-guestPassword: payitforward

Wifi?+

Yonatan LevinGoogle Developer Expert

parahalllevin.yonatan

>100 Cities > 30M usersRuby, Go, Python, Microservices

Ooooops...

Making genetic tests accessible

> 2000 members Largest Android Active Community

Jonathan Yarkoni

Android Developer & Advocate Ironsource

Android Academy Staff

Yonatan Levin

Android Google Developer

Expert

Britt Barak

Android LeadFigure8

Yossi Segev

Android Developer

Colu

Shahar Avigezer

Android DeveloperHello Heart

Community Mentors

What Do We Do?

●Android Fundamentals - NOW

● Android UI / UX - 29/1 !

● Community Hackathon - 9/3 !!!

●Android Performance

●Mentors Program●Active community

From Newbie to ...

Disclaimer: It’s my personal opinion

It’s all about “from Junior to Senior”

github.com/parahall/star_wars_movies

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/background_dark" android:orientation="vertical" tools:context="com.academy.android.starwarsmovies.MainActivity" >

<ImageView android:layout_width="match_parent" android:layout_height="100dp" android:layout_gravity="center_horizontal" android:src="@drawable/logo" />

<ProgressBar android:id="@+id/pb_am_loading" android:layout_width="match_parent" android:layout_height="match_parent" />

<ListView android:id="@+id/lv_am_movie_list" android:layout_width="match_parent" android:layout_height="match_parent" />

</LinearLayout>

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/background_dark" android:orientation="horizontal" android:padding="16dp" >

<ImageView android:layout_marginTop="8dp" android:id="@+id/iv_im_movie_poster" android:layout_width="70dp" android:layout_height="98dp" android:scaleType="fitXY" />

<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:orientation="vertical" > <TextView android:id="@+id/tv_im_movie_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textColor="@android:color/background_light" android:textSize="16sp" android:textStyle="bold" />

<TextView android:id="@+id/tv_im_movie_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textColor="@android:color/background_light" android:textSize="12sp" />

<TextView android:id="@+id/tv_im_movie_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textColor="@android:color/background_light" android:textStyle="italic" />

</LinearLayout>

</LinearLayout>

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

listView = (ListView) findViewById(R.id.lv_am_movie_list); listView.setVisibility(View.GONE); progressBar = (ProgressBar) findViewById(R.id.pb_am_loading); progressBar.setVisibility(View.VISIBLE); StarWarsAsyncTask asyncTask = new StarWarsAsyncTask(); asyncTask.execute();}

private class StarWarsAsyncTask extends AsyncTask<Void, Void, ArrayList<StarWarsMovie>> {

@Override protected ArrayList<StarWarsMovie> doInBackground(Void... params) { ... }

@Override protected void onPostExecute(ArrayList<StarWarsMovie> starWarsMovies) { … } }

[

{

"name": "Star wars",

"description": "The Imperial Forces, under orders from cruel Darth Vader,

hold Princess Leia hostage in their efforts to...",

"imageUrl": "http://cdn.collider.com/wp-content/uploads/star-wars-movie-

poster.jpg",

"releaseDate": "May 25, 1977"

},

{

"name": "Empire Strikes Back",

"description": "Luke Skywalker, Han Solo, ….",

"imageUrl" :

"https://cdn-images-1.medium.com/max/800/1*q6sBOxkTz7RXOimTLnGrjg.jpeg",

"releaseDate": "May 17, 1980"

},

{ ...

@Override protected ArrayList<StarWarsMovie> doInBackground(Void... params) { String json = ResourcesUtil.loadJson(MainActivity.this); ArrayList<StarWarsMovie> moviesList = null; JSONArray array = new JSONArray(json); moviesList = new ArrayList<>(array.length()); for (int i = 0; i < array.length(); i++) { JSONObject jsonObject = array.getJSONObject(i); String name = jsonObject.getString("name"); String description = jsonObject.getString("description"); String imageUrl = jsonObject.getString("imageUrl"); String releaseDate = jsonObject.getString("releaseDate");

SimpleDateFormat format = new SimpleDateFormat("MMM d, yyyy"); Date date = format.parse(releaseDate); ImageLoader imgLoader = new ImageLoader(getApplicationContext()); Bitmap bitmap = imgLoader.displayImage(imageUrl);

moviesList.add(new StarWarsMovie(name, description, bitmap, date)); } return moviesList; }

public static String loadJson(Context context) { StringBuilder jsonString = new StringBuilder(); URL url = new URL("https://api.starwars.com/movies"); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); InputStream in = new BufferedInputStream(urlConnection.getInputStream());

BufferedReader reader = new BufferedReader(new InputStreamReader(in));

String line; while ((line = reader.readLine()) != null) { jsonString.append(line); } return jsonString.toString();}

@Override protected void onPostExecute(ArrayList<StarWarsMovie> starWarsMovies) { progressBar.setVisibility(View.GONE); listView.setVisibility(View.VISIBLE); MovieAdapter movieAdapter = new MovieAdapter(MainActivity.this, starWarsMovies); listView.setAdapter(movieAdapter); } }

private class MovieAdapter extends ArrayAdapter<StarWarsMovie> { public MovieAdapter(Context context, ArrayList<StarWarsMovie> users) { super(context, 0, users); }

@Override public View getView(int position, View convertView, ViewGroup parent) { StarWarsMovie starWarsMovie = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_movie, parent, false); } TextView tvName = (TextView) convertView.findViewById(R.id.tv_im_movie_name); TextView tvDescription = (TextView) convertView.findViewById(R.id.tv_im_movie_description); TextView tvDate = (TextView) convertView.findViewById(R.id.tv_im_movie_date); ImageView ivPoster = (ImageView) convertView.findViewById(R.id.iv_im_movie_poster);

tvName.setText(starWarsMovie.getName()); tvDescription.setText(starWarsMovie.getDescription());

SimpleDateFormat format = new SimpleDateFormat("MMM d, yyyy"); tvDate.setText(format.format(starWarsMovie.getReleaseDate())); ivPoster.setImageBitmap(starWarsMovie.getImageBitmap());

return convertView; } }

Wake Up!VIEWHOLDER!!!!!

Is it only the problem here?

@Override protected ArrayList<StarWarsMovie> doInBackground(Void... params) { String json = ResourcesUtil.loadJson(MainActivity.this); ArrayList<StarWarsMovie> moviesList = null; JSONArray array = new JSONArray(json); moviesList = new ArrayList<>(array.length()); for (int i = 0; i < array.length(); i++) { JSONObject jsonObject = array.getJSONObject(i); String name = jsonObject.getString("name"); String description = jsonObject.getString("description"); String imageUrl = jsonObject.getString("imageUrl"); String releaseDate = jsonObject.getString("releaseDate");

SimpleDateFormat format = new SimpleDateFormat("MMM d, yyyy"); Date date = format.parse(releaseDate); ImageLoader imgLoader = new ImageLoader(getApplicationContext()); Bitmap bitmap = imgLoader.displayImage(imageUrl);

moviesList.add(new StarWarsMovie(name, description, bitmap, date)); } return moviesList; }

Memory Cache File Cache Load from the internet

public class ImageLoader {

MemoryCache memoryCache = new MemoryCache();

FileCache fileCache;

ExecutorService executorService;

public ImageLoader(Context context) {

fileCache = new FileCache(context);

executorService = Executors.newFixedThreadPool(5);

}

public class ImageLoader {

public Bitmap displayImage(String url) {

Bitmap bitmap = memoryCache.get(url);

if (bitmap == null) {

bitmap = getBitmap(url);

memoryCache.put(url, bitmap);

}

return bitmap;

}

private Bitmap getBitmap(String url) { File f = fileCache.getFile(url);

//from SD cache Bitmap b = decodeFile(f); if (b != null) return b;

//from web Bitmap bitmap = null; URL imageUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection(); conn.setConnectTimeout(30000); conn.setReadTimeout(30000); conn.setInstanceFollowRedirects(true); InputStream is = conn.getInputStream(); OutputStream os = new FileOutputStream(f); Utils.CopyStream(is, os); os.close(); bitmap = decodeFile(f); return bitmap; }

What if image too large?

//decodes image and scales it to reduce memory consumption

private Bitmap decodeFile(File f) {

try {

//decode image size

BitmapFactory.Options o = new BitmapFactory.Options();

o.inJustDecodeBounds = true;

BitmapFactory.decodeStream(new FileInputStream(f), null, o);

//Find the correct scale value. It should be the power of 2.

final int REQUIRED_SIZE = 70;

int width_tmp = o.outWidth, height_tmp = o.outHeight;

int scale = 1;

while (true) {

if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE) break;

width_tmp /= 2;

height_tmp /= 2;

scale *= 2;

}

//decode with inSampleSize

BitmapFactory.Options o2 = new BitmapFactory.Options();

o2.inSampleSize = scale;

return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);

} catch (FileNotFoundException e) {

}

return null;

}

And it’s just a beginning…

Source: http://konmik.com/post/introduction_to_model_view_presenter_on_android/

Source: http://konmik.com/post/introduction_to_model_view_presenter_on_android/

Complex tasks are split into simpler tasks and are easier to solve.

Smaller objects, less bugs, easier to debug.Testable.

MvpView

MainActivityView

Implements

BasePresenter<T extends MvpView>

Extends

interface abstract

Generic of type

Of Type

Activity

Implements Uses

Creates

ServiceMainPresenter

This was changed

public class StarWarsService extends IntentService {

@Override protected void onHandleIntent(Intent intent) { if (intent != null) { String json = ResourcesUtil.loadJson(this); ArrayList<StarWarsMovie> moviesList = null; JSONArray array = new JSONArray(json); moviesList = new ArrayList<>(array.length()); for (int i = 0; i < array.length(); i++) { JSONObject jsonObject = array.getJSONObject(i); String name = jsonObject.getString("name"); String description = jsonObject.getString("description"); String imageUrl = jsonObject.getString("imageUrl"); String releaseDate = jsonObject.getString("releaseDate"); SimpleDateFormat format = new SimpleDateFormat("MMM d, yyyy"); Date date = format.parse(releaseDate); ImageLoader imgLoader = new ImageLoader(getApplicationContext()); Bitmap bitmap = imgLoader.displayImage(imageUrl); moviesList.add(new StarWarsMovie(name, description, bitmap, date)); }...

public class StarWarsService extends IntentService {

@Override protected void onHandleIntent(Intent intent) { ... Intent response = new Intent(); response.setAction(MainPresenter.LOAD_COMPLETE_ACTION); response.putExtra(MainPresenter.DATA_KEY, moviesList); LocalBroadcastManager.getInstance(this).sendBroadcast(response); } }}

public interface MvpView {

public Context getAppContext();

public Context getActivityContext();

}

public interface MainActivityView extends MvpView {

ListView getListView();

ProgressBar getProgressBar();

}

public abstract class BasePresenter<T extends MvpView> { HandlerThread thread; Looper looper; Handler handler; private T mView;

public void attachView(T t) { thread = new HandlerThread(getClass().getName(),

android.os.Process.THREAD_PRIORITY_BACKGROUND); thread.start(); looper = thread.getLooper(); mView = t; }

public void detachView() { mView = null; if (handler != null) { handler.removeCallbacksAndMessages(null); handler = null; } if (thread != null) thread.quit(); }

}

public abstract class BasePresenter<T extends MvpView> {

public T getView() { return mView; }

public boolean isViewAttached() { return mView != null; }

}

Show me THE PRESENTER!

public class MainPresenter extends BasePresenter<MainActivityView> {

@Override public void attachView(MainActivityView mainActivityView) { super.attachView(mainActivityView); registerLoadDataReceiver();

if (isCacheAvailable()) { updateView(getView().getAppContext(), getView(), movieList); } else { Intent serviceIntent = new Intent(getView().getAppContext(), StarWarsService.class); getView().getAppContext().startService(serviceIntent); } }

public class MainPresenter extends BasePresenter<MainActivityView> {

@Override public void detachView() { unRegisterLoadDataReceiver(); Intent serviceIntent = new Intent(getView().getAppContext(), StarWarsService.class); getView().getAppContext().stopService(serviceIntent); super.detachView(); }

private void updateView(Context context, MainActivityView activityView, ArrayList<StarWarsMovie> list) {

MovieAdapter movieAdapter = new MovieAdapter(context, list); activityView.getListView().setAdapter(movieAdapter);

activityView.getProgressBar().setVisibility(View.GONE); activityView.getListView().setVisibility(View.VISIBLE);}

public class MainPresenter extends BasePresenter<MainActivityView> {

private void registerLoadDataReceiver() { movieDataReceiver = new StarWarsMovieDataReceiver(); LocalBroadcastManager.getInstance(getView().getAppContext()) .registerReceiver(movieDataReceiver, new IntentFilter(LOAD_COMPLETE_ACTION)); }

private void unRegisterLoadDataReceiver() { if (movieDataReceiver != null) { LocalBroadcastManager.getInstance(getView().getAppContext()) .unregisterReceiver(movieDataReceiver); }}

private class StarWarsMovieDataReceiver extends BroadcastReceiver {

@Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(LOAD_COMPLETE_ACTION)) {

if (isViewAttached()) { MainActivityView activityView = getView(); movieList = (ArrayList<StarWarsMovie>) intent.getSerializableExtra(DATA_KEY); updateView(context, activityView, movieList); } } }}

Activity?

public class MainActivity extends AppCompatActivity implements MainActivityView {

... public static MainPresenter mainPresenter;

@Override protected void onCreate(Bundle savedInstanceState) { ... ... if (mainPresenter == null) { mainPresenter = new MainPresenter(); } mainPresenter.attachView(this); }

@Override protected void onDestroy() { super.onDestroy(); mainPresenter.detachView(); if (isFinishing()) mainPresenter = null; }

@Override public Context getAppContext() { return getApplicationContext(); }

@Override public Context getActivityContext() { return this; }

@Override public ListView getListView() { return listView; }

@Override public ProgressBar getProgressBar() { return progressBar; }}

ATTENTION!

public class MainActivity extends AppCompatActivity implements MainActivityView {

@Override public Context getAppContext() { return getApplicationContext(); }

@Override public Context getActivityContext() { return this; }

@Override public ListView getListView() { return listView; }

@Override public ProgressBar getProgressBar() { return progressBar; }}

Questions ?

There are other ways to retain presenter

- Let him go (Not so user friendly)

- Static (Could be issue with multiple

instance)

- Fragments - setRetainInstance(true) (what

about nested fragments)?

- Loaders (Tricky when onLoaderFinished()

called within lifecycle)

- Static map. Nucleus.

Questions ?

We can do more. It’s just a beginning

compile 'com.squareup.retrofit2:retrofit:2.1.0'

compile 'com.squareup.retrofit2:converter-gson:2.1.0'

public interface StarwarsApi {

@GET("/movies/{movie_id}")

Call<StarWarsMovie> getMovie(@Path("movie_id") int id);

@GET("/movies")

Call<ArrayList<StarWarsMovie>> listMovies();

}

public static ArrayList<StarWarsMovie> getStarWarsMovies() {

ArrayList<StarWarsMovie> moviesList = null; Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.starwars.com/") .addConverterFactory( GsonConverterFactory.create(new GsonBuilder().setDateFormat("MMM d, yyyy").create())) .build();

StarwarsApi starwarsApi = retrofit.create(StarwarsApi.class);

Call<ArrayList<StarWarsMovie>> arrayListCall = starwarsApi.listMovies(); Response<ArrayList<StarWarsMovie>> listResponse = arrayListCall.execute(); moviesList = listResponse.body(); return moviesList;}

Memory Cache File Cache Load from the internet

ImageLoader imgLoader = new ImageLoader(getApplicationContext());

Bitmap bitmap = imgLoader.displayImage(imageUrl);

compile 'com.github.bumptech.glide:glide:3.7.0'

bitmap =

Glide.with(getApplicationContext()).load(imageUrl).asBitmap().into(100,

100).get();

More read

https://futurestud.io/tutorials/glide-getting-started

https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

findViewById no more

compile 'com.jakewharton:butterknife:8.4.0'

annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'

public class MainActivity extends AppCompatActivity implements MainActivityView {

private @BindView(R.id.lv_am_movie_list) ListView listView; private @BindView(R.id.pb_am_loading) ProgressBar progressBar;

@Override protected void onCreate(Bundle savedInstanceState) { …

listView = (ListView) findViewById(R.id.lv_am_movie_list);

progressBar = (ProgressBar) findViewById(R.id.pb_am_loading);

ButterKnife.bind(this); listView.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE);

}

static class ViewHolder {

@BindView(R.id.tv_im_movie_name) TextView tvName;

@BindView(R.id.tv_im_movie_description) TextView tvDescription;

@BindView(R.id.tv_im_movie_date) TextView tvDate;

@BindView(R.id.iv_im_movie_poster) ImageView ivPoster;

public ViewHolder(View view) {

ButterKnife.bind(this, view);

}

}

private class MovieAdapter extends ArrayAdapter<StarWarsMovie> {

public View getView(int position, View convertView, ViewGroup parent)

{

if (convertView == null) {

convertView =

LayoutInflater.from(getContext()).inflate(R.layout.item_movie, parent,

false);

holder = new ViewHolder();

holder.tvName = (TextView)

convertView.findViewById(R.id.tv_im_movie_name);

holder.tvDescription = (TextView)

convertView.findViewById(R.id.tv_im_movie_description);

holder.tvDate = (TextView)

convertView.findViewById(R.id.tv_im_movie_date);

holder.ivPoster = (ImageView)

convertView.findViewById(R.id.iv_im_movie_poster);

convertView.setTag(holder);

...

Can we do better?

Json Parsing

public class StarWarsMovie {

private String name;

private String description;

private BitmapString imageBitmapUrl;

private Date releaseDate;

}

compile 'com.google.code.gson:gson:2.8.0'

@Override protected void onHandleIntent(Intent intent) {

if (intent != null) {

...

Gson gson = new GsonBuilder().setDateFormat("MMM d,

yyyy").create();

ArrayList<StarWarsMovie> moviesList =

gson.fromJson(json, new TypeToken<ArrayList<StarWarsMovie>>() {

}.getType());

}

}

private class MovieAdapter extends ArrayAdapter<StarWarsMovie> {

@NonNull @Override public View getView(int position, View

convertView, ViewGroup parent) {

...

Glide.with(getContext())

.load(starWarsMovie.getImageUrl())

.placeholder(R.drawable.placeholder)

.into(holder.ivPoster);

return convertView;

}

Our performance course- LeakCanary- Retrofit- Dagger- Parceler- EventBus- RxJava (why not)- Retrolambda- Kotlin?

Britt BreakOR

top related