Developing Sports Applications Based on HarmonyOS Next: A Practical Guide to AppGallery Connect Integration
1. Introduction and Project Overview
With the rise of the national fitness trend, the demand for sports applications on mobile devices has grown significantly. HarmonyOS Next, as a next-generation operating system, provides developers with powerful distributed capabilities and a smooth user experience. This tutorial will guide developers in using the ArkTS language and AppGallery Connect services to build a comprehensive sports social application.
Our sample application, "SportConnect," will include the following core features:
- Recording and analyzing user sports data
- Sports community interaction
- Event registration and management
- Cloud synchronization of health data
2. Environment Setup and Project Creation
First, ensure that the latest version of DevEco Studio and the HarmonyOS SDK are installed. When creating a new project, select the "Application" template, choose ArkTS as the language, and the Stage model.
// Entry file: EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
console.info('SportConnect Application onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Load the home page when the main window is created
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('Failed to load the content. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in loading the content.');
});
}
}
3. Integrating AppGallery Connect Services
3.1 Configuring the AGC Project
- Log in to the AppGallery Connect console and create a new project.
- Add a HarmonyOS application to the project.
- Download the
agconnect-services.json
configuration file and place it in the project directory.
// Initialize AGC services when the app starts
import agconnect from '@hw-agconnect/api-ohos';
import '@hw-agconnect/core-ohos';
@Entry
@Component
struct Index {
aboutToAppear() {
// Initialize AGC services
agconnect.instance().init(this.context);
console.info('AGC initialization completed');
}
build() {
Column() {
Text('Welcome to SportConnect')
.fontSize(30)
.margin({ bottom: 20 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
3.2 Integrating Authentication Services
Sports applications typically require a user system. We will use AGC's authentication service:
// Authentication module: AuthService.ts
import { IdentityAuthManager } from '@hw-agconnect/auth-ohos';
export class AuthService {
// Anonymous login
static async anonymousLogin(): Promise<void> {
try {
await IdentityAuthManager.signIn();
console.info('Anonymous login success');
} catch (err) {
console.error(`Login failed: ${JSON.stringify(err)}`);
}
}
// Phone number login
static async phoneLogin(phone: string, code: string): Promise<void> {
try {
const credential = IdentityAuthManager.credentialWithVerifyCode(
phone,
code,
IdentityAuthManager.PHONE_VERIFY_CODE_LOGIN
);
await IdentityAuthManager.signIn(credential);
console.info('Phone login success');
} catch (err) {
console.error(`Phone login failed: ${JSON.stringify(err)}`);
}
}
}
4. Collecting and Storing Sports Data
4.1 Health Data Collection
Using HarmonyOS's health data management API:
// Motion data collection module: MotionService.ts
import { health, healthKit } from '@kit.HealthKit';
export class MotionService {
// Request health data permissions
static async requestPermissions(): Promise<void> {
const permissions: Array<string> = [
'ohos.permission.health.READ_HEALTH_DATA',
'ohos.permission.health.WRITE_HEALTH_DATA'
];
try {
await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(
this.context,
permissions
);
console.info('Health permissions granted');
} catch (err) {
console.error(`Failed to get health permissions: ${JSON.stringify(err)}`);
}
}
// Get today's step count
static async getTodaySteps(): Promise<number> {
try {
const options = {
startTime: new Date(new Date().setHours(0, 0, 0, 0)).getTime(),
endTime: new Date().getTime(),
dataType: health.DataType.DATA_TYPE_STEP_COUNT
};
const result = await health.getHealthData(options);
return result?.length > 0 ? result[0].value : 0;
} catch (err) {
console.error(`Get steps failed: ${JSON.stringify(err)}`);
return 0;
}
}
}
4.2 Cloud Data Storage
Using AGC's Cloud Database to store user sports data:
// Data storage module: CloudDBService.ts
import { clouddb } from '@hw-agconnect/database-ohos';
const CLOUDDB_ZONE_NAME = 'SportDataZone';
const SPORT_RECORD_TYPE = 'SportRecord';
interface SportRecord {
id: string;
userId: string;
sportType: string;
duration: number; // Minutes
calories: number;
distance?: number; // Kilometers
startTime: number;
endTime: number;
}
export class CloudDBService {
private static cloudDB: clouddb.CloudDBZone;
// Initialize Cloud Database
static async initCloudDB(): Promise<void> {
try {
const config = new clouddb.CloudDBZoneConfig(
CLOUDDB_ZONE_NAME,
clouddb.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
clouddb.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC
);
this.cloudDB = await clouddb.CloudDBZone.open(config);
await clouddb.CloudDBZone.registerObjectClass(this.cloudDB, SPORT_RECORD_TYPE);
console.info('CloudDB initialized successfully');
} catch (err) {
console.error(`CloudDB init failed: ${JSON.stringify(err)}`);
}
}
// Add a sports record
static async addSportRecord(record: SportRecord): Promise<boolean> {
try {
await this.cloudDB.executeUpsert(SPORT_RECORD_TYPE, [record]);
return true;
} catch (err) {
console.error(`Add sport record failed: ${JSON.stringify(err)}`);
return false;
}
}
}
5. Implementing Community Features
5.1 Posting User Updates
// Community module: CommunityService.ts
import { clouddb } from '@hw-agconnect/database-ohos';
const POST_TYPE = 'CommunityPost';
interface CommunityPost {
id: string;
userId: string;
content: string;
images?: Array<string>;
likes: number;
comments: number;
createTime: number;
sportType?: string;
}
export class CommunityService {
// Create a post
static async createPost(post: CommunityPost): Promise<boolean> {
try {
await CloudDBService.cloudDB.executeUpsert(POST_TYPE, [post]);
return true;
} catch (err) {
console.error(`Create post failed: ${JSON.stringify(err)}`);
return false;
}
}
// Get trending posts
static async getHotPosts(limit: number = 10): Promise<Array<CommunityPost>> {
try {
const query = clouddb.CloudDBZoneQuery.where(POST_TYPE)
.orderByDesc('likes')
.limit(limit);
const result = await CloudDBService.cloudDB.executeQuery(query, POST_TYPE);
return result as Array<CommunityPost>;
} catch (err) {
console.error(`Get posts failed: ${JSON.stringify(err)}`);
return [];
}
}
}
5.2 Implementing the Post List UI
// Community page: CommunityPage.ets
@Component
struct PostItem {
@Prop post: CommunityPost
build() {
Column() {
Row() {
Image($r('app.media.default_avatar'))
.width(40)
.height(40)
.borderRadius(20)
.margin({ right: 10 })
Column() {
Text(`User ${this.post.userId.substring(0, 6)}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text(new Date(this.post.createTime).toLocaleString())
.fontSize(12)
.fontColor('#999')
}
}
.width('100%')
.justifyContent(FlexAlign.Start)
Text(this.post.content)
.margin({ top: 10, bottom: 10 })
.width('100%')
// Like and comment section
Row() {
Image($r('app.media.ic_like'))
.width(20)
.height(20)
.margin({ right: 5 })
Text(this.post.likes.toString())
.fontSize(14)
Image($r('app.media.ic_comment'))
.width(20)
.height(20)
.margin({ left: 15, right: 5 })
Text(this.post.comments.toString())
.fontSize(14)
}
.width('100%')
.margin({ top: 10 })
}
.padding(15)
.borderRadius(10)
.backgroundColor('#FFF')
.margin({ bottom: 10 })
.width('100%')
}
}
@Entry
@Component
struct CommunityPage {
@State posts: Array<CommunityPost> = []
aboutToAppear() {
this.loadPosts()
}
async loadPosts() {
this.posts = await CommunityService.getHotPosts()
}
build() {
Column() {
List({ space: 10 }) {
ForEach(this.posts, (post: CommunityPost) => {
ListItem() {
PostItem({ post: post })
}
})
}
.width('100%')
.layoutWeight(1)
}
.padding(15)
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
6. Event Management Features
6.1 Event Data Model
// Event module: EventService.ts
import { clouddb } from '@hw-agconnect/database-ohos';
const EVENT_TYPE = 'SportEvent';
interface SportEvent {
id: string;
title: string;
description: string;
location: string;
startTime: number;
endTime: number;
maxParticipants: number;
currentParticipants: number;
coverImage: string;
sportType: string;
creatorId: string;
createTime: number;
}
export class EventService {
// Create an event
static async createEvent(event: SportEvent): Promise<boolean> {
try {
await CloudDBService.cloudDB.executeUpsert(EVENT_TYPE, [event]);
return true;
} catch (err) {
console.error(`Create event failed: ${JSON.stringify(err)}`);
return false;
}
}
// Get upcoming events
static async getUpcomingEvents(limit: number = 5): Promise<Array<SportEvent>> {
try {
const now = new Date().getTime();
const query = clouddb.CloudDBZoneQuery.where(EVENT_TYPE)
.greaterThan('startTime', now)
.orderByAsc('startTime')
.limit(limit);
const result = await CloudDBService.cloudDB.executeQuery(query, EVENT_TYPE);
return result as Array<SportEvent>;
} catch (err) {
console.error(`Get events failed: ${JSON.stringify(err)}`);
return [];
}
}
}
6.2 Implementing the Event Details Page
// Event details page: EventDetailPage.ets
@Entry
@Component
struct EventDetailPage {
@State event: SportEvent
@State isJoined: boolean = false
async joinEvent() {
if (this.event.currentParticipants >= this.event.maxParticipants) {
prompt.showToast({ message: 'Event is full' });
return;
}
this.event.currentParticipants++;
const success = await EventService.createEvent(this.event);
if (success) {
this.isJoined = true;
prompt.showToast({ message: 'Registration successful' });
} else {
prompt.showToast({ message: 'Registration failed, please try again' });
}
}
build() {
Column() {
Image(this.event.coverImage)
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
Column() {
Text(this.event.title)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Row() {
Image($r('app.media.ic_time'))
.width(16)
.height(16)
.margin({ right: 5 })
Text(new Date(this.event.startTime).toLocaleString())
.fontSize(14)
}
.margin({ bottom: 5 })
Row() {
Image($r('app.media.ic_location'))
.width(16)
.height(16)
.margin({ right: 5 })
Text(this.event.location)
.fontSize(14)
}
.margin({ bottom: 15 })
Text(this.event.description)
.fontSize(16)
.margin({ bottom: 20 })
Row() {
Text(`Participants: ${this.event.currentParticipants}/${this.event.maxParticipants}`)
.fontSize(14)
}
.margin({ bottom: 20 })
Button(this.isJoined ? 'Registered' : 'Register Now')
.width('80%')
.enabled(!this.isJoined)
.onClick(() => this.joinEvent())
}
.padding(20)
}
.width('100%')
.height('100%')
}
}
7. Application Optimization and Release
7.1 Performance Optimization Recommendations
- Pagination Loading: Implement pagination for community posts and event lists.
- Local Caching: Use Preferences to cache frequently accessed data locally.
- Image Compression: Compress images before uploading them to cloud storage.
7.2 Preparing for Release
- Complete the application information configuration in the AGC console.
- Configure necessary permission declarations.
- Generate a signing certificate.
- Build the release version.
// Add necessary permission declarations in config.json
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.health.READ_HEALTH_DATA",
"reason": "Read sports and health data"
},
{
"name": "ohos.permission.health.WRITE_HEALTH_DATA",
"reason": "Record sports data"
},
{
"name": "ohos.permission.INTERNET",
"reason": "Access network services"
}
]
}
}
8. Conclusion
This tutorial provided a detailed guide on developing sports applications using HarmonyOS Next and AppGallery Connect. By integrating AGC's authentication, database, and other services, we implemented core features such as user systems, sports data recording, community interaction, and event management. ArkTS's declarative UI development approach significantly improves development efficiency, while HarmonyOS's distributed capabilities lay the foundation for future multi-device sports experiences.
Developers can further expand on this foundation by:
- Adding sports trajectory recording
- Implementing sports data visualization and analysis
- Developing companion apps for wearable devices
- Adding an achievement system for sports
We hope this tutorial helps developers quickly master the core technologies of HarmonyOS application development and build more outstanding sports and health applications.
Top comments (0)