Android
Android 아키텍처 구성 요소
수색…
소개
Android Architecture Components는 강력하고 테스트 가능하며 유지 보수가 쉬운 앱을 디자인하는 데 도움이되는 새로운 라이브러리 모음입니다. 주요 부분은 Lifecycles, ViewModel, LiveData, Room입니다.
아키텍처 구성 요소 추가
프로젝트 build.gradle
allprojects {
repositories {
jcenter()
// Add this if you use Gradle 4.0+
google()
// Add this if you use Gradle < 4.0
maven { url 'https://maven.google.com' }
}
}
ext {
archVersion = '1.0.0-alpha5'
}
응용 프로그램 빌드 gradle
// For Lifecycles, LiveData, and ViewModel
compile "android.arch.lifecycle:runtime:$archVersion"
compile "android.arch.lifecycle:extensions:$archVersion"
annotationProcessor "android.arch.lifecycle:compiler:$archVersion"
// For Room
compile "android.arch.persistence.room:runtime:$archVersion"
annotationProcessor "android.arch.persistence.room:compiler:$archVersion"
// For testing Room migrations
testCompile "android.arch.persistence.room:testing:$archVersion"
// For Room RxJava support
compile "android.arch.persistence.room:rxjava2:$archVersion"
AppCompatActivity에서 수명주기 사용
이 활동에서 활동 펼치기
public abstract class BaseCompatLifecycleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
// We need this class, because LifecycleActivity extends FragmentActivity not AppCompatActivity
@NonNull
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@NonNull
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}
LiveData 변환이 포함 된 ViewModel
public class BaseViewModel extends ViewModel {
private static final int TAG_SEGMENT_INDEX = 2;
private static final int VIDEOS_LIMIT = 100;
// We save input params here
private final MutableLiveData<Pair<String, String>> urlWithReferrerLiveData = new MutableLiveData<>();
// transform specific uri param to "tag"
private final LiveData<String> currentTagLiveData = Transformations.map(urlWithReferrerLiveData, pair -> {
Uri uri = Uri.parse(pair.first);
List<String> segments = uri.getPathSegments();
if (segments.size() > TAG_SEGMENT_INDEX)
return segments.get(TAG_SEGMENT_INDEX);
return null;
});
// transform "tag" to videos list
private final LiveData<List<VideoItem>> videoByTagData = Transformations.switchMap(currentTagLiveData, tag -> contentRepository.getVideoByTag(tag, VIDEOS_LIMIT));
ContentRepository contentRepository;
public BaseViewModel() {
// some inits
}
public void setUrlWithReferrer(String url, String referrer) {
// set value activates observers and transformations
urlWithReferrerLiveData.setValue(new Pair<>(url, referrer));
}
public LiveData<List<VideoItem>> getVideoByTagData() {
return videoByTagData;
}
}
UI의 어딘가 :
public class VideoActivity extends BaseCompatLifecycleActivity {
private VideoViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get ViewModel
viewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
// Add observer
viewModel.getVideoByTagData().observe(this, data -> {
// some checks
adapter.updateData(data);
});
...
if (savedInstanceState == null) {
// init loading only at first creation
// you just set params and
viewModel.setUrlWithReferrer(url, referrer);
}
}
방연
방은 데이터베이스 클래스, DAO 클래스, 엔티티 클래스 및 마이그레이션 클래스의 4 가지 부분을 필요로합니다 (이제 DDL 메소드 만 사용할 수 있습니다).
엔티티 클래스
// Set custom table name, add indexes
@Entity(tableName = "videos",
indices = {@Index("title")}
)
public final class VideoItem {
@PrimaryKey // required
public long articleId;
public String title;
public String url;
}
// Use ForeignKey for setup table relation
@Entity(tableName = "tags",
indices = {@Index("score"), @Index("videoId"), @Index("value")},
foreignKeys = @ForeignKey(entity = VideoItem.class,
parentColumns = "articleId",
childColumns = "videoId",
onDelete = ForeignKey.CASCADE)
)
public final class VideoTag {
@PrimaryKey
public long id;
public long videoId;
public String displayName;
public String value;
public double score;
}
DAO 수업
@Dao
public interface VideoDao {
// Create insert with custom conflict strategy
@Insert(onConflict = OnConflictStrategy.REPLACE)
void saveVideos(List<VideoItem> videos);
// Simple update
@Update
void updateVideos(VideoItem... videos);
@Query("DELETE FROM tags WHERE videoId = :videoId")
void deleteTagsByVideoId(long videoId);
// Custom query, you may use select/delete here
@Query("SELECT v.* FROM tags t LEFT JOIN videos v ON v.articleId = t.videoId WHERE t.value = :tag ORDER BY updatedAt DESC LIMIT :limit")
LiveData<List<VideoItem>> getVideosByTag(String tag, int limit);
}
데이터베이스 클래스
// register your entities and DAOs
@Database(entities = {VideoItem.class, VideoTag.class}, version = 2)
public abstract class ContentDatabase extends RoomDatabase {
public abstract VideoDao videoDao();
}
마이그레이션
public final class Migrations {
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
final String[] sqlQueries = {
"CREATE TABLE IF NOT EXISTS `tags` (`id` INTEGER PRIMARY KEY AUTOINCREMENT," +
" `videoId` INTEGER, `displayName` TEXT, `value` TEXT, `score` REAL," +
" FOREIGN KEY(`videoId`) REFERENCES `videos`(`articleId`)" +
" ON UPDATE NO ACTION ON DELETE CASCADE )",
"CREATE INDEX `index_tags_score` ON `tags` (`score`)",
"CREATE INDEX `index_tags_videoId` ON `tags` (`videoId`)"};
for (String query : sqlQueries) {
database.execSQL(query);
}
}
};
public static final Migration[] ALL = {MIGRATION_1_2};
private Migrations() {
}
}
Application 클래스에서 사용하거나 Dagger를 통해 제공하십시오.
ContentDatabase provideContentDatabase() {
return Room.databaseBuilder(context, ContentDatabase.class, "data.db")
.addMigrations(Migrations.ALL).build();
}
저장소 작성 :
public final class ContentRepository {
private final ContentDatabase db;
private final VideoDao videoDao;
public ContentRepository(ContentDatabase contentDatabase, VideoDao videoDao) {
this.db = contentDatabase;
this.videoDao = videoDao;
}
public LiveData<List<VideoItem>> getVideoByTag(@Nullable String tag, int limit) {
// you may fetch from network, save to database
....
return videoDao.getVideosByTag(tag, limit);
}
}
ViewModel에서 사용 :
ContentRepository contentRepository = ...;
contentRepository.getVideoByTag(tag, limit);
사용자 정의 LiveData
사용자 지정 논리가 필요한 경우 사용자 지정 LiveData를 작성할 수 있습니다.
데이터 변환 만하면된다 (Transformations 클래스 사용).
public class LocationLiveData extends LiveData<Location> {
private LocationManager locationManager;
private LocationListener listener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// Do something
}
@Override
public void onProviderEnabled(String provider) {
// Do something
}
@Override
public void onProviderDisabled(String provider) {
// Do something
}
};
public LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
// We have observers, start working
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
// We have no observers, stop working
locationManager.removeUpdates(listener);
}
}
사용자 정의 라이프 사이클 인식 구성 요소
각 UI 구성 요소 라이프 사이클은 이미지와 같이 변경되었습니다. 라이프 사이클 상태 변경시 알림을받을 구성 요소를 만들 수 있습니다.
public class MyLocationListener implements LifecycleObserver {
private boolean enabled = false;
private Lifecycle lifecycle;
public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// connect
}
}
public void enable() {
enabled = true;
if (lifecycle.getState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow