android-java
Supports Android development in Java with clean architecture patterns and integrated testing.
Install
mkdir -p .claude/skills/android-java && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8302" && unzip -o skill.zip -d .claude/skills/android-java && rm skill.zipInstalls to .claude/skills/android-java
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Android Java development with MVVM, ViewBinding, and Espresso testingKey capabilities
- →Implement MVVM architecture patterns
- →Configure ViewBinding for Android layouts
- →Generate unit test boilerplate with Espresso
- →Maintain Android project structure
How it works
It applies a structured template for Android classes and testing frameworks, enforcing standard MVVM architectural boundaries.
Inputs & outputs
When to use android-java
- →Building Android app features
- →Implementing MVVM architecture
- →Writing Espresso UI tests
- →Managing Android project structure
About this skill
Android Java Skill
Project Structure
project/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/app/
│ │ │ │ ├── data/ # Data layer
│ │ │ │ │ ├── local/ # Room database, SharedPreferences
│ │ │ │ │ ├── remote/ # Retrofit services, API clients
│ │ │ │ │ └── repository/ # Repository implementations
│ │ │ │ ├── di/ # Dependency injection (Hilt/Dagger)
│ │ │ │ ├── domain/ # Business logic
│ │ │ │ │ ├── model/ # Domain models
│ │ │ │ │ ├── repository/ # Repository interfaces
│ │ │ │ │ └── usecase/ # Use cases
│ │ │ │ ├── ui/ # Presentation layer
│ │ │ │ │ ├── feature/ # Feature screens
│ │ │ │ │ │ ├── FeatureActivity.java
│ │ │ │ │ │ ├── FeatureFragment.java
│ │ │ │ │ │ └── FeatureViewModel.java
│ │ │ │ │ └── common/ # Shared UI components
│ │ │ │ └── App.java # Application class
│ │ │ ├── res/
│ │ │ │ ├── layout/
│ │ │ │ ├── values/
│ │ │ │ └── drawable/
│ │ │ └── AndroidManifest.xml
│ │ ├── test/ # Unit tests
│ │ └── androidTest/ # Instrumentation tests
│ └── build.gradle
├── build.gradle # Project-level build file
├── gradle.properties
├── settings.gradle
└── CLAUDE.md
Gradle Configuration
App-level build.gradle
plugins {
id 'com.android.application'
}
android {
namespace 'com.example.app'
compileSdk 34
defaultConfig {
applicationId "com.example.app"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
buildFeatures {
viewBinding true
}
}
dependencies {
// AndroidX
implementation 'androidx.core:core:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// Lifecycle
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata:2.7.0'
// Testing
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.8.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Architecture Patterns
MVVM with ViewModel
// ViewModel - holds UI state, survives configuration changes
public class UserViewModel extends ViewModel {
private final UserRepository repository;
private final MutableLiveData<User> user = new MutableLiveData<>();
private final MutableLiveData<Boolean> loading = new MutableLiveData<>(false);
private final MutableLiveData<String> error = new MutableLiveData<>();
public UserViewModel(UserRepository repository) {
this.repository = repository;
}
public LiveData<User> getUser() {
return user;
}
public LiveData<Boolean> isLoading() {
return loading;
}
public LiveData<String> getError() {
return error;
}
public void loadUser(String userId) {
loading.setValue(true);
repository.getUser(userId, new Callback<User>() {
@Override
public void onSuccess(User result) {
user.setValue(result);
loading.setValue(false);
}
@Override
public void onError(String message) {
error.setValue(message);
loading.setValue(false);
}
});
}
}
Repository Pattern
// Repository interface (domain layer)
public interface UserRepository {
void getUser(String userId, Callback<User> callback);
void saveUser(User user, Callback<Void> callback);
}
// Repository implementation (data layer)
public class UserRepositoryImpl implements UserRepository {
private final UserApi api;
private final UserDao dao;
public UserRepositoryImpl(UserApi api, UserDao dao) {
this.api = api;
this.dao = dao;
}
@Override
public void getUser(String userId, Callback<User> callback) {
// Try cache first, then network
User cached = dao.getUserById(userId);
if (cached != null) {
callback.onSuccess(cached);
return;
}
api.getUser(userId).enqueue(new retrofit2.Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful() && response.body() != null) {
dao.insert(response.body());
callback.onSuccess(response.body());
} else {
callback.onError("Failed to load user");
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
callback.onError(t.getMessage());
}
});
}
}
Activity & Fragment Patterns
Activity with ViewBinding
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private MainViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
setupObservers();
setupListeners();
}
private void setupObservers() {
viewModel.getUser().observe(this, user -> {
binding.userName.setText(user.getName());
});
viewModel.isLoading().observe(this, isLoading -> {
binding.progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
});
}
private void setupListeners() {
binding.refreshButton.setOnClickListener(v -> {
viewModel.loadUser(getCurrentUserId());
});
}
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
}
}
Fragment with ViewBinding
public class UserFragment extends Fragment {
private FragmentUserBinding binding;
private UserViewModel viewModel;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentUserBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class);
setupObservers();
}
private void setupObservers() {
viewModel.getUser().observe(getViewLifecycleOwner(), user -> {
binding.userName.setText(user.getName());
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null; // Prevent memory leaks
}
}
Testing
Unit Tests with JUnit & Mockito
@RunWith(MockitoJUnitRunner.class)
public class UserViewModelTest {
@Mock
private UserRepository repository;
@Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
private UserViewModel viewModel;
@Before
public void setup() {
viewModel = new UserViewModel(repository);
}
@Test
public void loadUser_success_updatesUserLiveData() {
// Arrange
User expectedUser = new User("1", "John Doe");
doAnswer(invocation -> {
Callback<User> callback = invocation.getArgument(1);
callback.onSuccess(expectedUser);
return null;
}).when(repository).getUser(eq("1"), any());
// Act
viewModel.loadUser("1");
// Assert
assertEquals(expectedUser, viewModel.getUser().getValue());
assertFalse(viewModel.isLoading().getValue());
}
@Test
public void loadUser_error_updatesErrorLiveData() {
// Arrange
doAnswer(invocation -> {
Callback<User> callback = invocation.getArgument(1);
callback.onError("Network error");
return null;
}).when(repository).getUser(eq("1"), any());
// Act
viewModel.loadUser("1");
// Assert
assertEquals("Network error", viewModel.getError().getValue());
assertFalse(viewModel.isLoading().getValue());
}
}
Instrumentation Tests with Espresso
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityScenarioRule<MainActivity> activityRule =
new ActivityScenarioRule<>(MainActivity.class);
@Test
public void userName_isDisplayed() {
onView(withId(R.id.userName))
.check(matches(isDisplayed()));
}
@Test
public void refreshButton_click_triggersRefresh() {
onView(withId(R.id.refreshButton))
.perform(click());
onView(withId(R.id.progressBar))
.check(matches(isDisplayed()));
}
@Test
public void userList_scrollToItem_displaysCorrectly() {
onView(withId(R.id.userList))
.perform(RecyclerViewAct
---
*Content truncated.*
When not to use it
- →Kotlin-first Android development
- →Native C++ NDK projects
Prerequisites
Limitations
- →Limited to Java-based Android projects
- →Requires manual integration with existing dependencies
How it compares
It automates the boilerplate for architecture enforcement rather than just generating basic code.
Compared to similar skills
android-java side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| android-java (this skill) | 0 | 4mo | No flags | Intermediate |
| qa_skill | 0 | 3mo | Review | Intermediate |
| flutter | 13 | 4mo | No flags | Intermediate |
| android-kotlin | 7 | 4mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by alinaqi
View all by alinaqi →You might also like
qa_skill
sameer-thakkar
Skill and agent index for crest (API), celium (UI), pixify (VD), and appium (Mobile) — load qa_common as the universal entry point or use individual skills (api, ui, vd, app) directly for single-layer tasks.
flutter
alinaqi
Flutter development with Riverpod state management, Freezed, go_router, and mocktail testing
android-kotlin
alinaqi
Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing
frontend-mobile-development-component-scaffold
sickn33
You are a React component architecture expert specializing in scaffolding production-ready, accessible, and performant components. Generate complete component implementations with TypeScript, tests, s
c64-parity
chrisgleissner
name: c64-parity description: Use when auditing or enforcing feature and behavior parity across Android, iOS, and Web routes in C64 Commander. argument-hint: (optional) specific feature or module user-invocable: true disable-model-invocation: true
flutter-bloc-state-management
HoangNguyen0403
Implement BLoC/Cubit state management correctly in Flutter. Use when writing, modifying, reviewing, or testing any BLoC, Cubit, state, or event file.