Страницы

Поиск по вопросам

Показаны сообщения с ярлыком sqlite. Показать все сообщения
Показаны сообщения с ярлыком sqlite. Показать все сообщения

среда, 15 апреля 2020 г.

Размещение созданной БД SQLite в проекте под Android

#android #java #sqlite #база_данных

                    
Здравствуйте. Пишу небольшой справочник, в основе которого будет база данных. Файл
БД создан на стороне средствами специального ПО. Данный файл нужно подгрузить в приложение
Android. Каким образом размещать файл, каков путь для доступа к внедряемой БД, какие
методы следует реализовать для обеспечения подключения и дальнейшей работы с данной
БД в приложении?    


Ответы

Ответ 1



Если ещё не читали, то будет полезна статья на Хабре «Правильная работа с БД в Android». Там же есть ссылка на тему прикрепления БД. Сам давно уже использую этот подход. Меня устраивает. Обсуждение тонкостей также производилось в вопросе «Правильная работа с SQLite-базой в Android».

Ответ 2



Базу на флешку. В приложении сделать выбор пути к базе.

четверг, 9 апреля 2020 г.

Библиотека для отображения данных из sqlite

#java #android #sqlite #android_layout

                    
Подскажите библиотеку для табличного отображения данных, максимально по виду и функционалу
приближенную к DBGrid из Delphi или Майкрософтовским DataGrid.
    


Ответы

Ответ 1



используйте TableLayout вот пример activity_main.xml

вторник, 7 апреля 2020 г.

cursor.requery is deprecated что теперь делать?

#java #android #sqlite

                    
После удалении из БД данных надо обновить ListView чтобы отображать изменении. Но
Студия говорит что requery() is deprecated . Какую функцию можно использовать вместе
него? Вот здесь говорят что надо повторно вызвать курсор. Но я не понял как это надо
делать. 

UPD: Скопировал первый код и заново вызвал курсор этим кодом не помогло. 

cursor =  mSqLiteDatabase.rawQuery("select * from "+ DatabaseHelper.DATABASE_TABLE,
null);

    


Ответы

Ответ 1



Решил проблему таким образом. Сначала повторно вызвал курсор тем кодом которым он был создан. cursor = mSqLiteDatabase.rawQuery("select * from "+ DatabaseHelper.DATABASE_TABLE, null); Потом использовал функции SimpleCursorAdapter : userAdapter.swapCursor(cursor); userAdapter.notifyDataSetChanged(); Работает как надо. Первым делом вызываем курсор снова потом используем вышеперечисленные методы. Всё вместе выглядит так: cursor = mSqLiteDatabase.rawQuery("select * from "+ DatabaseHelper.DATABASE_TABLE, null); userAdapter.swapCursor(cursor); userAdapter.notifyDataSetChanged(); PS1. В решении проблемы помогла вот это PS2. Если у вас нет SimpleCursorAdapter надо его создать чем то похожим на вот это: userAdapter = new SimpleCursorAdapter(this, R.layout.siyahi_obyekti, cursor, headers, new int[]{R.id.textView5, R.id.textView6,R.id.textView7}, 0); siyahi.setAdapter(userAdapter);

Ответ 2



Вы не поняли главной цели Гугла (Андроида) по изменению политики к курсорам. Идея состоит в том, чтобы вывести процесс загрузки курсора из основного потока, в фоновый, который бы не тормозил работу приложения для пользователя. ДА, Вы воспользовались командой swapCursor(cursor); но это как к японскому автомобилю березовая тяга. Эта команда предназначена для того, чтобы обновить данные в основном потоке на УЖЕ загруженный в фоновом режиме курсор. Изменения работы с курсором - большая тема, и там нельзя отделаться несколькими командами. Советую изучить КурсорЛоудеры или, хотя бы ассинхронные задачи, это первое. А второе - Контент Провайдеры, если нужно обмениваться инфо с другими приложениями. Хотя фанаты лепят их везде.

SQLite производительность

#java #android #sql #sqlite

                    
Есть ли разница в скорости получения данных из таблицы SQLite на Android:

Первый вариант:

    ArrayList arrayList;
    Cursor c = db.rawQuery("select a,b,c from table");
    for (int i = 0; i < c.getCount(); i++) {
        arrayList.add(c.getString(0));
    }


Второй вариант:

    ArrayList arrayList;
    for(int i=0; i < count; i++){
        Cursor c = db.rawQuery("select a,b,c from table where a = " + i)
        String a = c.getString(0);
        arrayList.add(a);
    }


Мне нужен второй способ, но он вызывает у меня сомнения. Если он тратит больше времени,
то есть ли другой, более быстрый, но чтобы данные получались последовательно, вместо
возвращения целого курсора с данными?
    


Ответы

Ответ 1



Второй вариант вероятно будет работать гораздо медленнее. Если есть возможность получить все данные одним обращением к БД(1 вариант), т.к. зачем вы намеренно обращаетесь к бд множество раз(2 вариант)? А вообще просто сравните скорость выполнения первого и второго способа на большом объёме данных. Меня только смущает в вашем втором варианте i<10. Первый вариант должен выглядеть видимо так: ArrayList arrayList; Cursor c = db.rawQuery("select a,b,c from table where a>=0 and a<10"); for (int i = 0; i < c.getCount(); i++) { arrayList.add(c.getString(0)); }

Ответ 2



Я бы сделал проверку if(c.moveToFirst()), странно что вы эту команду не вызываете. А в цикле нужно использовать c.moveToNext().

суббота, 21 марта 2020 г.

Как передать именованный аргумент sqlite?

#python #python_3x #sqlite #sqlite3


Не могу разобраться, как мне передать именованный аргумент (переменную) в базу данных.

Создал базу данных следующим образом:

import sqlite3

conn = sqlite3.connect("mydatabase.db")
cursor = conn.cursor()

cursor.execute("CREATE TABLE users (id numeric)") #пробовал и текстовое значение


Пытался вставить переменную так:

ids = [] #заполненный список с значениями, которые мне нужно вставить
for i in ids:
   cursor.execute("INSERT INTO users VALUES (?,)", i) #пробовал через форматирование
строки, тоже не вышло


Что я делаю не так?
    


Ответы

Ответ 1



cursor.execute(sql, parameters) в качестве параметров ожидает итерируемый объект (обычно список или кортеж): ids = [1,2,3] for i in ids: cursor.execute("INSERT INTO users VALUES (?)", (i,)) # qmark style # NOTE: ----------------------------> ^^^ ^^^^ используем именованные параметры: for i in ids: cursor.execute("INSERT INTO users VALUES (:id)", {"id": i}) # (named style) ВНИМАНИЕ: (<скаляр>) - не является кортежем! In [55]: i = 1 In [56]: type( i ) Out[56]: int In [57]: type( (i) ) # / NOTE! Out[57]: int # \ (i) - это просто скаляр, а не кортеж In [58]: type( (i,) ) Out[58]: tuple

четверг, 19 марта 2020 г.

Как лучше сделать динамическую ссылку на SQLite

#c_sharp #sqlite


У SQLite есть версии для x86 и x64. Компьютеры и Windows у нас тоже есть разные.
Нужно, чтобы всё работало и собиралось на компьютерах разных разработчиков с разными CPU.
Пока что у меня есть только такое решение: не ссылаться из сборок на SQLite, а загружать
сборки динамически в зависимости от CPU, а для вызова свойств и методов использовать
тип dynamic. Но это как-то криво выглядит.    


Ответы

Ответ 1



Any CPU не катит по описаным автором причинам. Я использовал кусок кода в самом файле проекта (*.csproj), который в зависимости от архитектуры CPU добавляет ссылку нужной разрядности. Проект найти не могу, но что то типа такого: False ..\..\Lib\SQLite\x86\System.Data.SQLite.dll То есть по умолчанию добавляется например ссылка на х86 и небольшое условие, которое обновит сссылку на другую, если необходимо. В примере только наоборот.

Ответ 2



Экстремальное решение - перейти с SQLite на C#-SQLite. В этом случае, проблемы x32-x64 уже не будут актуальны.

Ответ 3



А почему нельзя использовать вариант Any CPU? Или х86 всегда, ведь х64 достаточно сносно работают с х86 UPD Приходилось работать с библиотекой System.Data.SQLite.dll (x86) тестировал и на х64 и на х86. Правда была какая то проблема, не помню уже, разрешилась дописанием одной строчки в app.config

пятница, 13 марта 2020 г.

Как убрать кавычки в JSON?

#android #json #sqlite


У меня есть БД из нее получаю JSON следующим способом

    String DB_PATH = "/data/data/com.example.apps/databases/";
    String myPath = DB_PATH + DBHelper.DATABASE_NAME;// Set path to your database
    String myTable = DBHelper.TABLE_CONTACT;//Set name of your table
    SQLiteDatabase myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    String searchQuery = "SELECT  * FROM " + myTable;
    Cursor cursor = myDataBase.rawQuery(searchQuery, null );

    JSONArray json = new JSONArray();
    cursor.moveToFirst();
    while (cursor.isAfterLast() == false) {
        int totalColumn = cursor.getColumnCount();
        JSONObject rowObject = new JSONObject();

        for(int i=0 ;  i< totalColumn ; i++) {
            if(cursor.getColumnName(i) != null) {
                try {
                    if(cursor.getString(i) != null) {
                        Log.d("TAG_NAME", cursor.getString(i));
                        rowObject.put(cursor.getColumnName(i), cursor.getString(i));
                    } else {
                        rowObject.put(cursor.getColumnName(i), "");
                    }
                }
                catch(Exception e) {
                    Log.d("TAG_NAME", e.getMessage());
                }
            }
        }
        json.put(rowObject);
        cursor.moveToNext();
    }
    cursor.close();
    Log.d("TAG_NAME_ALL", json.toString());
    return json.toString();


JSON получается какой необходим, за исключением одного значения latlng.

Я получаю - "latlng":"[54.601530521,55.918132215051]"

Нужно получить - "latlng":[54.601530521,55.918132215051]

Как избавиться от кавычек?
    


Ответы

Ответ 1



из комментарий и оригинал из stackoverflow.com Если вам нужно быстро перевести строку на double, хоть и с грязным кодом, тогда вы можете сделать так: String s = "[12.3456789,98.7654321]"; String trimmed = s.substring(1, s.length()-1); String[] parts = trimmed.split(","); double lat = Double.parseDouble(parts[0]); double lng = Double.parseDouble(parts[1]); Log.d("latlng", "lat="+lat+" lng="+lng); Это не красиво, но работает.

понедельник, 9 марта 2020 г.

Как добавить в приложение уже заполненную базу?

#android #sqlite


Есть приложение, в нем есть список чего-то на 200 итемов.

Как мне поставлять приложение с уже заполненной базой ?
т.е. чтобы после установки приложения этот список заполнился из базы.
    


Ответы

Ответ 1



Схематично: В assets кладете Вашу БД Создаете БД в приложении (стандартно SQLiteOpenHelper) Копируете при первом запуске БД из assets в БД приложения. Работаете с заполненной БД. После очистки данных приложения пользователем повторить п.3

воскресенье, 8 марта 2020 г.

Отсутствует таблица в SQLite

#java #android #sqlite



  android.database.sqlite.SQLiteException: no such table: RESUME (code
  1): , while compiling: SELECT DISTINCT * FROM RESUME


Потому что в предыдущей версии этой таблицы "RESUME" не было. Щас добавил и запустил
и получил эту ошибку. 

Как исправить эту ошибку (кроме удаления приложения и заново запустить или очистки кэша)?
    


Ответы

Ответ 1



Почему никто не читает документацию? Создайте дополнительную таблицу в методе SQLiteOpenHelper.onUpgrade(). public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < 2) { db.execSQL("create table resume ..."); } }

суббота, 7 марта 2020 г.

Нужно ли проверять SQLiteDatabase db на null?

#android #sqlite


В своем андроид-приложении ни разу не проверял db != null, при этом запросы работают.
Прочитал, что надо проверять, к примеру, как в следующем коде.

SQLiteDatabase db = new ContactDbHelper(
                        getApplicationContext()).getWritableDatabase();
if (db != null) {
                 Toast.makeText(getApplicationContext(),
                 "DB Contacts is created", Toast.LENGTH_LONG).show();
}else {
                 Toast.makeText(getApplicationContext(),
                 "Error create database!", Toast.LENGTH_LONG).show();
}


Вопрос такой, нужно ли проверять SQLiteDatabase db на null? Это просто подстраховка
и если да, по каким причинам БД может не создаться, при условии, что код рабочий? 
    


Ответы

Ответ 1



Код открытия БД ищет БД по пути задаваемому методом Context.getDatabasePath(), легко может случиться, что какая-то сволочь однажды решит перегрузить этот метод в своем Activity и указать путь на внешний носитель - тогда вас будет ждать легкий Überraschung :) Так что проверять надо все таки.

среда, 4 марта 2020 г.

Вшить SQLite.Interop в exe с помощью Costura.Fody

#c_sharp #sqlite


После использования Costura.Fody почти все dll пошли в exe, но пару dll, которые
в папках, не могу закинуть в exe.

В чём проблема ?
(в папках 2 dll от Sqlite)

    


Ответы

Ответ 1



Это нативные библиотеки, поэтому нужно указывать явно, что вы их тоже хотите вшить в exe. 1. Нужно указать названия (только названия, включать расширение файлов не нужно) библиотек в файле FodyWeavers.xml. SQLite.Interop SQLite.Interop 2. Создать в проекте папки costura32 и costura64 и поместить в них соответствующие версии библиотечных файлов. 3. Указать действие при построении (Build Action) - встраиваемый ресурс (Embedded Resource) Результат должен получиться примерно следующим Примечание. Картинка взята из статьи Including SQLite.Interop.dll into your C# project Более подробную информацию можно найти в README на странице проекта на гитхабе

воскресенье, 1 марта 2020 г.

Ошибка OutOfMemoryError при работе с изображениями

#java #android #sqlite #listview


Делаю приложение, одним из составляющих которого является список, получающий данные
для отображения из базы данных. Элемент списка состоит из маленького изображения и
текста. В бд хранится текст и uri для получения определенного изображения из папки
drawable. При нажатии на элемент списка открывается активити, где, в том числе, показывается
то самое определенное изображение. Также имеются ещё пару активити со списками и изображениями
и текстовыми данными (всё берется из бд).  

Проблема в следующем: по мере открытия новых активити, прокручивании списков, возврата
к старым активити постоянно возрастает объём занимаемой приложением ОЗУ, и по достижении
определенного момента приложение вылетает, выдавая OutOfMemoryError.  

Пробовал получать Cursor через AsyncTask, но на данной ситуации это никак не отразилось.
Судя по Java Heap происходит утечка памяти, но не совсем понимаю с чем она связана.
 Может кто знает как исправить? Заранее благодарю  

Далее прилагаю код одного из активити со списком, логи и скрин Java Heap 

import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;



public class MyActivity extends AppCompatActivity {

    MyDBHelper myDBHelper;
    SQLiteDatabase sqLiteDatabase;
    Cursor cursor;
    ListView listView;
    SimpleCursorAdapter simpleCursorAdapter;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        listView = (ListView) findViewById(R.id.list_of_vyzi);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position,
long id) {
                Intent intent = new Intent(getApplicationContext(), UniversityMainActivity.class);
                intent.putExtra("_id", id);

                startActivity(intent);
            }
        });


        // открываем подключение
        myDBHelper = new MyDBHelper(getApplicationContext());
        sqLiteDatabase = myDBHelper.open();

        //получаем данные из бд в виде курсора
        cursor =  sqLiteDatabase.query(TablesNames.UNIVERSITIES,
                new String[] {UniMainInfoConsts.ID,
                        UniMainInfoConsts.IMG_SRC, MyDBHelper.COLUMN_UNI,
                        MyDBHelper.COLUMN_BRI}, null, null,null,null,null);

        // определяем, какие столбцы из курсора будут выводиться в ListView
        cursor.moveToFirst();
        String[] headers = new String[] {UniMainInfoConsts.IMG_SRC, MyDBHelper.COLUMN_UNI,
MyDBHelper.COLUMN_BRI};
        // создаем адаптер, передаем в него курсор
        simpleCursorAdapter = new SimpleCursorAdapter(this, R.layout.layout_for_list,
                cursor, headers, new int[]{R.id.imageView, R.id.main_text, R.id.sub_text}, 0);

        listView.setAdapter(simpleCursorAdapter);
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        // Закрываем подключение и курсор
        sqLiteDatabase.close();
        cursor.close();
        myDBHelper.close();
        Log.d("Destr", "List destr");
    }


    public void openAddComment (View v){
        Intent intent = new Intent(this, Add_comment_activity.class);
        startActivity(intent);
    }



}


Java Heap



    04-28 22:22:35.744 5511-5511/ru.abityrienty.vyzi I/Choreographer: Skipped 30
frames!  The application may be doing too much work on its main thread.
04-28 22:22:37.365 5511-5511/ru.abityrienty.vyzi I/PersonaManager: getPersonaService()
name persona_policy
04-28 22:22:37.445 5511-5511/ru.abityrienty.vyzi D/AbsListView: Get MotionRecognitionManager
04-28 22:22:38.877 5511-5511/ru.abityrienty.vyzi D/DESTROY: InstPage has destroyed
04-28 22:22:38.877 5511-5511/ru.abityrienty.vyzi D/AbsListView: onDetachedFromWindow
04-28 22:22:39.558 5511-5511/ru.abityrienty.vyzi D/AbsListView: Get MotionRecognitionManager
04-28 22:22:39.598 5511-5511/ru.abityrienty.vyzi I/PersonaManager: getPersonaService()
name persona_policy
04-28 22:22:40.218 5511-5511/ru.abityrienty.vyzi D/DESTROY: InstPage has destroyed
04-28 22:22:40.228 5511-5511/ru.abityrienty.vyzi D/AbsListView: onDetachedFromWindow
04-28 22:22:41.249 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 17393K,
23% free 60720K/78840K, paused 22ms, total 26ms
04-28 22:22:41.370 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 7174K,
31% free 55185K/78840K, paused 20ms, total 20ms
04-28 22:22:41.530 5511-5511/ru.abityrienty.vyzi I/dalvikvm-heap: Grow heap (frag
case) to 74.282MB for 18662416-byte allocation
freed 17393K, 23% free 60720K/78840K, paused 22ms, total 26ms
04-28 22:22:41.370 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 7174K,
31% free 55185K/78840K, paused 20ms, total 20ms
04-28 22:22:41.530 5511-5511/ru.abityrienty.vyzi I/dalvikvm-heap: Grow heap (frag
case) to 74.282MB for 18662416-byte allocation
04-28 22:22:42.791 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 25168K,
23% free 61188K/78828K, paused 38ms, total 38ms
04-28 22:22:42.811 5511-5511/ru.abityrienty.vyzi I/dalvikvm-heap: Grow heap (frag
case) to 69.961MB for 7985680-byte allocation
04-28 22:22:42.951 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 3595K,
23% free 66797K/86628K, paused 19ms, total 19ms
04-28 22:22:44.683 5511-5511/ru.abityrienty.vyzi I/dalvikvm-heap: Forcing collection
of SoftReferences for 18662416-byte allocation
04-28 22:22:44.723 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_BEFORE_OOM freed
23183K, 26% free 54168K/73080K, paused 37ms, total 38ms
04-28 22:22:46.105 5511-5511/ru.abityrienty.vyzi D/AbsListView: Get MotionRecognitionManager
04-28 22:22:46.135 5511-5511/ru.abityrienty.vyzi I/PersonaManager: getPersonaService()
name persona_policy
04-28 22:22:47.516 5511-5511/ru.abityrienty.vyzi D/DESTROY: InstPage has destroyed
04-28 22:22:47.516 5511-5511/ru.abityrienty.vyzi D/AbsListView: onDetachedFromWindow
04-28 22:22:48.597 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 19896K,
9% free 66731K/73080K, paused 25ms, total 26ms
04-28 22:22:48.617 5511-5511/ru.abityrienty.vyzi I/dalvikvm-heap: Grow heap (frag
case) to 75.380MB for 7985680-byte allocation
04-28 22:22:48.687 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_FOR_ALLOC freed 1165K,
8% free 74770K/80880K, paused 19ms, total 19ms
04-28 22:22:49.518 5511-5511/ru.abityrienty.vyzi I/dalvikvm-heap: Forcing collection
of SoftReferences for 18662416-byte allocation
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi D/dalvikvm: GC_BEFORE_OOM freed
25070K, 32% free 59775K/87240K, paused 24ms, total 25ms
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi E/dalvikvm-heap: Out of memory on
a 18662416-byte allocation.
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm: "main" prio=5 tid=1 RUNNABLE
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:   | group="main" sCount=0
dsCount=0 obj=0x41855ea0 self=0x4174b050
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:   | sysTid=5511 nice=0
sched=0/0 cgrp=apps handle=1073848660
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:   | state=R schedstat=(
0 0 0 ) utm=532 stm=230 core=0
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.graphics.BitmapFactory.nativeDecodeAsset(Native
Method)
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:693)
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:518)
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.content.res.Resources.loadDrawable(Resources.java:3457)
04-28 22:22:49.548 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.content.res.Resources.getDrawable(Resources.java:1921)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.ImageView.resolveUri(ImageView.java:666)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.ImageView.setImageURI(ImageView.java:409)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.support.v4.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:201)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.support.v4.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:147)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:273)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.AbsListView.obtainView(AbsListView.java:2713)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.ListView.makeAndAddView(ListView.java:1811)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.ListView.fillDown(ListView.java:697)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.ListView.fillGap(ListView.java:661)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.AbsListView.trackMotionScroll(AbsListView.java:6693)
04-28 22:22:49.558 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:5692)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:813)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.view.Choreographer.doCallbacks(Choreographer.java:613)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.view.Choreographer.doFrame(Choreographer.java:582)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:799)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.os.Handler.handleCallback(Handler.java:733)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.os.Handler.dispatchMessage(Handler.java:95)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.os.Looper.loop(Looper.java:146)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at android.app.ActivityThread.main(ActivityThread.java:5593)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at java.lang.reflect.Method.invokeNative(Native
Method)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at java.lang.reflect.Method.invoke(Method.java:515)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi I/dalvikvm:     at dalvik.system.NativeStart.main(Native
Method)
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi D/skia: --- allocation failed for
scaled bitmap
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi D/AndroidRuntime: Shutting down VM
04-28 22:22:49.568 5511-5511/ru.abityrienty.vyzi W/dalvikvm: threadid=1: thread exiting
with uncaught exception (group=0x41854da0)
04-28 22:22:49.578 5511-5511/ru.abityrienty.vyzi E/AndroidRuntime: FATAL EXCEPTION: main
                                                                   Process: ru.abityrienty.vyzi,
PID: 5511
                                                                   java.lang.OutOfMemoryError
                                                                       at android.graphics.BitmapFactory.nativeDecodeAsset(Native
Method)
                                                                       at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:693)
                                                                       at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:518)
                                                                       at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
                                                                       at android.content.res.Resources.loadDrawable(Resources.java:3457)
                                                                       at android.content.res.Resources.getDrawable(Resources.java:1921)
                                                                       at android.widget.ImageView.resolveUri(ImageView.java:666)
                                                                       at android.widget.ImageView.setImageURI(ImageView.java:409)
                                                                       at android.support.v4.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:201)
                                                                       at android.support.v4.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:147)
                                                                       at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:273)
                                                                       at android.widget.AbsListView.obtainView(AbsListView.java:2713)
                                                                       at android.widget.ListView.makeAndAddView(ListView.java:1811)
                                                                       at android.widget.ListView.fillDown(ListView.java:697)
                                                                       at android.widget.ListView.fillGap(ListView.java:661)
                                                                       at android.widget.AbsListView.trackMotionScroll(AbsListView.java:6693)
                                                                       at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:5692)
                                                                       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:813)
                                                                       at android.view.Choreographer.doCallbacks(Choreographer.java:613)
                                                                       at android.view.Choreographer.doFrame(Choreographer.java:582)
                                                                       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:799)
                                                                       at android.os.Handler.handleCallback(Handler.java:733)
                                                                       at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                       at android.os.Looper.loop(Looper.java:146)
                                                                       at android.app.ActivityThread.main(ActivityThread.java:5593)
                                                                       at java.lang.reflect.Method.invokeNative(Native
Method)
                                                                       at java.lang.reflect.Method.invoke(Method.java:515)
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
                                                                       at dalvik.system.NativeStart.main(Native
Method)


Натыкался вот на такое, но не совсем понял как воплотить в жизнь:

The real solution involves:


Ensuring that you have profiled your app's allocations and are not leaking memory
Ensuring that you have profiled your app's allocations and are not
leaking memory
You are not holding references to Bitmaps via a hidden or
non-displayed Fragment, View, etc.. in such a way that they can not be collected
and released
You are using a Bitmap cache (i.e. LruCache) to manage how many
Bitmaps are been held in memory at any give time
That the Bitmaps that are actually being held in memory are sized
appropriately for the device's resolution, i.e.:
Resizing content on the device on the fly and file caching the
results
Dynamically via the web service that you are requesting them from

Переделанный адаптер для получения BLOB

import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.widget.SimpleCursorAdapter;
import android.widget.ImageView;

 public class ListOfVyziAdapter extends SimpleCursorAdapter {

/**
 * It's important to place column where you store images on the first place
 * of the @from array
 */

Context ctx;
// String [] columns = {0="img_src",1="for main_text", 2="for sub_text"}
String [] columns;


public ListOfVyziAdapter(Context context, int layout, Cursor c, String[] from, int[]
to, int flags) {
    super(context, layout, c, from, to, flags);
    ctx = context;
    columns = from;
}


@Override
public void setViewImage(ImageView v, String value) {

    v.setImageDrawable(imageForList(getCursor()));
}

private Drawable imageForList(Cursor cur){

    byte [] blob = cur.getBlob(cur.getColumnIndex(columns[0]));
    Bitmap bitmap = BitmapFactory.decodeByteArray(blob, 0, blob.length);
    return new BitmapDrawable(Resources.getSystem(), bitmap);
}
}


    


Ответы

Ответ 1



Для того чтоб бороться с OOM вначале вам необходимо создать такую возможность. Определите свой адаптер, иначе вам очень сложно(невозможно) контролировать процесс биндинга(bind) Blob из Sql в ImageView, а так у вас появится возможность контролировать размер изображения, применить компрессию, сделать кеширование, и самое главное учесть возможность выделения памяти для разных устройств. Для простоты можно использовать готовые решения, которые уже зарекомендовали себя например Glide там будет проще сделать, в Picasso не поддерживается load для byte[] или Bitmap, только Drawable и Url.

вторник, 25 февраля 2020 г.

Android - как ещё можно ускорить вставку в базу SQLite?

#android #java #sqlite


Здравствуйте! По совету из прошлого вопроса я не стал записывать нулевые ячейки в
базу, теперь объём записываемых данных уменьшился с 25 165 824 до 6 881 280. Вопрос:
можно ли ещё как-нибудь ускорить сохранение (сейчас оно занимает 140 секунд, хотелось
бы сократить это время хотя бы до минуты) этих данных в базу?
Вот мой код сохранения:

    void save()
    {
        final SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(path, null);
        String sql_w_table = 
            "CREATE TABLE w_data (" +
            "a LONG," +
            "b LONG," +
            "type LONG" +
            ");";
        database.execSQL(sql_w_table);

        String query = "INSERT INTO w_data VALUES (?,?,?);";
        SQLiteStatement st = database.compileStatement(query);

        database.beginTransaction();

        for (int i = 0; i <= 32767; i++)
        {
            for (int n=0; n <= 255; n++)
            {
                int b = map[i][n];

                if (b != 0)
                {
                    st.bindLong(1, i);
                    st.bindLong(2, n);
                    st.bindLong(3, b);
                    st.executeInsert();
                    st.clearBindings();
                }
            }
        }

        database.setTransactionSuccessful();
        database.endTransaction();
        database.close();           
    }
    


Ответы

Ответ 1



Предлагаю самый наивный способ - не сохранять "поячеечно", а "построчно". Попробую на коленке переписать (!!). void save() { final SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(path, null); String sql_w_table = "CREATE TABLE w_data (" + "a LONG," + "type TEXT" + ");"; database.execSQL(sql_w_table); String query = "INSERT INTO w_data VALUES (?,?);"; SQLiteStatement st = database.compileStatement(query); database.beginTransaction(); for (int i = 0; i <= 32767; i++) { String s = ''; for (int n=0; n <= 255; n++) { s += map[i][n] + " "; } st.bindLong(1, i); st.bindLong(2, s); st.executeInsert(); st.clearBindings(); } database.setTransactionSuccessful(); database.endTransaction(); database.close(); } суть - в базу сохраняем сразу по строке. Когда нужно будет распаковывать, то используем обычный метод split у строки. Можно пойти дальше и использовать blob и сохранять строку в один подход. Андроидовская обвязка для blob умеет сохранять массив byte. Класс java.nio.IntBuffer и java.nio.ByteBuffer позволяют завернуть массив int в массив byte. Вот пример как сохраняют картинку в базу. Но я верю, что сохранение в файл будет как минимум не медленнее (если только не сохранять поэлементно).

Ответ 2



Одного не понимаю, зачем здесь нужны транзакции в локальной БД куда юзер/аппа имеет монопольный доступ? Ну я бы еще понял, если автор где-то при ошибке вставки откатывал бы транзакции и проч. - но этого ведь нет. Раз так, то напрашивается рецепт: для ускорения вставок я бы рекомендовал установить наинизший возможный уровень изоляции транзакций: PRAGMA read_uncommitted = 1; И убрал бы словеса beginTransaction/endTransaction и проч. ненужную в данном контексте муть Еще бы поигрался с местом хранения временных файлов: PRAGMA temp_store = 0 | DEFAULT | 1 | FILE | 2 | MEMORY;

Ответ 3



Судя по коду, bulk insert вам поможет.

Ответ 4



Попробуйте установить другой вариант синхронизации: PRAGMA synchronous = 0 | OFF | 1 | NORMAL | 2 | FULL; Но тут надо понимать что если вырубание питания, или ещё какой непредвиденный косяк у Вас может "умереть" база. Хотя PRAGMA synchronous = 0 скорость увеличится раз в 10сять. И попробуйте ещё закидывать не по одному инсерту а по "пачке".

Как переименовать SQLite базу данных?

#sqlite #android


У меня есть заполненная база с названием first_base и мне нужно ее пепеименовать
в second_base. Как это реализовать (если возможно - через Java код)?
    


Ответы

Ответ 1



Можно переименовать файл базы данных, но сначала убедиться, что подключение к БД закрыто: Вызвать в activity: private void renameDatabase() { File databaseFile = getDatabasePath("yourdb.whatever"); File oldDatabaseFile = new File(databaseFile.getParentFile(), "yourdb_old.whatever"); databaseFile.renameTo(oldDatabaseFile); } Переименовать саму бд нельзя, нужно копировать данные в новую и удалять старую: Использовав SQLiteOpenHelper, можно использовать метод createDatabaseIfRequired() в методах getReadableDatabase() getWritableDatabase(): private boolean checkOldDatabase() { Log.d(Constants.LOGTAG, "OperationDbHelper.checkDatabase"); File f = new File(DB_PATH + OLD_DB_NAME); return f.exists(); } public void createDatabaseIfRequired() throws IOException, SQLiteException { if (!checkOldDatabase()) { // сравнивать БД / удалять старую / копировать новую } }

понедельник, 24 февраля 2020 г.

Qt выпадающий список в tableview

#cpp #qt #sqlite #tableview


Как организовать выпадающий список в tableview? Использую databinding с sqlite. В
одном из столбцов есть элементы, которые нужно представить на выбор выпадающим списком.
    


Ответы

Ответ 1



Если данные выпадающего списка содержатся в отдельной таблице базы данных, то удобно использовать QSqlRelationalTableModel и класс делегата QSqlRelationalDelegate. Для демонстрации предположим, что имеется таблица городов: CREATE TABLE IF NOT EXISTS `cities` ( `city_id` INTEGER UNSIGNED NOT NULL , `name` VARCHAR(50) NOT NULL , PRIMARY KEY(`city_id`) ) ... и таблица сотрудников, которые работают в этих городах: CREATE TABLE IF NOT EXISTS `employees` ( `employee_id` INTEGER UNSIGNED NOT NULL , `city_id` INTEGER NOT NULL , `name` VARCHAR(255) NOT NULL , PRIMARY KEY(`employee_id`) ) Заполним эти таблицы данными: INSERT INTO `cities` VALUES(1, 'Москва') INSERT INTO `cities` VALUES(2, 'Санкт-Петербург') INSERT INTO `employees` VALUES(1, 1, 'Иван Иваныч') INSERT INTO `employees` VALUES(2, 2, 'Пётр Петрович') Иван Иваныч работает в Москве, а Пётр Петрович в Санкт-Петербурге. Теперь создадим модель и представление таблицы с использованием Qt: QSqlRelationalTableModel *model = new QSqlRelationalTableModel(); model->setTable("employees"); model->setEditStrategy(QSqlTableModel::OnManualSubmit); model->setRelation(1, QSqlRelation("cities", "city_id", "name")); model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID")); model->setHeaderData(1, Qt::Horizontal, QObject::tr("City")); model->setHeaderData(2, Qt::Horizontal, QObject::tr("Name")); QTableView *view = new QTableView(); view->setModel(model); Ключевой строкой кода при объединении данных двух таблиц является использование метода setRelation() у QSqlRelationalTableModel. Первый аргумент метода - это индекс колонки в исходной таблице (в нашем случае employees), второй - объект объединения QSqlRelation, в конструкторе которого указываются наименование подключаемой таблицы, наименование её колонки, соответствующей исходной, плюс наименование колонки, содержимое которой должно подменять содержимое в ячейке исходной таблицы, если соответствие будет обнаружено. Если запустить программу, то в таблице сотрудников вместо идентификаторов городов окажутся их наименования. Однако при попытке редактирования ячейки города раскрывающийся список с доступными наименованиями не появится. Для решения этой стороны вопроса достаточно подключить объект делегата QSqlRelationalDelegate к представлению: view->setItemDelegateForColumn(1, new QSqlRelationalDelegate(view)); Разумеется, всё это будет работать только в том случае, если исходная модель получена наследованием от QSqlRelationalTableModel или прямым использованием объекта этого класса. Это не во всех случаях представляется возможным. Например, если модель получает данные из сети. Для этого случая предположим, что имеющиеся таблицы employees и cities содержатся не в БД, а лишь как данные, например, в произвольном наследнике QAbstractItemModel. Также как и в первом случае потребуется делегат, который будет выполнять роль связующего звена между таблицами сотрудников и городов. Файл comboboxitemdelegate.h: #ifndef COMBOBOXITEMDELEGATE_H #define COMBOBOXITEMDELEGATE_H #include #include class QAbstractItemModel; class ComboBoxItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit ComboBoxItemDelegate(QObject *parent = Q_NULLPTR); virtual ~ComboBoxItemDelegate() {} QAbstractItemModel *model() const; void setModel(QAbstractItemModel *model); int modelKeyColumn() const; void setModelKeyColumn(int column); int modelViewColumn() const; void setModelViewColumn(int column); virtual QString displayText(const QVariant &value , const QLocale &locale) const; virtual QWidget *createEditor(QWidget *parent , const QStyleOptionViewItem &option , const QModelIndex &index) const; virtual void setModelData(QWidget *editor , QAbstractItemModel *model , const QModelIndex &index) const; private: int _model_key_column, _model_view_column; QPointer _model; }; #endif Файл comboboxitemdelegate.cpp: #include #include "comboboxitemdelegate.h" ComboBoxItemDelegate::ComboBoxItemDelegate(QObject *parent) : QStyledItemDelegate(parent) , _model_key_column(0), _model_view_column(0) {} QAbstractItemModel *ComboBoxItemDelegate::model() const { return _model; } void ComboBoxItemDelegate::setModel(QAbstractItemModel *model) { _model = model; } int ComboBoxItemDelegate::modelKeyColumn() const { return _model_key_column; } void ComboBoxItemDelegate::setModelKeyColumn(int column) { _model_key_column = column; } int ComboBoxItemDelegate::modelViewColumn() const { return _model_view_column; } void ComboBoxItemDelegate::setModelViewColumn(int column) { _model_view_column = column; } QString ComboBoxItemDelegate::displayText(const QVariant &value , const QLocale &locale) const { Q_UNUSED(locale); if(_model.isNull()) return QString(); while(_model->canFetchMore(QModelIndex())) _model->fetchMore(QModelIndex()); QModelIndexList indexes = _model->match(_model->index(0,_model_key_column) , Qt::DisplayRole, value, 1, Qt::MatchExactly); if(indexes.isEmpty()) return QString(); QModelIndex index = _model->index(indexes.first().row(), _model_view_column); return (index.isValid()) ? _model->data(index).toString() : QString(); } QWidget *ComboBoxItemDelegate::createEditor(QWidget *parent , const QStyleOptionViewItem &option, const QModelIndex &index) const { if(_model.isNull()) return; while(_model->canFetchMore(QModelIndex())) _model->fetchMore(QModelIndex()); QComboBox *cbox = new QComboBox(parent); cbox->setGeometry(option.rect); cbox->setModel(_model); cbox->setModelColumn(_model_view_column); QModelIndexList indexes = _model->match(_model->index(0,_model_key_column) , Qt::DisplayRole, index.data(), 1, Qt::MatchExactly); if(!indexes.isEmpty()) cbox->setCurrentIndex(indexes.first().row()); return cbox; } void ComboBoxItemDelegate::setModelData(QWidget *editor , QAbstractItemModel *model, const QModelIndex &index) const { if(_model.isNull()) return; QComboBox *cbox = qobject_cast(editor); if(cbox == Q_NULLPTR) return; while(_model->canFetchMore(QModelIndex())) _model->fetchMore(QModelIndex()); QModelIndex model_index = _model->index(cbox->currentIndex(), _model_key_column); if(model_index.isValid()) model->setData(index, model_index.data()); } Представленный делегает не претендует на применение и опубликован с целью демонстрации механизма ассоциации значений между моделями. Подключение и использование делегата: QAbstractItemModel *employees = ... ; QAbstractItemModel *cities = ... ; QTableView *view = new QTableView(); view->setModel(employees); ComboBoxItemDelegate *delegate = new ComboBoxItemDelegate(view); delegate->setModel(cities); delegate->setModelKeyColumn(0); delegate->setModelViewColumn(1); view->setItemDelegateForColumn(1, delegate);

Ответ 2



Вы хоть уточните про который tableview речь-то идет - QTableView из widgets или про TableView из QtQuick.Controls. В целом в обоих случаях пишется делегат, для первого случая у QTableView (точнее у его предка) есть метод setItemDelegateForColumn, которому скармливается номер колонки и потомка QAbstractItemDelegate. Такового потомка я когда-то для себя писал и насколько помню он даже работал, но это было давно и работает ли он сейчас не знаю. Со вторым случаем все проще - у TableViewColumn есть соотвествующее свойство delegate куда вставляется qml Item (например, как тут, хотя скорее всего можно было и без Item обойтись), но сам я на практике этим не пользовался насколько помню, да и вообще с комбобоксом из QtQuick.Controls у меня натянутые отношения - то он не редактируемый, то редактируемый но на пробел забито раскрытие списка (поправили только в 5.4.1), то программно не устанавливается пустая строка в качестве значения

Какие существуют способы сохранения состояния списка (ArrayList) в БД?

#java #android #массивы #sqlite


Пишу приложение на Android, где у меня есть некоторый список (RecyclerView), в котором
есть возможность перетаскивания (drag & drop) строк в нужное для пользователя место
(выше/ниже относительно друг друга). Элементы списка (строки) могут быть разных классов
(под общим интерфейсом). Информация о созданных экземплярах, соответственно, хранится
в SQLite. 

На данный момент, сортировка списка всех элементов списка происходит по дате (при
создании списка сортируются в TreeSet'е и потом ArrayList'ом отдаются в адаптер).
Таким образом, при изменении порядка отображения элементов списка мне необходимо
зафиксировать этот порядок и сохранить его в БД (или может просто в JSON?).
Какие существуют, так сказать, лучшие практики сохранения и извлечения состояния
такого списка (да и вообще, подобных данных)?

Заранее благодарен!
    


Ответы

Ответ 1



Вам не нужно перегонять данные из БД в какие то локальные массивы. Работайте напрямую с базой данных, она имеет на порядок более мощный, чем "жалкие" методы коллекций, язык SQL, специально предназначеный для обработки, сортировки и тп. данных, содержащихся в базе, а так же выполняет эту обработку на порядки же быстрее, чем Java. Сам подход, который вы пытаетесь применить выглядит несколько нелогично. Зачем хранить данные в БД, но обрабатывать их какими то примитивными и медленными способами, при этом еще тратя время и на промежуточное извлечение из БД в массив, потом еще в другой массив. По вопросу - вам не нужно сохранять какой то измененный порядок сортировки, переписывая БД. Вам нужен правильный запрос в БД, который вернет данные (курсор), отсортированные в нужном порядке, именно так это делается. Затем этот курсор, в котором уже все отсортированно как надо через SQL-запрос, вы передаете непосредственно в адаптер списка и заполняете сам список данными из курсора, а не из каких то ArrayList-ов. UPD Если нужно сохранить произвольную пользовательскую сортировку, то опять же нужно модифицировать не следование записей в базе, а добавить к каждой записи еще один столбец для хранения порядка сортировки. При перетаскивании записывать в этот столбец позицию в списке элемента, в которую его перетащили. При выборке делать запрос с сортировкой по этому столбцу. То есть, примерная схема - делаем выборку из БД с сортировкой по времени, заполняем этой выборкой список, при перетаскивании пишем в БД позицию, в которую перетащили (в столбец пользовательской сортировки для записи, которую перетащили), обновляем адаптер через выборку с сортировкой по столбцу с этими позициями - все просто и прозрачно Это будет правильная архитектура работы с БД, а то, что пытаетесь сделать вы, какой то любительский костыль, простите за резкость, надеюсь это поможет понять вам всю глубину заблуждений :) Так же смотрите этот ответ по сортировке в БД

пятница, 14 февраля 2020 г.

Оптимальное количество элементов подгружаемых из SQLite в RecyclerView за один запрос

#android #sqlite #recyclerview


Есть новостной агрегатор. Статьи грузятся из сети, и хранятся в локальной SQLite.
UI организован стандартно: RecyclerView, элементами которого являются превью статей. 

Вопрос следующий: как оптимально организовать подгрузку данных в RecyclerView из
SQLite, если на экране помещается ≈ 3 элемента?

Поясню: в данном вопросе временем загрузки статей из сети, их размещением в БД и
временем преобразования статей в превью пренебрегаем. Интересует механизм подгрузки
элементов в RecyclerView за один запрос и организация этой подгрузки. Стандартный механизм
(загружаем сколько-то элементов -> пользователь прокручивает список до конца -> загружаем
новую порцию элементов) не подходит.

Например, возможно стоит ловить скроллинг на середине списка и уже тогда начинать
подгрузку новых данных? Кроме того, возможно у уважаемого сообщества есть опыт о том,
какое количество элементов из SQLite оптимально забирать за один запрос? Т.к. есть
подозрение, что загрузка 3 элементов из SQLite будет примерно равна по времени загрузке
30 элементов, т.к. большая часть времени тратится на саму транзакцию, но уже скажем
запрашивать 300 элементов за запрос будет неоптимально. 
    


Ответы

Ответ 1



@pavloff, конечно все верно расписал: за один запрос нужно "забирать" все нужные данные, нет никакого смысла делить их на маленькие части. Такое применимо к сетевым запросам, но никак не к локальным БД. Cursor сам подгрузит что надо, в нужном ему количестве, насколько мне помниться. В любом случае не стоит делать из этого проблем Но проблема в том, что RecyclerView из коробки не умеет поддерживать адаптер с курсором, то есть надо самому заморачиваться с изобретением адаптера, который будет читать из БД или же придумывать адаптер, который работает поверх Cursor, по аналогии с CursorAdapter, который имеется в комплекте ListView. К счастью умные люди уже все написали все. Возьмите RecyclerViewCursorAdapter - подайте ему на вход заранее подготовленный Cursor и будет вам счастье

Ответ 2



Разобрался в проблеме. Представлю свой ответ. Во-первых, почему для достаточно больших списков не стоит загружать сразу все данные для RecyclerView из SQLite: Нет смысла грузить данные, которые пользователь, возможно, не увидит. Если при той же отзывчивости интерфейса (без тормозов), мы можем обеспечить плавную подгрузку данных и, при этом, наше приложение будет кушать меньше памяти, то почему бы не сделать так? Использование SQLiteCursor имеет ту особенность, что при выборке данных размером больше 2 MB появляются дополнительные расходы по времени на выборку каждых следующих 2 MB данных. Речь о том, что размер используемого SQLiteCursor буфера строк (CursorWindow) около 2 MB. Более подробно об этой проблеме см. в этой статье: оригинал (en) и перевод на русский. Существуют решения по плавной подгрузке данных в RecyclerView из SQLite: см., например, этот цикл статей (en). Есть данные, что оптимально загружать за один раз не менее чем в 10 раз больше элементов RecyclerView, чем помещается на экране. Т.е., если на экране 3 элемента, подгружаем за один раз около 30. Подгрузку следующей порции данных, следует начинать на половине или 2/3 скроллинга до конца списка. Это общие советы, т.к. стоит учитывать размер данных, возможное их преобразование в POJO-объекты для списка, особенности реализации RecyclerView. Наконец, есть библиотека от Google Paging Library (еще не вышла в релиз 1.0), которая призвана в том числе решать проблему, затронутую в вопросе. Еще один пример, реализация подгрузки c использованием RxJava (1 часть, 2 часть).

SQLite.Interop.dll. Не удается загрузить

#c_sharp #winforms #sqlite


Доброе утро. У меня вопрос. Я создал UserControl и добавил в tabPage. Из базы данных
беру данные в UserControl. Вопрос первый. "SQLite.Interop.dll. Не удается загрузить"
Это серьезная проблема или нет? Как можно решить? Сам SQLite.Interop.dll в References
нету, а в проекте в папке существует. Заранее спасибо. Теперь у меня вообще не работает
все. Когда добавляю UserCOntrol в Winforms.
    


Ответы

Ответ 1



Для работы с SQLite вам нужно подключить System.Data.SQLite, а библиотеку SQLite.Interop положить рядом с exe-файлом тогда System.Data.SQLite сама найдет её.

Ответ 2



Если я правильно понял из такой скудной вводной - вам в проекте надо выставить у DLL свойство CopyToOutputDirectory в true.

Как предотвратить создание пустой БД SQLite в Python

#python #python_3x #sqlite


Пытаюсь работать с базой данных SQLite в Python. Пишу класс для этой цели. В конструкторе
класса пишу такие строки:

def __init__(self, file_name='test.db'):
    self.conn = sqlite3.connect(file_name)
    self.cur = self.conn.cursor()


Эта БД должна именно хранить данные. Поэтому, если что-то случилось с файлом БД,
то обязательно нужно эту ситуацию обнаружить и сообщить админу.

Однако, если метод sqlite3.connect(file_name) не может открыть файл БД, то он просто
пересоздаёт пустую БД. Это очень нежелательное поведение.

Сколько я не читал документацию, нигде не говорится ни про какие коды завершения
или except-оны. Как поймать такую ситуацию?

Я понимаю, что можно средствами ОС обнаружить отсутствие файла БД, но это совсем
не то... А вдруг - он есть, но испорчен? Поэтому вопрос: как предотвратить поведение
по умолчанию метода сonnect и обнаружить ошибку?
    


Ответы

Ответ 1



Поскольку имя файла БД никаким образов не инкапсулировано в какой-нибудь хитрой строке подключения - совершенно нормально делать проверку на существование файла перед его открытием. В случае же если файл существует, но поврежден и не может быть прочитан - никто его автоматически пересоздавать не будет, вместо этого будет исключение. Также вместо проверки на существование файла можно попробовать использовать формат URI для имени БД: sqlite3.connect('file:test.db?mode=rw', uri=True) Как сказано в документации, в режиме rw драйвер sqlite не пытается создать новую базу данных. Это API существует с версии 3.4

Ответ 2



Предлагаю перед открытием файла, делать проверку на существование. def __init__(self, file_name='test.db'): if os.path.isfile(file_name='test.db'): self.conn = sqlite3.connect(file_name) self.cur = self.conn.cursor() else: raise FileExistsError(file_name)

воскресенье, 9 февраля 2020 г.

Как передать строку в запрос к БД

#python #sqlite


Имеется функция:

def data_selection(): 
    c.execute("SELECT * FROM TEST WHERE num = 5")


Я хочу сделать так, чтобы пользователь сам ввёл num = 5 с клавиатуры и передать эту
строку в запрос.

Подскажите пожалуйста, как можно провернуть что - то подобное?   
    


Ответы

Ответ 1



inp = input() z = 'SELECT * FROM TEST WHERE num = ' + str(inp) c.execute(z)

Ответ 2



В соотсвествие с документацией для метода execute примерно так num = int(input()) c.execute("SELECT * FROM TEST WHERE num=?", num) либо: c.execute("SELECT * FROM TEST WHERE num=:num", {"num": num})