Форум программистов, компьютерный форум, киберфорум
Наши страницы
Программирование Android
Войти
Регистрация
Восстановить пароль
 
Araikovich
10 / 9 / 1
Регистрация: 09.08.2017
Сообщений: 62
1

Тема утечек памяти

18.02.2018, 01:51. Просмотров 325. Ответов 3

Всем привет. Прошу проконсультировать по поводу утечек памяти, а именно, когда утекает Активити. У меня есть Активность, презентер, и модель, которая работает с БД. Как известно, для работы с DBHelper - нужен Context. Я создаю в Активити объект модели, которой и передается Context. И вот мой вопрос, при уничтожении этой самой активити не утекает ли память, ведь ее контекст у модели. Ниже я приведу код, чтоб стало понятнее

Activity:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
public class HairstyleCategoryActivity extends MvpAppCompatActivity implements HairstyleCategoryActivityView {
 
    @InjectPresenter
    HairstyleCategoryActivityPresenter presenter;
 
    Toolbar toolbar;
    RecyclerView recyclerView;
    ProgressBar progressBar;
    HairstyleCategoryRecyclerAdapter adapter;
    CircleImageView refresh;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hairstyle_category);
        initView();
    }
 
    private void initView(){
        toolbar = (Toolbar)findViewById(R.id.hairstyle_categories_toolbar);
        recyclerView = (RecyclerView)findViewById(R.id.hairstyles_category_recycler_view);
        progressBar = (ProgressBar)findViewById(R.id.hairstyle_categories_progressbar);
        refresh = (CircleImageView)findViewById(R.id.hairstyle_categories_refresh);
 
        //set up toolbar;
        final Intent intent = getIntent();
        presenter.changetitle(intent.getStringExtra("title"));
        toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_back));
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });
 
        refresh.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.setCards(intent.getIntExtra("genderId", 0));
            }
        });
 
        //set up recycler view
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        if(adapter == null){
            adapter = new HairstyleCategoryRecyclerAdapter(this);
        }else {
            adapter.setContext(this);
        }
        recyclerView.setAdapter(adapter);
 
        adapter.setOnClickListener(new CategoryOnClickListener() {
            @Override
            public void onClick(int cardId, String title) {
                presenter.next(cardId, title);
            }
        });
 
        //getting model
        //
       //Вот в этом месте я и переживаю за утечку...
       //
 
        ModelComponent component = DaggerModelComponent.builder().contextModule(new ContextModule(this)).build();
        HairstyleCategoryModel model = component.getHairstyleCategoryModel();
        presenter.setModel(model);
        //load data
        if (!adapter.isLoaded())
            presenter.setCards(intent.getIntExtra("genderId", 0));
    }
 
    @Override
    public void setProgressBar(int visibility) {
        progressBar.setVisibility(visibility);
    }
 
    @Override
    public void setAdapter(ArrayList<HairstyleCategoryCard> cards) {
        adapter.setCards(cards);
    }
 
    @Override
    public void setTitle(String title) {
        toolbar.setTitle(title);
    }
 
    @Override
    public void showToast(String toast) {
        Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void showRefresh(int visibility) {
        refresh.setVisibility(visibility);
    }
 
    @Override
    public void nextActivity(int hairstyleId, String hairstyleTitle) {
        Intent intent = new Intent(HairstyleCategoryActivity.this, HairstyleDetailActivity.class);
        intent.putExtra("hairstyleId", hairstyleId);
        intent.putExtra("hairstyleTitle", hairstyleTitle);
        startActivity(intent);
    }
 
}
Presenter:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@InjectViewState
public class HairstyleCategoryActivityPresenter extends MvpPresenter<HairstyleCategoryActivityView> {
 
    private HairstyleCategoryModel model;
 
    public void setModel(HairstyleCategoryModel model){
        if(this.model == null)
        this.model = model;
    }
 
    public void setCards(final int genderId){
        if(model.isIsdLoading())
            return;
        getViewState().showRefresh(View.GONE);
        model.loadCategoriesFromServer(genderId, new Callback<ArrayList<HairstyleCategoryCard>>() {
            @Override
            public void onResponse(Call<ArrayList<HairstyleCategoryCard>> call, Response<ArrayList<HairstyleCategoryCard>> response) {
                getViewState().setAdapter(response.body());
                getViewState().setProgressBar(View.GONE);
                model.saveCardToDb(response.body());
            }
 
            @Override
            public void onFailure(Call<ArrayList<HairstyleCategoryCard>> call, Throwable t) {
                model.getCardsFromDb(genderId, new LoadCallBack<ArrayList<HairstyleCategoryCard>>() {
                    @Override
                    public void onLoadSuccess(ArrayList<HairstyleCategoryCard> result) {
                        getViewState().setAdapter(result);
                        getViewState().setProgressBar(View.GONE);
                    }
 
                    @Override
                    public void onFail(String message) {
                        getViewState().setProgressBar(View.GONE);
                        getViewState().showRefresh(View.VISIBLE);
                        getViewState().showToast("Пробдемы с сетью, попробуйте еще раз");
                    }
                });
            }
        });
    }
 
    public void changetitle(String title){
        getViewState().setTitle(title);
    }
 
    public void next(int hairstyleId, String hairstyleTitle){
        getViewState().nextActivity(hairstyleId, hairstyleTitle);
    }
 
}
И Model, которая как раз зависима от Context:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public class HairstyleCategoryModel {
 
    private ServerApi api;
    private DBHelper db;
    private boolean isdLoading = false;
 
    public HairstyleCategoryModel(ServerApi api, DBHelper db){
        this.api = api;
        this.db = db;
    }
 
    public void loadCategoriesFromServer(int genderId, Callback callback){
        Call<ArrayList<HairstyleCategoryCard>> call = api.getHairstyleCategories(genderId);
        call.enqueue(callback);
    }
 
    public void saveCardToDb(ArrayList<HairstyleCategoryCard> cards){
        SaveCategoryCardsTask task = new SaveCategoryCardsTask();
        task.execute(cards);
    }
 
    public void getCardsFromDb(int genderId, LoadCallBack callBack){
        LoadCategoryCardsTask task = new LoadCategoryCardsTask(genderId, callBack);
        task.execute();
    }
 
    public boolean isIsdLoading(){
        return isdLoading;
    }
 
    class SaveCategoryCardsTask extends AsyncTask<ArrayList<HairstyleCategoryCard>, Void, Void> {
 
        @Override
        protected Void doInBackground(ArrayList<HairstyleCategoryCard>... params) {
            ContentValues contentValues = new ContentValues();
            SQLiteDatabase database = db.getWritableDatabase();
            database.delete("category", null, null);
            for(int i = 0; i<params[0].size(); i++){
                contentValues.put("id", params[0].get(i).getId());
                contentValues.put("genderId", params[0].get(i).getSexId());
                contentValues.put("title", params[0].get(i).getTitle());
                contentValues.put("imageUrl", params[0].get(i).getImageUrl());
                database.insert("category", null, contentValues);
                contentValues.clear();
            }
            return null;
        }
 
    }
 
    class LoadCategoryCardsTask extends AsyncTask<Void, Void, ArrayList<HairstyleCategoryCard>>{
 
        LoadCallBack callBack;
        int genderId;
 
        public LoadCategoryCardsTask(int genderId, LoadCallBack callBack){
            this.callBack = callBack;
            this.genderId = genderId;
        }
 
        @Override
        protected ArrayList<HairstyleCategoryCard> doInBackground(Void... params) {
            isdLoading = true;
            ArrayList<HairstyleCategoryCard> cards = new ArrayList<>();
            Cursor cursor = db.getReadableDatabase().query("category", null, null, null, null, null, null);
            while (cursor.moveToNext()){
                if(cursor.getInt(cursor.getColumnIndex("genderId")) != genderId){
                    return null;
                }
                HairstyleCategoryCard card = new HairstyleCategoryCard();
                card.setSexId(cursor.getInt(cursor.getColumnIndex("genderId")));
                card.setSexId(cursor.getInt(cursor.getColumnIndex("id")));
                card.setTitle(cursor.getString(cursor.getColumnIndex("title")));
                card.setImageUrl(cursor.getString(cursor.getColumnIndex("imageUrl")));
                cards.add(card);
            }
            cursor.close();
            return cards;
        }
 
        @Override
        protected void onPostExecute(ArrayList<HairstyleCategoryCard> cards) {
            super.onPostExecute(cards);
            isdLoading = false;
            if(cards!= null && cards.size() > 0)
                callBack.onLoadSuccess(cards);
            else
                callBack.onFail("Null table");
        }
    }
}
Проще говоря, прошу проверить опытных программистов на тему утечки памяти. Спасибо)
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
18.02.2018, 01:51
Ответы с готовыми решениями:

Определение утечек памяти (Android Studio)
Как можно в AS 1.5 определить строчку кода которая приводит к утечке памяти(не...

Тема на Диплом
Подскажите какую ни будь тему с программированием на Android.

Тема магистерской дисертации
Привет всем. Может кто подскажет интересную тему для дисера, связанную с...

Не применяется тема с Eclipse
Привет, почему-то не применяются темы, в Eclipse выбираю, но не ставиться. Не...

Тема дипломной работы
Всем привет! Тему диплома пока не выбрал, но есть огромный интерес к созданию...

3
demixdn
310 / 255 / 79
Регистрация: 31.10.2016
Сообщений: 619
19.02.2018, 13:37 2
Araikovich, есть 2 вида необходимости контекста: контекст от приложения, и контекст от активити.
Контекст от приложения нужен для баз данных, контент провайдера, извлечения ресурсов приложения таких как строки, цвета, все то что лежит в res/ и других системных вещей.
Контекст от активити нужен для отображения UI компонентов, например для алерт диалога, view inflate и других UI вещей.
Причем контекст от активити можно использовать как системный, он отработает как нужно. Но если использовать контекст приложения для алерта, то могут быть проблемы с темой приложения, если она другая от активти. Короче есть нюансы и их нужно знать.
К чему я веду. Да, ваш код ведет к утечке актвити. Вы передаете контекст актвити внутрь своих инекций, и скорей всего он будет использоваться для формирования БД и когда асинк такси будут работать, они утянут этот контекст.
Java
1
2
ModelComponent component = DaggerModelComponent.builder().contextModule(new ContextModule(this)).build();
        HairstyleCategoryModel model = component.getHairstyleCategoryModel();
вот этой херни не должно быть в Activity. ContextModule должен формироваться в Application.
Java
1
DaggerModelComponent.builder().contextModule(new ContextModule(this)).build();
не должно вызываться нигде кроме как в Application. Вы же делаете dependency injection и сразу же в актвити раcкрываете детали реализации. WTF?
Java
1
HairstyleCategoryModel model = component.getHairstyleCategoryModel();
не должно быть в принципе. HairstyleCategoryModel должна попасть в конструктор вашего презентера с использованием di инъекции, не через сеттер.
ну а класс HairstyleCategoryModel это нечто особенное, но может это ваш стиль, не буду придираться.
2
Araikovich
10 / 9 / 1
Регистрация: 09.08.2017
Сообщений: 62
19.02.2018, 22:45  [ТС] 3
demixdn, Спасибо огромное! Я через monitors в итоге обнаружил утечки. Насчет HairstyleCategoryModel -
это говно. Но я не знаю, как правильно. Может поделитесь ссылками на хорошие статьи о том, как проектировать model, и о di
0
demixdn
310 / 255 / 79
Регистрация: 31.10.2016
Сообщений: 619
20.02.2018, 17:16 4
Araikovich, вот весьма приличный курс по архитектуре от e-legion. тут еще точка входа в сообщество "чистой" архитектуры. есть еще куча разрозненных статей об архитектуре, но вам того что дал достаточно для того чтоб войти в тему.
2
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
20.02.2018, 17:16

Тема для дипломной работы
Скоро уже нужно определиться с темой для бакалаврской работы,хотелось бы какое...

Нужна тема для проекта
Доброго всем времени суток! Требуется разработать приложение под Андроид на...

Тема приложения не распространяется на активити
Здравствуйте уважаемые программисты! Не могли бы вы мне помочь? Дело в том,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru