Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions app/src/main/java/ru/yandex/yamblz/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import ru.yandex.yamblz.developer_settings.DevMetricsProxy;
import ru.yandex.yamblz.developer_settings.DeveloperSettingsModel;
import ru.yandex.yamblz.handler.CriticalSectionsManager;
import ru.yandex.yamblz.handler.StubCriticalSectionsHandler;
import ru.yandex.yamblz.loader.CollageLoaderManager;
import ru.yandex.yamblz.loader.StubCollageLoader;
import timber.log.Timber;

public class App extends Application {
Expand All @@ -34,8 +36,8 @@ public void onCreate() {
devMetricsProxy.apply();
}

CollageLoaderManager.init(null); // add implementation
CriticalSectionsManager.init(null); // add implementation
CollageLoaderManager.init(new StubCollageLoader()); // add implementation
CriticalSectionsManager.init(new StubCriticalSectionsHandler()); // add implementation
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,84 @@
package ru.yandex.yamblz.handler;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

public class StubCriticalSectionsHandler implements CriticalSectionsHandler {

private Set<Integer> sections = new HashSet<>();
private LinkedList<Task> taskQueue = new LinkedList<>();
private Handler handler = new Handler(Looper.getMainLooper());
private HashMap<Task, Runnable> delayedTask = new HashMap<>();
private Runnable callBack = new Runnable() {
@Override
public void run() {
if (taskQueue.isEmpty()) {
return;
}

Task firstTask = taskQueue.pop();
firstTask.run();

if (sections.isEmpty()) {
handler.post(callBack);
}
}
};

@Override
public void startSection(int id) {

Log.d("startSection", Integer.toString(sections.size()));
sections.add(id);
Log.d("startSection", Integer.toString(sections.size()));
handler.removeCallbacks(callBack);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

У тебя в очереди 10 тасков, один из них начал выполняться. В это время сказали что началась критическая секция. Ты удалил ранбл, но он уже выполнялся параллельно в другом потоке и добавил сам себя опять в хендлер. И получается что съел таким образом твое удаление при старте секции критической

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@metrologma Он же проверяет, перед тем, как запушить на if (sections.isEmpty()) {

}

@Override
public void stopSection(int id) {

Log.d("stopSection", Integer.toString(sections.size()));
sections.remove(id);
Log.d("stopSection", Integer.toString(sections.size()));
if (sections.isEmpty()) {
handler.post(callBack);
}
}

@Override
public void stopSections() {

sections.clear();
handler.post(callBack);
}

@Override
public void postLowPriorityTask(Task task) {

Log.d("postLowPriorityTask", "post");
taskQueue.add(task);
if (taskQueue.size() == 1 && sections.isEmpty()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Почему длина очереди должна быть равна 1?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@metrologma Ну если до этого этот callback выполнил все таски, то он себя не запушит заново, и если пришла новая таска в этот момент, то нужно это сделать.

handler.post(callBack);
}
}

@Override
public void postLowPriorityTaskDelayed(Task task, int delay) {

Runnable runnable = () -> postLowPriorityTask(task);
delayedTask.put(task, runnable);
handler.postDelayed(runnable, delay);
}

@Override
public void removeLowPriorityTask(Task task) {

taskQueue.remove(task);
handler.removeCallbacks(delayedTask.get(task));
}

@Override
public void removeLowPriorityTasks() {

taskQueue.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.widget.ImageView;

import java.lang.ref.WeakReference;
import java.util.List;

public interface CollageLoader {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
public interface CollageStrategy {

Bitmap create(List<Bitmap> bitmaps);
int numOfImage();
}
46 changes: 46 additions & 0 deletions app/src/main/java/ru/yandex/yamblz/loader/CollageStrategyImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ru.yandex.yamblz.loader;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.Log;

import java.util.List;
import java.util.Random;

/**
* Created by kostya on 26.07.16.
*/

public class CollageStrategyImpl implements CollageStrategy {
private static final int WIDTH = 600;
private static final int HEIGHT = 600;
@Override
public Bitmap create(List<Bitmap> bitmaps) {
Log.d("Bitmap create, size = ", Integer.toString(bitmaps.size()));
if (bitmaps.isEmpty()) {
return null;
}
else if (bitmaps.size() < 4) {
Bitmap bitmap = Bitmap.createBitmap(WIDTH/2, HEIGHT/2, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(bitmaps.get(0), 0f, 0f, null);
canvas.save();
return bitmap;
}
else {
Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(bitmaps.get(0), 0f, 0f, null);
canvas.drawBitmap(bitmaps.get(1), WIDTH/2, 0, null);
canvas.drawBitmap(bitmaps.get(2), 0f, HEIGHT/2, null);
canvas.drawBitmap(bitmaps.get(3), WIDTH/2, HEIGHT/2, null);
canvas.save();
return bitmap;
}
}

@Override
public int numOfImage() {
return 4;
}
}
81 changes: 81 additions & 0 deletions app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
package ru.yandex.yamblz.loader;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import ru.yandex.yamblz.R;
import ru.yandex.yamblz.handler.CriticalSectionsManager;
import ru.yandex.yamblz.handler.Task;

public class StubCollageLoader implements CollageLoader {

private final ExecutorService pool = Executors.newFixedThreadPool(10);

@Override
public void loadCollage(List<String> urls, ImageView imageView) {

Expand All @@ -19,7 +41,28 @@ public void loadCollage(List<String> urls, ImageTarget imageTarget) {
@Override
public void loadCollage(List<String> urls, ImageView imageView,
CollageStrategy collageStrategy) {
imageView.setImageDrawable(null);

Object old_setter = imageView.getTag();
if (old_setter != null) {
Log.d("onBindViewHlder", "removeLowPriorityTask");
CriticalSectionsManager.getHandler().removeLowPriorityTask((Task) old_setter);
}

UUID uuid = UUID.randomUUID();

Task setter = () -> {
pool.execute(() -> loadCollageImpl(
urls,
imageView,
collageStrategy,
uuid
));
};

imageView.setTag(setter);
imageView.setTag(R.id.img_in_list, uuid);
CriticalSectionsManager.getHandler().postLowPriorityTask(setter);
}

@Override
Expand All @@ -28,4 +71,42 @@ public void loadCollage(List<String> urls, ImageTarget imageTarget,

}

private void loadCollageImpl(List<String> urls, ImageView imageView,
CollageStrategy collageStrategy, UUID uuid) {
Log.d("loadCollageImpl", "download");
List<Bitmap> bitmapList = new ArrayList<>();
int size = Math.min(collageStrategy.numOfImage(), urls.size());
for (int i = 0; i < size; i++) {
bitmapList.add(getBitmapFromURL(urls.get(i)));
}
Bitmap bitmap = collageStrategy.create(bitmapList);
if (imageView != null && imageView.getTag(R.id.img_in_list).equals(uuid)) {
imageView.post(() -> {
imageView.setImageDrawable(null);
imageView.setImageBitmap(bitmap);
});
}
}

private static Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (IOException e) {
Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
int[] colors = {Color.BLACK, Color.BLUE, Color.GREEN, Color.RED};
Random random = new Random();
int ind = Math.abs(random.nextInt()) % colors.length;
Paint paint = new Paint();
paint.setColor(colors[ind]);
canvas.drawCircle(150, 150, 150, paint);
canvas.save();
return bitmap;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,97 @@
package ru.yandex.yamblz.ui.fragments;

import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;

import org.json.JSONArray;
import org.json.JSONException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import ru.yandex.yamblz.R;
import ru.yandex.yamblz.loader.CollageStrategyImpl;
import ru.yandex.yamblz.ui.fragments.Genre.Artist;
import ru.yandex.yamblz.ui.fragments.Genre.DownloadArtistsTask;
import ru.yandex.yamblz.ui.fragments.Genre.Genre;
import ru.yandex.yamblz.ui.fragments.Genre.GenreAdapter;
import ru.yandex.yamblz.ui.fragments.Genre.GenreOnScrollListener;

public class ContentFragment extends BaseFragment {
private static final String urlStr = "http://download.cdn.yandex.net/mobilization-2016/artists.json";
ImageView imageView;

@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_content, container, false);
RecyclerView recyclerView = (RecyclerView)inflater.inflate(R.layout.fragment_content, container, false);
List<Genre> genres = getGenres();
RecyclerView.Adapter adapter = new GenreAdapter(genres, super.getActivity());
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new GenreOnScrollListener());
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return recyclerView;
}

private List<Genre> getGenres() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Заюзай method profiling, посмотри на сколько этот метод блокирует main thread

List<Genre> genres = new ArrayList<>();
List<Artist> artists = null;
SharedPreferences sharedPreferences = getActivity().getApplicationContext().getSharedPreferences("genres", 0);
String artistsJSON = sharedPreferences.getString("artistsJSON", null);
if (artistsJSON == null) {
Log.d("Main list fragment", "download");
DownloadArtistsTask downloadArtists = new DownloadArtistsTask();
downloadArtists.execute(urlStr);
try {
artistsJSON = downloadArtists.get();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Правильно понимаю что мы ждем результата асинктаска на главном потоке?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@metrologma Да, но это только при первом запуске, просто непонятно ,что тогда показывать, если у нас еще нет списка артистов. Последующие запуски будут брать из sharedPreferences. И я там запушил примерно в тоже время, пока ты смотрел, к примеру сейчас я уже отменяю таски, если приходят новые на теже вьюхи. Можешь сейчас посмотреть, падает ли по OutOfMemoryError ? Просто сейчас я у себя запускаю, больше 10Мб Allocated не бывает.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Самое главное в задании с лоадером - чтобы не мелькали картинки (отменять операции), не падать на оом, спрятать всю логику за интерфейсом (поток должен внутри создаваться)

} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("artistsJSON", artistsJSON);
editor.apply();
}
try {
artists = Artist.fromJSON(new JSONArray(artistsJSON));
} catch (JSONException e) {
e.printStackTrace();
}

Map<String, Genre> genreMap = new HashMap<>();

assert artists != null;
for (Artist artist: artists) {
for(String genre: artist.getGenres()) {
if (!genreMap.containsKey(genre)) {
Genre genreObj = new Genre(genre);
genreMap.put(genre, genreObj);
}
genreMap.get(genre).addArtist(artist);
}
}

genres.addAll(genreMap.values());

return genres;
}
}
Loading