使用 Web 工具进行 Android 开发:使用 Ionic React 进行生产的最快方式
投资Android开发可以带来巨大的设备市场份额、扩大的市场范围和高投资回报。
全球拥有超过 68 亿智能手机用户。 Android 占据全球约 70% 的市场份额,相当于约 47.6 亿用户,找到您的利基市场触手可及。这一切都是为了生成高质量、快速的应用程序。
Ionic 具有原生 Web 组件,使您能够使用 HTML、JavaScript 和 CSS 等熟悉的工具实现高性能和高质量的 Android 应用程序,同时利用 Capacitor 来利用原生功能。
这不仅仅是一个关于 Ionic 的教程;它是关于构建高质量和高性能的 Android 应用程序以供生产使用。
本文是系列的简介,其中我们将介绍以 React 作为前端的 Ionic 基础知识,稍后使用 Capacitor 探索本机技术和 Web 技术之间的桥梁。
Ionic 框架:简介
不同技术之间的运行时或桥梁并不是什么新鲜事!
以 Node.js 为例。只有通过 Node,JavaScript 才能成为一种系统语言。
[JS] [byte code] [Node] --> [N API] --> [C/C++ modules] [bindings] [Cpp][V8][libuv] [OS]
考虑使用 HTML、JavaScript 和 CSS 作为通过 Web 视图的视图的混合桌面应用程序。 Go Wails,一个非常高性能的桌面开发框架,就是基于这个想法。同样,Rust Tauri 应用程序也遵循这一原则。
绑定已经在移动世界中存在并经过了一段时间的测试,例如 React Native 和 NativeScript。
开发界正在认识到的不仅仅是 UI 的重要性,还有美观且响应灵敏的 UI 的重要性。该领域还没有像 Web 技术一样先进的框架。
Android 原生开发正在向 React 和 Kotlin 中可组合 UI 的方向转变,远离不太受青睐的 XML。
这种趋势带来了两全其美的效果:原生速度和漂亮的可组合 UI。这就是 Ionic 在同类产品中脱颖而出的地方。不同之处在于 Ionic 很容易掌握——我在不到一个月的时间内成功构建了客户的应用程序。
设置
创建一个新的项目文件夹并运行以下命令:
npx ionic start
这将引导您完成 Ionic 设置。选择React作为本文的前端框架。
Ionic 仍然使用 Webpack 和 Create React App (CRA),因为 Vite 尚不支持 Ionic Web 组件的核心 Stencil.js。
一切安装完毕后,在 VSCode 中打开项目。我更喜欢删除 npm 并使用 pnpm (此步骤是可选的)。如果你也想这样做:
删除node_modules文件夹。
删除 package-lock.json 文件,而不是 package.json。
运行 pnpm install。
要运行 Ionic 应用程序,请使用:
npx ionic serve
Ionic CLI 将处理一切。您还可以使用 --lab 选项进行类似手机的预览(注意,这不是 Android 或 iOS 模拟器,而是“视图”):
pnpm add -D @ionic/lab npx ionic serve --lab
这使我们能够预览 UI 在类似手机的视图上的外观。
回顾结构
我假设您已在您选择的 IDE 中打开该项目。如果你没有 React 经验,这可能有点困难。我建议学习基本的 React 教程并学习 React 路由器。
入口点是index.tsx中的标准React应用程序渲染:
root.render( <React.StrictMode> <App/> </React.StrictMode> );
在 App.tsx 中,它是一个路由器和选项卡导航栏,使用 Ionic 路由器和组件。 Ionic 组件是使用 Stencil.js 构建的原生 Web 组件,旨在看起来像移动应用程序。
Ionic 提供 CSS 文件和主题来匹配 iOS 和 Android 的标准。在 HTML 上使用 Ionic 组件以获得自然的移动应用程序外观和感觉。
让我们从路由器开始分解 App.tsx。它的工作原理与 React Web 路由器类似,将路径匹配到组件并在导航上渲染匹配的组件。
import Tab1 from './pages/Tab1'; import Tab2 from './pages/Tab2'; import Tab3 from './pages/Tab3'; <IonRouterOutlet> <Route exact path="/tab1"> <Tab1 /> </Route> <Route exact path="/tab2"> <Tab2 /> </Route> <Route path="/tab3"> <Tab3 /> </Route> <Route exact path="/"> <Redirect to="/tab1" /> </Route> </IonRouterOutlet>
如果您熟悉后端,路径就像一个端点,而组件是一个处理程序。
<IonTabBar slot="bottom"> <IonTabButton tab="tab1" href="/tab1"> <IonIcon aria-hidden="true" icon={triangle} /> <IonLabel>Tab 1</IonLabel> </IonTabButton> <IonTabButton tab="tab2" href="/tab2"> <IonIcon aria-hidden="true" icon={ellipse} /> <IonLabel>Tab 2</IonLabel> </IonTabButton> <IonTabButton tab="tab3" href="/tab3"> <IonIcon aria-hidden="true" icon={square} /> <IonLabel>Tab 3</IonLabel> </IonTabButton> </IonTabBar>
IonTabBar 在提供的插槽(在我们的应用程序的底部)处创建一个选项卡栏。神奇之处在于选项卡按钮:使用 href 触发路由器。所有正常的 React 代码,都包含在 Ionic 组件中。
关注其中一个标签页;它们本质上只是页面。
<IonPage> <IonHeader> <IonToolbar> <IonTitle>Tab 1</IonTitle> </IonToolbar> </IonHeader> <IonContent fullscreen> <IonHeader collapse="condense"> <IonToolbar> <IonTitle size="large">Tab 1</IonTitle> </IonToolbar> </IonHeader> <ExploreContainer name="Tab 1 page" /> </IonContent> </IonPage>
使用 Ionic 页面组件可以开箱即用地处理诸如滚动和响应能力之类的事情。
Ionic 页面的标准结构包括带有工具栏的标题和内容区域,与大多数移动应用程序类似。
标题:
<IonHeader> <IonToolbar> <IonTitle>Tab 1</IonTitle> </IonToolbar> </IonHeader>
内容区:
<IonContent fullscreen> <IonHeader collapse="condense"> <IonToolbar> <IonTitle size="large">Tab 1</IonTitle> </IonToolbar> </IonHeader> <ExploreContainer name="Tab 1 page" /> </IonContent>
The content area occupies most of the screen, where most of the application lives. The ExploreContainer acts as a slot; we can conditionally render components based on the name prop.
<ExploreContainer name="Tab 1 page" />
When name is "Tab 1," we render a component for that tab. You can hard code components for each tab, but the slot method is more flexible and composable.
For example, open the ExploreContainer component under the components folder and create three new components:
const Tab1Content = () => { return ( "I am tab 1 content" ); } const Tab2Content = () => { return ( "I am tab 2 content" ); } const Tab3Content = () => { return ( "I am tab 3 content" ); }
Now update the container to conditionally render based on the name prop:
<div className="container"> {name.includes("Tab 1") ? <Tab1Content /> : name.includes("Tab 2") ? <Tab2Content /> : <Tab3Content />} </div>
This is just an example; you can create an easy-to-follow pattern matching method. The updated preview should show "I am tab x content" based on the tab clicked.
This application is still web-based. We haven't installed or initialized Capacitor, which is responsible for turning our application into a native app.
Capacitor is a cross-platform native runtime for web apps, allowing us to create cross-platform iOS, Android, and Progressive Web Apps with JavaScript, HTML, and CSS.
Enabling Capacitor in Ionic
First, install Capacitor:
pnpm add @capacitor/core pnpm add -D @capacitor/cli
Next, initialize the Capacitor configuration file:
npx cap init
The package ID should uniquely identify your app. We use an inverted domain name, e.g., com.example.app.
Capacitor is initialized. Run the following commands to install a Capacitor platform and primitive plugins:
pnpm add @capacitor/android pnpm add @capacitor/app @capacitor/haptics @capacitor/keyboard @capacitor/status-bar
The following command will create the native android project structure and files in your ionic project:
npx cap add android
Important: Build the web app:
pnpm run build
to avoid this error before we run sync
[error] Could not find the web assets directory: .\build. ... More info: https://capacitorjs.com/docs/basics/workflow#sync-your-project
Once the build is finished, you can sync, copying the built web app; into the native webview:
npx cap sync
Believe it or not, we are ready to either build or preview the native application in an emulator.
We'll dive deeper into Capacitor and native development, environment setup, etc., in the next article.
Since we are still getting a feel for Ionic, let's play with a few Ionic components and wrap up with a simple application example.
PokeApp Example
You can easily find Ionic components in the documentation.
We'll implement a simple app that fetches Pokémon data from the PokeAPI, for a compact card view, and then build it into an APK.
From the results, we can already see how decent the app looks with no styling—thanks to the power of Ionic components.
Open the ExploreContainer component, and we'll work on Tab 2.
Update the component and add the following:
const BASE_LINK = "https://pokeapi.co/api/v2/pokemon/" const Tab2Content = () => { const [pokemon, setPokemon] = useState("pikachu") useEffect(()=> { if(pokemon != ""){ fetch(BASE_LINK + pokemon).then(async(poke)=> { console.log(await poke.json()) }).catch((err)=>console.log(err)) } }, [pokemon]) // add some padding to the div below return ( <div style={{padding: ".5em"}}> I am tab 2 content </div> )}
We've added a state to track the Pokémon we want to look up, with the default being Pikachu:
const [pokemon, setPokemon] = useState("pikachu")
On load, we fetch the Pokémon data from the PokeAPI:
useEffect(()=> { if(pokemon != ""){ fetch(BASE_LINK + pokemon).then(async(poke)=> { console.log(await poke.json()) }).catch((err)=>console.log(err)) } }, [pokemon])
The useEffect hook runs twice in React strict mode.
Instead of logging our result, let's turn it into a state so we can use it in our card component.
First, add a new useState under the Pokémon one:
const [showResults, setResults] = useState()
Then, update the useEffect to set the results::
useEffect(()=> { if(pokemon != ""){ fetch(BASE_LINK + pokemon).then(async(poke)=> { const results = await poke.json() const {front_default} = results.sprites setResults({front_default}) }).catch((err)=> console.log(err)) } }, [pokemon])
The PokeAPI returns a lot of data. We are interested in the Pokémon image, specifically the front-facing image in the sprites object:
const results = await poke.json() const {front_default} = results.sprites setResults({front_default})
If you are familiar with React, you know we have created the re-render on state change loop already. Now, we just need to consume the data:
return ( <div style={{padding: ".5em"}}> <IonCard> <IonCardHeader> <IonCardTitle>{pokemon}</IonCardTitle> </IonCardHeader> <IonCardContent> <img src={showResults ? showResults.front_default : ""} /> </IonCardContent> </IonCard> </div> )
We use an Ion card component to show the retrieved image:
<IonCardContent> <img src={showResults ? showResults.front_default : ""} /> </IonCardContent>
We have a basic structure already, but we can only show the default Pokémon. We need a way to accept user input (a Pokémon name) and make a fetch request based on that input.
The basic React approach is to have an input element bound to a useState value, updating it on onChange. However, in our case, this is problematic because every keystroke will trigger our useEffect, making multiple erroneous requests to the PokeAPI.
Instead, we need the user to type fully and press a search button to initiate the API call. Copy the code below and paste it on top of the Ion card:
<IonItem> <IonInput aria-label="Pokemon" value={pokemon} ref={pokeNameref}></IonInput> </IonItem> <IonButton onClick={()=> PokeSearch() }>search</IonButton> </IonItem>
From the code above, we need two things: a useRef pointing to our Ion input and a PokeSearch function triggered by an Ion button.
const Tab2Content = () => { const [pokemon, setPokemon] = useState("pikachu") const [showResults, setResults] = useState<any>() const pokeNameref = useRef<any>(null) const PokeSearch = () => { if(pokeNameref.current){ console.log(pokeNameref.current.value) setPokemon(pokeNameref.current.value.toLocaleLowerCase()) } } .... }
The code below is responsible for updating the state, triggering the effect
if(pokeNameref.current){ console.log(pokeNameref.current.value) setPokemon(pokeNameref.current.value.toLocaleLowerCase()) }
The entire component:
const Tab2Content = () => { const [pokemon, setPokemon] = useState("pikachu") const [showResults, setResults] = useState<any>() const pokeNameref = useRef<any>(null) const PokeSearch = () => { if(pokeNameref.current){ console.log(pokeNameref.current.value) setPokemon(pokeNameref.current.value.toLocaleLowerCase()) } } useEffect(()=> { if(pokemon != ""){ fetch(BASE_LINK + pokemon).then(async(poke)=> { const results = await poke.json() console.log(results.sprites) const {front_default} = results.sprites setResults({front_default}) }).catch((err)=> console.log(err)) } }, [pokemon]) return ( <div style={{padding: ".5em"}}> <IonItem> <IonInput aria-label="Pokemon" value={pokemon} ref={pokeNameref}></IonInput> </IonItem> <IonButton onClick={()=> PokeSearch() }>search</IonButton> <IonCard> <IonCardHeader> <IonCardTitle>{pokemon}</IonCardTitle> </IonCardHeader> <IonCardContent> <img src={showResults ? showResults.front_default : ""} /> </IonCardContent> </IonCard> </div> ) }
Our simple PokeApp is complete. Make sure ionic serve --lab is running and type a few Pokémon names, such as:
bulbasaur dragonite
If everything is set up correctly, the Pokémon should change on search.
Not a life-changing application, but enough for learning Ionic. The next step requires Android Studio . Download it and follow the defaults while installing it.
PokeApp to APK
If you have never seen Android Studio, it’s probably the most complex IDE and has a steep learning curve!
I suggest following the defaults on installation and letting it run its course. My only suggestion is to select the option to install an emulator, which makes it easier to build and review the APK before bundling it.
When you download Android Studio for the first time, it'll download a lot of dependencies and set up Gradle, which may take some time. Let it do its thing. Gradle is a build tool for Android, similar to how we use Webpack or Vite in web development.
When you are ready and Android Studio is installed, navigate to our PokeApp in the terminal.
As an extra precaution, build and sync before opening the project in Android Studio to ensure there are no errors:
pnpm run build npx cap sync
If the build is successful, we can rest assured there are no errors in our application. Next, open the project in Android Studio:
npx cap open android
Let the Gradle processes run:
When Gradle is done, try running the app in an emulator (top middle) in the IDE. If the app runs on the emulator, you can be sure it'll bundle to a standalone APK:
Check this extensive link for more ways to debug and run your APK: android studio run
Notes on Building the APK
There are a few steps involved in building an actual production APK for the Google Play Store, from setting up an Android console to creating banner images, which are tedious but essential tasks.
Note: The Android development account is a one-time fee. You can buy and set it up on Google Console.
Design, search keywords, and banners are beyond coding. This series is about getting the coding part right! I promise everything else will fall into place with practice and getting used to the tediousness of the Google Play Console.
In short, I will skip the Google Play Console for a few reasons:
It takes a while (2 weeks minimum) to get approved.
When approved, the APK goes through a vetting process (takes time, may fail).You can't submit an APK on Google Console unless you have banners and icons.
There is a lot of editing and icon generation for different screens.
These reasons make it impractical to include in a tutorial. But rest assured, what I will show you in this and upcoming articles will prepare you to build production-ready applications to publish in any store besides Google or for self-hosting.
However, if you already have a Google Play account, there are many articles and videos on publishing an Ionic Android app.
For our case, as long as we can generate a debug APK file and install it on an emulator or real phone, the other steps are just a Google search away!
Because this process is tedious, I will dedicate a separate article in this series to go through Android Studio, sign an APK, and build a release. For now, a debug APK will suffice as this article is already long.
Generating a debug apk
Look at your Android Studio top bar left; after the Android icon, there should be a hamburger menu button. Select to expand the menu. The build option is hidden there:
If the APK is generated successfully, a popup should show at the bottom right with a locate option, which will open the explorer to the APK path. You can share or install it on an Android device!
If you want to create a signed APK, the full production deal, Google has an extensive documentation
This was a high-level overview. We will go deeper with each article in the series.
在本文中,我们介绍了使用 Web 工具进行 Android 开发,我们选择的框架是 Ionic。我们介绍了 Ionic 和 Ionic 组件的基础知识、如何设置本机运行时桥 Capacitor,并构建了一个调试 APK。
如果您准备好深入了解 Capacitor,可以在这里找到下一篇文章:Capacitor JS:Web 技术与原生 — Android、IOS、PWA 之间的桥梁
这只是开始。
如果您对更长、独家、实用的内容感兴趣,我有旨在提高您在 ko-fi 平台上的编程技能的等级和帖子。
以上是使用 Web 工具进行 Android 开发:使用 Ionic React 进行生产的最快方式的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。
