Sobald API festgelegt ist, ich es auf diese Weise implementieren würde:
Von DB Perspektive, I 2 Tabellen haben würde - Usertable und RelationsTable alle Kanten Ihrer Freunde zu halten grafisch darstellen:
Ie Die Idee ist, Benutzer in der einen Tabelle und ihre Beziehungen in Relationen Tabelle zu halten. Es erlaubt auch, später eine zusätzliche Logik hinzuzufügen (zum Beispiel blendet der Benutzer seine Verbindung aus oder blockiert jemanden, usw. - irgendwelche möglichen Kanten des Graphen). Auch ermöglicht es, Probleme mit kreisförmigen Referenzen zu verringern.
Als Framework zum Abrufen von Daten aus Service & Parse JSons, würde ich Retrofit verwenden.
Zuerst würde ich UserBase
und User
Klassen definieren:
public class UserBase {
public string id;
}
public final class User extends UserBase {
public string name;
public List<UserBase> friends;
// user's "real" friends, not just ids, fills from SQLite
public List<User> userFriends;
}
wo, wie Sie sehen können, friends
ist eine Liste von UserBase
Objekte für Retrofit das Objekt von JSON und userFriends
zu analysieren - die Liste, welche wir in weiteren Schritten manuell aus SQLite füllen werden.
Nun lassen Sie uns einige Hilfe-Klassen definieren mit DBs zu bedienen:
public interface Dao<TItem> {
void add(List<TItem> items);
void removeAll();
List<TItem> getAll();
}
....
public abstract class AbstractDao<TItem> implements Dao<TItem> {
protected final SQLiteDatabase database;
protected final SqlUtilities sqlUtilities;
public AbstractDao(SQLiteDatabase database, SqlUtilities sqlUtilities) {
this.database = database;
this.sqlUtilities = sqlUtilities;
}
}
Jetzt müssen wir Dao für RELATED und für Usertable:
public class UserRelation {
public String mainUserId;
public String relatedUserId;
}
...
public interface UserRelationDao extends Dao<UserRelation> {
...
List<User> getFriends(String userId);
...
}
...
public interface UserDao extends Dao<User> {
...
void addWithIgnore(List<TItem> items);
void update(List<TItem> items);
void upsert(List<TItem> items);
User getById(String userId);
...
}
Sobald es fertig ist, wir dies tatsächlich umsetzen können Schnittstellen:
DefaultUserRelationDao
Klasse:
public class DefaultUserRelationDao extends AbstractDao<UserRelation> implements UserRelationDao {
static final String MAIN_USER_COLUMN = "mainuser";
static final String RELATED_USER_COLUMN = "relateduser";
private static final String[] COLUMN_NAMES = new String[]{
MAIN_USER_COLUMN,
RELATED_USER_COLUMN,
};
private static final String[] COLUMN_TYPES = new String[]{
"TEXT",
"TEXT",
};
private static final String TABLE = "userrelation";
static final String CREATE_TABLE = SqlUtilities.getCreateStatement(TABLE, COLUMN_NAMES, COLUMN_TYPES);
static final String ALL_CONNECTED_USERS =
"SELECT " + Joiner.on(",").join(DefaultUserDao.COLUMN_NAMES) +
" FROM " + UserTable.TABLE_NAME + "," + TABLE +
" WHERE " + RELATED_USER_COLUMN + "=" + DefaultUserDao.USER_ID_COLUMN;
public DefaultUserRelationDao(SQLiteDatabase database, SqlUtilities sqlUtilities) {
super(database, sqlUtilities);
}
@Override
public void add(List<UserRelation> userRelations) {
try {
database.beginTransaction();
ContentValues contentValues = new ContentValues();
for (UserRelation relation : userRelations) {
sqlUtilities.setValuesForUsersRelation(contentValues, relation);
database.insertOrThrow(TABLE, null, contentValues);
}
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
@Override
public List<User> getFriends(String userId) {
Cursor cursor = database.rawQuery(ALL_CONNECTED_USERS, new String[]{userId});
return sqlUtilities.getConnectedUsers(cursor);
}
}
und DefaultUserDao
Klasse:
public final class DefaultUserDao extends AbstractUDao<User> implements UserDao {
public static final String USER_ID_COLUMN = "userid";
static final String USER_NAME_COLUMN = "username";
public static final String[] COLUMN_NAMES = new String[]{
USER_ID_COLUMN,
USER_NAME_COLUMN,
};
private static final String TABLE = "users";
private static final String SELECT_BY_ID =
SqlUtilities.getSelectWhereStatement(TABLE, COLUMN_NAMES, new String[]{ USER_ID_COLUMN });
static final String CREATE_TABLE = SqlUtilities.getCreateStatement(TABLE, COLUMN_NAMES, COLUMN_TYPES);
public DefaultUserDao(SQLiteDatabase database, SqlUtilities sqlUtilities) {
super(database, sqlUtilities);
}
@Override
public void add(List<User> users) {
try {
database.beginTransaction();
ContentValues contentValues = new ContentValues();
for (User user : users) {
sqlUtilities.setValuesForUser(contentValues, user);
database.insertOrThrow(UserTable.TABLE_NAME, null, contentValues);
}
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
@Override
public User getById(String userId) {
return getUserBySingleColumn(SELECT_BY_ID, userId);
}
.....
private User getUserBySingleColumn(String selectStatement, String value) {
Cursor cursor = database.rawQuery(selectStatement, new String[]{value});
List<User> users = sqlUtilities.getUsers(cursor);
return (users.size() != 0) ? users.get(0) : null;
}
}
unsere Tabellen erstellen, müssen wir SQLiteOpenHelper
und in onCreate()
tatsächlich Tabellen erstellen erweitern:
public final class DatabaseHelper extends SQLiteOpenHelper {
static final String DATABASE_NAME = "mysuper.db";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, SCHEMA_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DefaultUserDao.CREATE_TABLE);
db.execSQL(DefaultUserRelationDao.CREATE_TABLE);
}
...
}
Jetzt würde ich vorschlagen, Localstorage-Schnittstelle mit allen definieren mögliche Aktionen mit Cache:
hinzufügen und es ist die Implementierung:
public final class SqlLocalStorage implements LocalStorage {
private UserDao userDao;
private UserRelationDao userRelationDao;
private SQLiteDatabase database;
private final Object initializeLock = new Object();
private volatile boolean isInitialized = false;
private SqlUtilities sqlUtilities;
// there database is
// SQLiteOpenHelper helper = new DatabaseHelper(context);
// database = helper.getWritableDatabase();
public SqlLocalStorage(SQLiteDatabase database, SqlUtilities sqlUtilities) {
this.database = database;
this.sqlUtilities = sqlUtilities;
}
@Override
public User getUserById(String userId) {
initialize();
User user = userDao.getById(userId);
if (user == null) {
return null;
}
List<User> relatedUsers = userRelationDao.getFriends(userId);
user.userFriends = relaterUsers;
return user;
}
@Override
public void addUsers(List<User> users) {
initialize();
for (User user : users) {
for (UserBase friend : user) {
UserRelation userRelation = new UserRelation();
userRelation.mainUserId = user.id;
userRelation.relatedUserId = friend.id;
UserRelation userRelationMutual = new UserRelation();
userRelationMutual.mainUserId = friend.id;
userRelationMutual.relatedUserId = user.id;
userRelationDao.add(userRelation);
userRelationMutual.add(userRelation)
}
}
userDao.addWithIgnore(users);
}
void initialize() {
if (isInitialized) {
return;
}
synchronized (initializeLock) {
if (isInitialized) {
return;
}
Log.d(LOG_TAG, "Opens database");
userDao = new DefaultUserDao(database, sqlUtilities);
userRelationDao = new DefaultUserRelationDao(database, sqlUtilities);
isInitialized = true;
}
}
}
Letzter Schritt - die tatsächliche Verwendung davon:
//somewhere in non-UI thread
List<User> users = dataSource.getUsers();
localStorage.addUsers(users);
final User userBob = localStorage.getUserById("42");
Achtung! Ich benutze hier stark meine benutzerdefinierte Klasse SqlUtilities. Leider ist es viel zu groß, um es hier zu posten, aber nur ein Beispiel einige Ideen zu geben, was drin ist - hier ist, wie GetUsers (Cursor Cursor) sieht es:
.....
public List<User> getUsers(Cursor cursor) {
ArrayList<User> users = new ArrayList<>();
try {
while (cursor.moveToNext()) {
users.add(getUser(cursor));
}
} finally {
cursor.close();
}
return users;
}
private User getUser(Cursor cursor) {
User user = new User(cursor.getString(0));
user.FullName = cursor.getString(1);
....
return user;
}
.....
Ich hoffe, werde verzeihen Sie mir einige Skipping Details (vor allem in Bezug auf den Fall, wenn DB aktualisiert werden muss, wenn Daten nicht voll ist und neben dem Abrufen aus dem Cache, müssen Sie es zuerst vom Server abrufen, und laden Sie es dann in den Cache usw.). Wenn ein wichtiger Teil fehlt - bitte, posten Sie es in Kommentaren und ich werde mich freuen, den Beitrag zu aktualisieren.
Ich hoffe, es wird Ihnen helfen.
Ich kann Ihnen eine Methode erstellen, um die ID, Namen, Freunde analysieren und als Liste oder eine beliebige Datenstruktur für den weiteren Prozess zurückgeben, würde das helfen? –