DEV Community

linzhongxue
linzhongxue

Posted on

Comprehensive Guide to Developing a Smart News Client Based on HarmonyOS Next

Comprehensive Guide to Developing a Smart News Client Based on HarmonyOS Next

1. Project Introduction and Development Preparation

In this era of information explosion, an excellent news application needs to feature fast response times, personalized recommendations, and a smooth user experience. Leveraging the powerful capabilities of HarmonyOS Next and the backend services of AppGallery Connect, we will create a modern news reading application.

Development Environment Requirements

  • DevEco Studio 4.0 Beta1 or later
  • HarmonyOS SDK API 10
  • An activated Huawei Developer account
  • Enabled authentication services and cloud database in the AppGallery Connect project

Project Initialization Steps

  1. Create a new project in DevEco Studio, selecting the "Application" template.
  2. Choose "Phone" as the device type and "Empty Ability" as the template.
  3. Name the project "NewsHub" and ensure ArkTS is selected as the language.
// Example of basic configuration in app.json5  
{  
  "app": {  
    "bundleName": "com.example.newshub",  
    "vendor": "example",  
    "versionCode": 1,  
    "versionName": "1.0.0",  
    "icon": "$media:app_icon",  
    "label": "$string:app_name"  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

2. UI Design and Implementation

Developing the News List Page

The news list is the core interface of the application. We will use the List component to implement a high-performance scrolling list that supports pull-to-refresh and infinite scrolling.

// NewsList.ets  
@Component  
struct NewsList {  
  @State newsItems: Array<NewsItem> = [] // Array for news data  
  @State isLoading: boolean = false     // Loading state  

  build() {  
    Column() {  
      // Top title bar  
      Row() {  
        Text('Today\'s News')  
          .fontSize(24)  
          .fontWeight(FontWeight.Bold)  
          .margin({ left: 16 })  
      }  
      .width('100%')  
      .height(56)  
      .backgroundColor('#2196F3')  

      // Main news list  
      List({ space: 8 }) {  
        ForEach(this.newsItems, (item: NewsItem) => {  
          ListItem() {  
            NewsCard({ newsItem: item }) // Custom news card component  
          }  
        })  
      }  
      .onScrollIndex((start, end) => {  
        // Load more when scrolling near the bottom  
        if (end >= this.newsItems.length - 3 && !this.isLoading) {  
          this.loadMoreNews()  
        }  
      })  
      .width('100%')  
      .layoutWeight(1)  
    }  
    .width('100%')  
    .height('100%')  
    .onAppear(() => {  
      this.fetchNewsData() // Fetch data when the page appears  
    })  
  }  

  // Method to fetch news data  
  private fetchNewsData() {  
    this.isLoading = true  
    // In a real project, this would call a network request  
    setTimeout(() => {  
      this.newsItems = mockNewsData // Use mock data  
      this.isLoading = false  
    }, 1000)  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

3. Integrating AppGallery Connect Services

1. User Authentication Integration

Using AppGallery Connect's authentication service, we can quickly implement user login functionality, supporting various methods such as Huawei ID and phone number login.

// AuthService.ets  
import agconnect from '@hw-agconnect/api-ohos';  
import '@hw-agconnect/auth-ohos';  

export class AuthService {  
  // Initialize authentication service  
  static init() {  
    try {  
      agconnect.instance().init();  
    } catch (error) {  
      console.error('AGC initialization failed:', error);  
    }  
  }  

  // Huawei ID login  
  static async huaweiLogin(): Promise<boolean> {  
    try {  
      const authService = agconnect.auth().getInstance();  
      const user = await authService.signIn();  
      console.log('Login successful:', user);  
      return true;  
    } catch (error) {  
      console.error('Login failed:', error);  
      return false;  
    }  
  }  

  // Get current user  
  static getCurrentUser() {  
    return agconnect.auth().getInstance().getCurrentUser();  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

2. Cloud Database Integration

Use AppGallery Connect's cloud database to store user favorites and personalized settings.

// CloudDBService.ets  
import agconnect from '@hw-agconnect/api-ohos';  
import '@hw-agconnect/database-ohos';  

// Define the favorite news data model  
interface FavoriteNews {  
  id: string;  
  userId: string;  
  newsId: string;  
  timestamp: number;  
}  

export class CloudDBService {  
  private static cloudDB: any;  

  // Initialize the cloud database  
  static async init() {  
    if (!this.cloudDB) {  
      const agc = agconnect.instance();  
      this.cloudDB = await agc.cloudDB({  
        name: 'NewsDB',  
        models: [FavoriteNews]  
      });  
    }  
  }  

  // Add a favorite  
  static async addFavorite(newsId: string): Promise<boolean> {  
    const userId = AuthService.getCurrentUser()?.uid;  
    if (!userId) return false;  

    const favorite: FavoriteNews = {  
      id: `${userId}_${newsId}`,  
      userId,  
      newsId,  
      timestamp: new Date().getTime()  
    };  

    try {  
      await this.cloudDB.upsert(favorite);  
      return true;  
    } catch (error) {  
      console.error('Failed to add favorite:', error);  
      return false;  
    }  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

4. News Detail Page and Interactive Features

1. Implementing the Detail Page

The news detail page displays the full content and supports interactive features like favoriting and sharing.

// NewsDetail.ets  
@Component  
struct NewsDetail {  
  @State newsItem: NewsItem | null = null  
  @State isFavorite: boolean = false  

  build() {  
    Column() {  
      // Back button and title  
      Row() {  
        Image($r('app.media.ic_back'))  
          .onClick(() => router.back())  
        Text(this.newsItem?.title || 'News Details')  
          .fontSize(20)  
          .margin({ left: 16 })  
      }  
      .width('100%')  
      .padding(16)  

      // News content area  
      Scroll() {  
        Column() {  
          Text(this.newsItem?.title || '')  
            .fontSize(22)  
            .fontWeight(FontWeight.Bold)  
            .margin({ bottom: 16 })  

          Text(`Published: ${this.newsItem?.time || ''}`)  
            .fontSize(14)  
            .fontColor('#666')  
            .margin({ bottom: 24 })  

          Image(this.newsItem?.image || '')  
            .width('100%')  
            .aspectRatio(1.78)  
            .margin({ bottom: 24 })  

          Text(this.newsItem?.content || '')  
            .fontSize(16)  
            .lineHeight(24)  
        }  
        .padding(16)  
      }  
      .layoutWeight(1)  

      // Bottom action bar  
      Row() {  
        Icon(this.isFavorite ? $r('app.media.ic_favorite') : $r('app.media.ic_favorite_border'))  
          .onClick(() => this.toggleFavorite())  
          .margin({ right: 24 })  

        Icon($r('app.media.ic_share'))  
          .onClick(() => this.shareNews())  
      }  
      .width('100%')  
      .height(56)  
      .justifyContent(FlexAlign.Center)  
    }  
  }  

  private toggleFavorite() {  
    if (!this.newsItem) return;  

    if (this.isFavorite) {  
      // Logic to remove favorite  
    } else {  
      CloudDBService.addFavorite(this.newsItem.id).then(success => {  
        if (success) this.isFavorite = true;  
      });  
    }  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

5. Performance Optimization and Testing

1. Lazy Loading for Images

Implement lazy loading for images in the news list to reduce initial rendering resource consumption.

// LazyImage.ets  
@Component  
struct LazyImage {  
  @Prop src: string  
  @State loaded: boolean = false  

  build() {  
    Stack() {  
      if (!this.loaded) {  
        // Placeholder image while loading  
        Image($r('app.media.placeholder'))  
          .width('100%')  
          .aspectRatio(1.78)  
      }  

      Image(this.loaded ? this.src : '')  
        .width('100%')  
        .aspectRatio(1.78)  
        .onComplete(() => {  
          this.loaded = true  
        })  
    }  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

2. Offline Caching Strategy

Use HarmonyOS's data management capabilities to implement offline caching for news data.

// CacheManager.ets  
import dataPreferences from '@ohos.data.preferences';  

export class CacheManager {  
  private static prefs: dataPreferences.Preferences | null = null;  

  // Initialize cache  
  static async init() {  
    try {  
      this.prefs = await dataPreferences.getPreferences(globalThis.context, 'newsCache');  
    } catch (error) {  
      console.error('Cache initialization failed:', error);  
    }  
  }  

  // Save news list  
  static async saveNewsList(news: Array<NewsItem>) {  
    if (!this.prefs) await this.init();  
    try {  
      await this.prefs.put('latestNews', JSON.stringify(news));  
      await this.prefs.flush();  
    } catch (error) {  
      console.error('Failed to save cache:', error);  
    }  
  }  

  // Get cached news list  
  static async getCachedNews(): Promise<Array<NewsItem>> {  
    if (!this.prefs) await this.init();  
    try {  
      const cached = await this.prefs.get('latestNews', '[]');  
      return JSON.parse(cached as string);  
    } catch (error) {  
      console.error('Failed to read cache:', error);  
      return [];  
    }  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

6. Project Build and Release

1. Build Configuration

Configure build parameters in the project's build-profile.json5 to ensure proper packaging.

{  
  "app": {  
    "signingConfigs": [  
      {  
        "name": "release",  
        "material": {  
          "certpath": "signing/release.p12",  
          "storePassword": "yourpassword",  
          "keyAlias": "release",  
          "keyPassword": "yourpassword",  
          "signAlg": "SHA256withECDSA",  
          "profile": "signing/release.p7b",  
          "type": "pkcs12"  
        }  
      }  
    ],  
    "products": [  
      {  
        "name": "default",  
        "signingConfig": "release",  
        "compileSdkVersion": 10,  
        "compatibleSdkVersion": 10  
      }  
    ]  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

2. Publishing to AppGallery

  1. In DevEco Studio, select Build > Generate Key and CSR to generate a signing key.
  2. After configuring the signing information, select Build > Build HAP(s)/APP(s) > Build APP.
  3. Log in to AppGallery Connect, navigate to "My Apps," and create a new app.
  4. Upload the built APP package, fill in the app information, and submit for review.

7. Conclusion and Extensions

Through this tutorial, we have completed the development of a full-featured news client based on HarmonyOS Next, covering UI design, data management, and cloud service integration. This project can be further extended with:

  1. Personalized recommendation algorithms
  2. Dark mode switching
  3. News comment functionality
  4. Push notification services

HarmonyOS Next, combined with AppGallery Connect, provides developers with powerful tools and services, significantly improving development efficiency and application quality. We hope this guide helps you quickly get started with HarmonyOS app development.

Top comments (0)