《Android 3D游戏开发技术宝典——OpenGL ES 2.0》——2.3节手机自带数据库——SQLite

简介:

本节书摘来自异步社区《Android 3D游戏开发技术宝典——OpenGL ES 2.0》一书中的第2章,第2.3节手机自带数据库——SQLite,作者 吴亚峰,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.3 手机自带数据库——SQLite
Android 3D游戏开发技术宝典——OpenGL ES 2.0
上一节介绍了如何使用Preferences存储简单数据,而复杂的数据就需要存储到文件或数据库中了。 Android自带了一款轻量级的关系数据库——SQLite,其具有体积小,功能强大等诸多特点,成为嵌入式设备首选的数据库系统。本节将带领读者走进SQLite的世界,去学习如何应用SQLite数据库进行数据的增、删、改、查等基本操作。

2.3.1 初识SQLite
SQLite是一款满足ACID特性的具有完备功能的关系数据库系统,由于其设计目标为轻量级、开源、支持嵌入式使用,因此,目前已经在嵌入式设备领域被广泛采用。其运行需要的系统资源非常少,在嵌入式设备中可能只需要几百KB的内存就够了。

SQLite对主流编程语言的支持也非常全面,如C#、PHP、Java等,同时还支持ODBC接口。另外,SQLite的性能也是一流的,在一般应用情况下,其处理速度比MySQL、PostgreSQL这两款著名的开源数据库管理系统都快。

提示 SQLite的最新版本为3.7.6,发布时间是2011年4月12日。其官方网站为:http://www.sqlite.org或者http://www.sqlite.com.cn,读者可以在该网站上获取SQLite的源代码和相关文档。
虽然SQLite占用的资源非常少,但是其功能、特性与服务器级数据库相比却丝毫不差,这也是SQLite能够受到Android系统青睐的主要原因,其部分特性如下所列。

最大可以支持2TB的数据库文件。
占用资源少,一般占用250KB左右。
API非常简单,易于使用。
没有任何额外的依赖,是独立的。
源代码完全开放,可以用于任何用途。
Android系统中很多的用户数据都是存储在SQLite数据库中的,如联系人信息、通话记录、短信等,由此可见SQLite对于Android的重要性。

提示 读者要想很好地使用SQLite数据库,必须熟练掌握SQL语言。这是由于SQL已经事实上成为关系数据库操作的标准语言,市面上的关系数据库几乎无一例外都支持SQL。因此在数据库领域,有这样一句话“学好SQL,走遍天下都不怕”。

2.3.2 SQLite数据库的基本操作
一般学习数据库相关课程的时候,首先介绍的就是数据库的一些基本操作,如数据的增、删、改、查等。按照惯例,本书也首先简单介绍SQLite数据库的创建、关闭及数据的增加、删除、修改、查询等基本操作,具体如下所列。

提示 想在Android下通过Java编程对SQLite数据库进行操作,就必须要用到android.database.sqlite包下的SQLiteDatabase类,该类提供了对SQLite数据库进行基本操作的所有重要方法。
创建数据库。
创建数据库需要用到的是openDatabase方法,此方法签名为“public static SQLiteDatabase openDatabase (String path, SQLiteDatabase.CursorFactory factory, int flags)”。其中path为数据库所在的路径;factory为游标工厂;flags为标志,可以用来控制数据库的访问模式。

关闭数据库。
关闭数据库需要用到的是close方法,此方法签名为“public void close()”。在实际开发中数据库使用完毕后,一定不要忘记使用该方法关闭数据库,以防止资源的浪费。

插入数据。
插入数据可以使用insert方法,此方法签名为“public long insert (String table, String nullColumnHack, ContentValues values)”。其中table为待插入的表名,nullColumnHack通常设置为null,values为待插入的数据。

更新数据。
更新数据可以使用update方法,其签名为“public int update(String table, ContentValues values, String whereClause, String[] whereArgs)”。其中table为待更新的表名;values为待更新内容;whereClause为where子句的内容,用来进行记录筛选;whereArgs为where子句的参数。

删除数据。
删除数据可以使用delete方法,其签名为“public int delete (String table, String whereClause, String[] whereArgs)”。其中table为要操作的表名;whereClause为where子句的内容,用来进行记录筛选;whereArgs为where子句的参数。

查询数据。
查询数据可以使用query方法,其方法签名为“public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)”。其中table为要查询的表,columns为要查询的列,selection为过滤记录的子句,selectionArgs为过滤的参数值,groupBy为分组子句,having为过滤分组的子句,orderBy为记录排序子句。

提示 Android中query方法被重载了,有多个变体。这里由于篇幅所限,不再赘述,有需要的读者可以自行查阅API或其他相关资料。
执行非查询SQL。
对于不太熟悉SQL语言的初学者而言,插入、更新、删除数据可以用前面介绍的insert、update、delete方法。但对于熟练掌握SQL的开发人员而言,就是使用execSQL方法直接执行相应的SQL语句更方便了。

此方法签名为“public void execSQL (String sql)”或“public void execSQL (String sql, Object[] bindArgs)”。其中sql为需要执行的SQL语句,bindArgs为带参数SQL语句的参数值数组。

提示 需要注意的是,此方法仅支持执行非查询的SQL语句,如CREATE TABLE、 DELETE、 INSERT、UPDATE等,不能用于执行SELECT语句。
执行查询SQL。
对于熟练掌握SQL的开发人员而言,会觉得前面介绍的query方法使用过于烦琐。Android的设计人员也考虑到了这个问题,提供了支持执行查询SQL语句的rawQuery方法,其方法签名为“public Cursor rawQuery (String sql, String[] selectionArgs)”。其中sql为要执行的查询SQL语句(可以带参数),selectionArgs为查询参数的值。

提示 SQLiteDatabase类中用于数据库操作的方法还有很多,本书只是介绍了其中一些常用的,若读者有需要可以查阅API或其他相关资料进一步学习。

2.3.3 SQLite数据库的简单案例
上一小节介绍了SQLite数据库的基本操作方法,本小节将详细介绍一个使用SQLite数据库的简单案例,以使读者可以更加快速地掌握SQLite数据库的使用方法,从而在开发中进行合理地使用。本案例运行效果分别如图2-10、图2-11和图2-12所示。


8c955bf9da37bf5603efd4ea9beb85a7aea0269b


39a2391e1de7b56846d2eea2289f62e282659ba8

介绍完本案例的运行效果后,接下来将开发本案例中唯一的一个类——Sample2_4_Activity,其代码如下。

1 package com.bn.pp4;         //声明包
2 import android.app.Activity;       //引入相关类
3 ……//此处省略了部分类的引入代码,读者可自行查看随书光盘的源代码
4 import android.widget.Toast;       //引入相关类
5 public class Sample2_4_Activity extends Activity {
6  SQLiteDatabase sld;         // 声明SQLiteDatabase引用
7  public void onCreate(Bundle savedInstanceState) {  // 重写的onCreate方法
8   super.onCreate(savedInstanceState);
9   setContentView(R.layout.main);    // 跳转到主界面
10   Button b = (Button) this.findViewById(R.id.Button01);
             // 获取打开/创建数据库按钮的引用
11   b.setOnClickListener(      // 为打开/创建按钮添加监听器
12   new OnClickListener() {
13    public void onClick(View v) {
14     createOrOpenDatabase();   // 调用方法打开或创建数据库
15   }});
16   b = (Button) this.findViewById(R.id.Button02); // 获取关闭数据库按钮的引用
17   b.setOnClickListener(      // 为关闭按钮添加监听器
18   new OnClickListener() {
19    public void onClick(View v) {
20     closeDatabase();     // 调用方法关闭数据库
21   }});
22   b = (Button) this.findViewById(R.id.Button03);  // 获取添加记录按钮的引用
23   b.setOnClickListener(      // 为添加按钮添加监听器
24   new OnClickListener() {
25    public void onClick(View v) {
26     insert();      // 调用方法插入记录
27   }});
28   b = (Button) this.findViewById(R.id.Button04); // 获取删除记录按钮的引用
29   b.setOnClickListener(      // 为删除按钮添加监听器
30   new OnClickListener() {
31    public void onClick(View v) {
32     delete();        // 调用方法删除记录
33   }});
34   b = (Button) this.findViewById(R.id.Button05);  // 获取查询记录按钮的引用
35   b.setOnClickListener(      // 为查询按钮添加监听器
36   new OnClickListener() {
37    @Override
38    public void onClick(View v) {
39     query();      // 调用方法查询记录
40  }});}
41  public void createOrOpenDatabase() {  // 创建或打开数据库的方法
42   try {
43    sld = SQLiteDatabase.openDatabase(
44      "/data/data/com.bn.pp4/mydb",  // 数据库所在路径
45      null, // 游标工厂,默认为null
46      SQLiteDatabase.OPEN_READWRITE |
47      SQLiteDatabase.CREATE_IF_NECESSARY //模式为读写,不存在则创建
48    );
49            // 生成创建数据库的SQL语句
50    String sql = "create table if not exists student" +
51      "(sno char(5),stuname varchar(20)," +
52      "sage integer,sclass char(5))";
53    sld.execSQL(sql);     // 执行SQL语句
54    Toast.makeText(getBaseContext(), "成功创建数据库。", 
55      Toast.LENGTH_LONG).show();
56   } catch (Exception e) {
57    e.printStackTrace();
58  }}
59  public void closeDatabase() {     // 关闭数据库的方法
60   try {
61    sld.close();        // 关闭数据库
62    Toast.makeText(getBaseContext(), "成功关闭数据库。", 
63      Toast.LENGTH_LONG).show();
64   } catch (Exception e) {
65    e.printStackTrace();
66  }}
67  public void insert() {      // 插入记录的方法
68   try  {         // 生成插入记录的SQL语句
69    String sql = "insert into student values" +
70      "('001','Android',22,'283')";
71    sld.execSQL(sql);      // 执行SQL语句
72    Toast.makeText(getBaseContext(), "成功插入一条记录。",
73       Toast.LENGTH_LONG).show();
74   } catch (Exception e) {
75    e.printStackTrace();
76  }}
77  public void delete() {      // 删除记录的方法
78   try  {         // 生成删除所有记录的SQL语句
79    String sql = "delete from student;";
80    sld.execSQL(sql);      // 执行SQL语句
81    Toast.makeText(getBaseContext(), "成功删除所有记录。", 
82      Toast.LENGTH_LONG).show();
83   } catch (Exception e) {
84    e.printStackTrace();
85  }}
86  public void query(){       // 查询的方法
87   try {         // 生成查询记录的SQL语句
88    String sql = "select * from student where sage>?";
89    Cursor cur = sld.rawQuery(sql, new String[] { "20" });
              // 获取Cursor对象引用
90    while (cur.moveToNext()) {    // 若存在记录
91     String sno = cur.getString(0);  // 获取第一列信息
92     String sname = cur.getString(1);  // 获取第二列信息
93     int sage = cur.getInt(2);    // 获取第三列信息
94     String sclass = cur.getString(3);  // 获取第四列信息
95     Toast.makeText(
96       getBaseContext(),
97       "查询到的记录为:'" + sno + "'\t'" + sname 
98       + "'\t\t'" + sage+ "'\t'" + sclass + "'", 
99       Toast.LENGTH_LONG).show();
100    }
101    cur.close();        // 关闭Cursor
102   } catch (Exception e) {
103    e.printStackTrace();
104 }}}

第10-40行为案例中的各个按钮添加监听器,监听器中调用对应的方法来实现数据库的打 开/创建、关闭、插入、删除和查询等操作。
第41-58行为创建及打开数据库的方法,方法中首先获取了SQLiteDatabase对象的引用,并为其指定数据库的存储路径和读写模式,然后用“create table”语句来创建了一张名称为student的表。
第67-76行为向数据库中插入一条记录的方法,插入的记录内容为“‘001’,‘Android’,22,‘283’”。
第77-85行为删除数据库中所有记录的方法。
第86-104行为从数据库中查找符合条件记录的方法,首先需要获取Cursor对象的引用,并为其添加查找范围(具体范围为年龄大于20)。若查到相应记录,则将记录信息用Toast显示出来。

2.3.4 使用ContentProvider组件共享数据
前一小节介绍了SQLite数据库中的一些操作,但有时数据库中的信息不但创建其的应用程序要使用,还希望能够分享给其他应用程序使用。这时就需要使用ContentProvider组件了,ContentProvider组件的基本情况如下所列。

Android平台中每个应用程序都有自己的用户ID并在自己的进程中运行,每个进程拥有独立的运行环境,这样可以保证程序的完整性。但这也使得应用程序在需要进行资源共享和数据通信时,很不方便。为了解决这一问题,Android提供了专门用来在应用程序之间分享数据的ContentProvider组件。
ContentProvider能将应用程序中特定的数据提供给其他应用程序使用,这些数据可以来自应用程序私有文件夹下的私有数据文件,也可以来自应用程序自己私有的SQLite数据库。当然,数据的来源还有很多其他选择,ContentProvider组件本身并没有做出限制,读者可以充分发挥想象的空间。
使用ContentProvider组件共享数据的基本方式是继承ContentProvider类并重写其中的相应方法,具体情况在后面的案例中进行介绍。
别的应用程序想分享数据时需要使用ContentResolver,通过ContentResolver对象将分享的需求发送给ContentProvider组件,而不能直接调用ContentProvider组件。
下面使用ContentProvider组件将上一小节的案例进行升级,使得此案例具有分享数据给其他应用程序的能力,其具体开发步骤如下。

(1)在案例Sample2_4的com/bn/pp4包下新建MyContentProvider类,该类继承自ContentProvider类,并要实现其中所有的抽象方法,具体代码如下。

1 package com.bn.pp4;         //声明包
2 import android.content.ContentProvider;     //引入相关类
3 ……//此处省略了部分类的引入代码,读者可自行查看随书光盘的源代码
4 import android.net.Uri;        //引入相关类
5 public class MyContentProvider extends ContentProvider {  // 继承ContentProvider
6  private static final UriMatcher um;     // 声明Uri匹配引用
7  static {
8   um = new UriMatcher(UriMatcher.NO_MATCH);   // 创建UriMatcher
9   um.addURI("com.bn.pp4.provider.student", "stu", 1); // 设置匹配字符串
10  }
11  SQLiteDatabase sld;     // 声明SQLiteDatabase引用
12  public String getType(Uri uri) {
13   return null;
14  }
15          // 调用数据库的query方法时会自动调用该方法
16  public Cursor query(Uri uri, String[] projection, String selection,
17    String[] selectionArgs, String sortOrder) {
18   switch (um.match(uri)) {     // 若匹配成功
19   case 1:          // 执行操作,获取Cursor对象引用
20    Cursor cur = sld.query("student", projection, selection,
21      selectionArgs, null, null, sortOrder);
22    return cur;        // 返回Cursor对象引用
23   }
24   return null;
25  }
26  public int delete(Uri arg0, String arg1, String[] arg2) { // 空实现
27   return 0;
28  }
29  public Uri insert(Uri uri, ContentValues values) {   // 空实现
30   return null;
31  }
32  public boolean onCreate() {      // 创建数据库时自动调用该方法
33   sld = SQLiteDatabase.openDatabase(
34     "/data/data/com.bn.pp4/mydb",   // 数据库所在路径
35     null,        // 游标工厂,默认为null
36     SQLiteDatabase.OPEN_READWRITE| 
37     SQLiteDatabase.CREATE_IF_NECESSARY // 读写、若不存在则创建
38   );
39   return false;
40  }
41  public int update(Uri uri, ContentValues values, String selection,
42    String[] selectionArgs) {    // 空实现
43   return 0;
44  }
45 }

第6-10行为声明Uri匹配对象,并且设置匹配字符串。此匹配字符串在需要得到分享数据的应用程序中提供给ContentResolver使用,以进行配对。
第12-14行重写了getType方法,本案例中对getType方法没有要求,因此其返回空值。
第16-25行重写了query方法,在匹配成功后,数据需求方通过ContentResolver调用此方法查询需要的数据。
第26-31行重写了delete与insert方法,本案例中对这两个方法没有要求,因此都设置为返回空值。
第32-40行重写了onCreate方法,其功能为首先获取SQLiteDatabase对象引用,然后创建或打开数据库,为信息的分享做好准备。
第41-44行重写了update方法,本案例中对这个方法没有要求,因此设置为返回空值。
(2)仅仅是完成上面的代码还是不够的,在Android程序开发中,有一个很重要的配置文件AndroidManifest.xml。要想使用ContentProvider组件,在完成代码的开发后,还必须在该配置文件中进行相应的配置,将如下代码插入到AndroidManifest.xml文件中的application标签中。

1 <provider 
2      android:name="MyContentProvider"       <!--将调用的类名-->
3      android:authorities="com/bn/pp4.provider.student" <!--要匹配的Uri字符串-->
4  />

2.3.5 使用ContentResolver获取分享数据
升级完了Sample2_4使其具有了数据分享能力之后,就可以在别的应用程序中通过ContentResolver匹配到Sample2_4案例中的ContentProvider组件获取分享的数据了。具体的开发步骤如下所列。

(1)新建项目Sample2_4_From,将项目的包名设定为com.bn.pp4f,并新建一个继承自Activity的类ContentConsumerActivity,其代码如下。

1 package com.bn.pp4f;          //包声明
2 import android.app.Activity;        //相关类的引入
3 //……此处省略了部分相关类的引入代码,读者可自行查看随书光盘的源代码
4 import android.widget.EditText;       //相关类的引入
5 public class ContentConsumerActivity extends Activity {  
6     ContentResolver cr;         // ContentResolver的引用
7     @Override            //重写方法的标志
8     public void onCreate(Bundle savedInstanceState) {   
9         super.onCreate(savedInstanceState);     //继承父类的onCreate方法
10         setContentView(R.layout.main);     //跳转到主界面
11         cr=this.getContentResolver();   //获取ContentResolver的对象
12         //初始化查询按钮
13         Button b=(Button)this.findViewById(R.id.Button01); // Button类的引用
14         b.setOnClickListener(       //设置按钮监听
15           new OnClickListener(){       
16    @Override        //重写方法的标志
17    public void onClick(View v) {    //重写onClick方法
18     String stuname="Android";    //设置查询的字符串
19     Cursor cur=cr.query(      
20        Uri.parse("content://com.bn.pp4.provider.student/stu"), 
21        new String[]{"sno","stuname","sage","sclass"}, 
22        "stuname=?",       //查询条件
23        new String[]{stuname},     
24        "sage ASC"       
25     );         
26           while(cur.moveToNext()){     
27            String sno=cur.getString(0);   //获取学号
28            String sname=cur.getString(1);   //获取名称
29            int sage=cur.getInt(2);    //获取年龄
30            String sclass=cur.getString(3);  //获取班级
31            appendMessage(sno+"\t"+sname+"\t\t"+sage+"\t"+sclass);
32           }
33           cur.close();        //关闭ContentResolver
34  }});}           
35     public void appendMessage(String msg){       //向文本区中添加文本
36      EditText et=(EditText)this.findViewById(R.id.EditText02);//获取EditText的对象
37      et.append(msg+"\n");        //添加显示的字符串
38 }}

第8-25行主要功能为获取ContentResolver对象的引用,并给按钮添加监听器,使得按钮按下后可以通过ContentResolver匹配到Sample2_4案例中的ContentProvider组件获取需要的数据。
第26-33行功能为将获取的Sample2_4案例分享的数据显示到屏幕上的EditText控件中。
第35-38行为向EditText控件中添加文本信息的方法。
(2)Sample2_4_From案例开发完成后,运行该案例,其效果如图2-13、图2-14所示。


8daf2ddf62f3183c15d656784b2a5fa9b0d3d4c6

说明 图2-13为运行该案例后的界面效果图,图2-14为单击“获取”按钮后,通过ContentResolver匹配到Sample2_4案例中的ContentProvider组件获取数据后的效果图。

相关实践学习
使用DAS实现数据库SQL优化
本实验介绍如何通过数据库自治服务DAS对RDS MySQL高可用实例进行SQL优化,包含购买RDS实例并创建数据库、数据导入、生成并优化慢SQL、执行优化后的SQL语句等实验步骤。
相关文章
|
3月前
|
数据库 索引
深入探索数据库索引技术:回表与索引下推解析
【10月更文挑战第15天】在数据库查询优化的领域中,回表和索引下推是两个核心概念,它们对于提高查询性能至关重要。本文将详细解释这两个术语,并探讨它们在数据库操作中的作用和影响。
87 3
|
3月前
|
数据库 索引
深入理解数据库索引技术:回表与索引下推详解
【10月更文挑战第23天】 在数据库查询性能优化中,索引的使用是提升查询效率的关键。然而,并非所有的索引都能直接加速查询。本文将深入探讨两个重要的数据库索引技术:回表和索引下推,解释它们的概念、工作原理以及对性能的影响。
149 3
|
4月前
|
存储 缓存 监控
数据库优化技术:提升性能与效率的关键策略
【10月更文挑战第15天】数据库优化技术:提升性能与效率的关键策略
134 8
|
19天前
|
SQL 存储 关系型数据库
【SQL技术】不同数据库引擎 SQL 优化方案剖析
不同数据库系统(MySQL、PostgreSQL、Doris、Hive)的SQL优化策略。存储引擎特点、SQL执行流程及常见操作(如条件查询、排序、聚合函数)的优化方法。针对各数据库,索引使用、分区裁剪、谓词下推等技术,并提供了具体的SQL示例。通用的SQL调优技巧,如避免使用`COUNT(DISTINCT)`、减少小文件问题、慎重使用`SELECT *`等。通过合理选择和应用这些优化策略,可以显著提升数据库查询性能和系统稳定性。
77 9
|
1月前
|
存储 运维 OLAP
【Meetup回顾 第1期】竟是这样的国产数据库,YashanDB技术内幕曝光
YashanDB是一款基于统一内核,支持单机/主备、共享集群、分布式等多种部署方式,覆盖OLTP/HTAP/OLAP交易和分析混合负载场景的新型数据库系统;YashanDB同时提供开发平台、运维平台和迁移平台3大工具平台以满足数据全生命周期管理。
41 2
【Meetup回顾 第1期】竟是这样的国产数据库,YashanDB技术内幕曝光
|
4月前
|
存储 NoSQL 关系型数据库
数据库技术深度解析:从基础到进阶
【10月更文挑战第17天】数据库技术深度解析:从基础到进阶
169 0
|
1月前
|
关系型数据库 分布式数据库 数据库
1月17日|阿里云云谷园区,PolarDB V2.0技术沙龙,畅聊国产数据库
为了助力国产化项目顺利推进,阿里云邀请企业开发者和数据库负责人到云谷园区,与PolarDB V2.0技术专家面对面交流。扫描海报二维码报名,我们将根据信息为您申请入园。欢迎参与,共同探讨PolarDB的最新技术和应用!
|
2月前
|
人工智能 物联网 大数据
解密时序数据库的未来:TDengine Open Day技术沙龙精彩回顾
在数字化时代,开源已成为推动技术创新和知识共享的核心力量,尤其在数据领域,开源技术的涌现不仅促进了行业的快速发展,也让更多的开发者和技术爱好者得以参与其中。随着物联网、工业互联网等技术的广泛应用,时序数据库的需求愈发强烈,开源的兴起更是为这一技术的创新与普及提供了强有力的支持。
53 3
|
2月前
|
存储 JSON NoSQL
学习 MongoDB:打开强大的数据库技术大门
MongoDB 是一个基于分布式文件存储的文档数据库,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。它与 MySQL 类似,但使用文档结构而非表结构。核心概念包括:数据库(Database)、集合(Collection)、文档(Document)和字段(Field)。MongoDB 使用 BSON 格式存储数据,支持多种数据类型,如字符串、整数、数组等,并通过二进制编码实现高效存储和传输。BSON 文档结构类似 JSON,但更紧凑,适合网络传输。
88 15
|
3月前
|
负载均衡 网络协议 数据库
选择适合自己的数据库多实例负载均衡技术
【10月更文挑战第23天】选择适合自己的数据库多实例负载均衡技术需要全面考虑多种因素。通过深入的分析和评估,结合自身的实际情况,能够做出明智的决策,为数据库系统的高效运行提供有力保障。
153 61

热门文章

最新文章

  • 1
    MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
  • 2
    【01】整体试验思路,如何在有UID的情况下获得用户手机号信息,python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
  • 3
    美团面试:手机扫描PC二维码登录,底层原理和完整流程是什么?
  • 4
    【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
  • 5
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 6
    Android历史版本与APK文件结构
  • 7
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
  • 8
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 9
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 10
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡