美文网首页
Android学习:四大组件、六大布局、五大存储

Android学习:四大组件、六大布局、五大存储

作者: 木马不在转 | 来源:发表于2017-07-27 15:30 被阅读331次

    一.四大组件:

    Android四大组件分别为activityservicecontent providerbroadcast receiver

    1、activity

    (1)一个Activity通常就是一个单独的屏幕(窗口)。
    (2)Activity之间通过Intent进行通信。
    (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity

    2、service

    (1)service用于在后台完成用户指定的操作。service分为两种:

    • started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
    • bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。

    (2)startService()bindService()区别:

    • started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。

    • 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。

    (3)开发人员需要在应用程序配置文件中声明全部的service,使用<service></service>标签。

    (4)Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。

    3、content provider

    (1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。

    (2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。

    (3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。

    (4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。

    (5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。

    4、broadcast receiver

    (1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activityserice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

    (2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。

    (3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

    二.六大布局:

    六大界面布局方式包括:
    LinearLayout(线性布局)
    RelativeLayout(相对布局)
    TableLayout(表格布局)
    FrameLayout(帧布局)
    GridLayout(网格布局)
    AbsoluteLayout(绝对布局)

    1. LinearLayout 线性布局

    LinearLayout容器中的组件一个挨一个排列,通过控制android:orientation属性,可控制各组件是横向排列还是纵向排列。
    LinearLayout的常用XML属性及相关方法:

    XML属性 相关方法 说明
    android:gravity setGravity(int) 设置布局管理器内组件的对齐方式
    android:orientation setOrientation(int) 设置布局管理器内组件的排列方式,可以设置为horizontal、vertical两个值之一

    其中,gravity属性支持top, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal。也可以同时指定多种对齐方式的组合。

    LinearLayout子元素支持的常用XML属性及方法:

    XML属性 说明
    android:layout_gravity 指定该子元素在LinearLayout中的对齐方式
    android:layout_weight 指定子元素在LinearLayout中所占的权重
    2. TableLayout表格布局

    TableLayout继承自Linearout,本质上仍然是线性布局管理器。表格布局采用行、列的形式来管理UI组件,并不需要明确地声明包含多少行、多少列,而是通过添加TableRow、其他组件来控制表格的行数和列数。
    每向TableLayout中添加一个TableRow就代表一行;
    每向TableRow中添加一个一个子组件就表示一列;
    如果直接向TableLayout添加组件,那么该组件将直接占用一行;

    在表格布局中,可以为单元格设置如下三种行为方式:

    • Shrinkable:该列的所有单元格的宽度可以被收缩,以保证该表格能适应父容器的宽度;
    • Strentchable:该列所有单元格的宽度可以被拉伸,以保证组件能完全填满表格空余空间;
    • Collapsed:如果该列被设置为Collapsed,那么该列的所有单元格会被隐藏;

    TableLayout的常用XML属性及方法

    XML属性 相关方法 说明
    android:collapseColumns setColumns(int, boolean) 设置需要被隐藏的列的序号,多个序号间用逗号分隔
    android:shrinkColumns setShrinkAllColumns(boolean) 设置需要被收缩的列的序号
    android:stretchColumns setStretchAllColumns(boolean) 设置允许被拉伸的列的序号
    3. FrameLayout帧布局

    FrameLayout直接继承自ViewGroup组件。帧布局为每个加入其中的组件创建一个空白的区域(称为一帧),每个子组件占据一帧,这些帧会根据gravity属性执行自动对齐。

    FrameLayout的常用XM了属性及方法

    XML属性 相关方法 说明
    android:foreground setForeground(Drawable) 设置该帧布局容器的前景图像
    android:foregroundGravity setForeGroundGraity(int) 定义绘制前景图像的gravity属性
    4. RelativeLayout相对布局

    RelativeLayout的XML属性及相关方法说明

    XML属性 相关方法 说明
    android:gravity setGravity(int) 设置布局管理器内组件的对齐方式
    android:ignoreGravity setIgnoreGravity(int) 设置哪个组件不受gravity属性的影响

    为了控制该布局容器的各子组件的布局分布,RelativeLayout提供了一个内部类:RelativeLayout.LayoutParams
    RelativeLayout.LayoutParams里只能设为boolean的XML属性:

    XML属性 说明
    android:layout_centerHorizontal 设置该子组件是否位于布局容器的水平居中
    android:layout_centerVertical 设置该子组件是否位于布局容器的竖直居中
    android:layout_centerParent 设置该子组件居中
    android:layout_alignParentBottom 设置该子组件距底部多少
    android:layout_alignParentLeft 设置该子组件距左边多少
    android:layout_alignParentRight 设置该子组件距右边多少
    android:layout_alignParentTop 设置该子组件距顶部多少

    RelativeLayout.LayoutParams里属性值为其他UI组件ID的XML属性

    XML属性 说明
    android:layout_toRightOf 控制该子组件位于给出ID组件的右侧
    android:layout_toLeftOf 控制该子组件位于给出ID组件的左侧
    android:layout_above -
    android:layout_below -
    android:layout_alignTop -
    android:layout_alignBottom -
    android:layout_alignRight -
    android:layout_alignRight -
    5. Android 4.0新增的网格布局GridLayout

    GridLayoutAndroid4.0增加的网格布局控件,与之前的TableLayout有些相似,它把整个容器划分为rows × columns个网格,每个网格可以放置一个组件。性能及功能都要比tablelayout好,比如GridLayout布局中的单元格可以跨越多行,而tablelayout则不行,此外,其渲染速度也比tablelayout要快。

    GridLayout提供了setRowCount(int)setColumnCount(int)方法来控制该网格的行和列的数量。

    GridLayout常用的XML属性和方法说明:

    XML属性 相关方法 说明
    android:alignmentMode setAlignmentMode(int) 设置该布局管理器采用的对齐模式
    android:columnCount setColumnCount(int) 设置该网格的列数量
    android:columnOrderPreserved setColumnOrderPreserved(boolean) 设置该网格容器是否保留序列号
    android:roeCount setRowCount(int) 设置该网格的行数量
    android:rowOrderPreserved setRowOrderPreserved(boolean) 设置该网格容器是否保留行序号
    android:useDefaultMargins setUseDefaultMargins(boolean) 设置该布局管理器是否使用默认的页边距

    为了控制GridLayout布局容器中各子组件的布局分布,GridLayout提供了一个内部类:GridLayout.LayoutParams,来控制Gridlayout布局容器中子组件的布局分布。

    XML属性 说明
    android:layout_column 设置该组件在GridLayout的第几列
    android:layout_columnSpan 设置该子组件在GridLayout横向上跨几列
    android:layout_gravity 设置该子组件采用何种方式占据该网格的空间
    android:layout_row 设置该子组件在GridLayout的第几行
    android:layout_rowSpan 设置该子组件在GridLayout纵向上跨几行
    6. AbsoluteLayout绝对布局

    Android不提供任何布局控制,而是由开发人员自己通过X坐标、Y坐标来控制组件的位置。每个组件都可指定如下两个XML属性:

    • layour_x;
    • layout_y;

    绝对布局已经过时,不应使用或少使用。

    界面布局类型的选择和性能优化

    首先得明确,界面布局类型的嵌套越多越深越复杂,会使布局实例化变慢,使Activity的展开时间延长。建议尽量减少布局嵌套,尽量减少创建View对象的数量。

    1 . 减少布局层次,可考虑用RelativeLayout来代替LinearLayout。通过Relative的相对其他元素的位置来布局,可减少块状嵌套;

    2 . 另一种减少布局层次的技巧是使用 <merge />标签来合并布局;

    3 . 重用布局。Android支持在XML中使用 <include />标签, <include />通过指定android:layout属性来指定要包含的另一个XML布局。
    如:

    <include android:id="@+id/id1" android:layout="@layout/mylayout">
    <include android:id="@+id/id2" android:layout="@layout/mylayout">
    <include android:id="@+id/id3" android:layout="@layout/mylayout">
    

    三.五大存储:

    Android中,可供选择的存储方式有SharedPreferences、文件存储、SQLite数据库方式、内容提供器(Content provider)和网络。

    1.SharedPreferences方式

    Android提供用来存储一些简单的配置信息的一种机制,例如,一些默认欢迎语、登录的用户名和密码等。其以键值对的方式存储,使得我们可以很方便的读取和存入.

    保存:

     @Override   
        protected void onStop(){   
            super.onStop();   
              
            SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //首先获取一个SharedPreferences对象   
            settings.edit()   
                    .putString(NAME, field_name.getText().toString())   
                    .putString(PASSWORD, filed_pass.getText().toString())   
                    .commit();   
        } //将用户名和密码保存进去   ```
    获取:
    

    SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //获取一个SharedPreferences对象
    String name = settings.getString(NAME, ""); //取出保存的NAME
    String password = settings.getString(PASSWORD, ""); //取出保存的PASSWORD

     **注意**:`Preferences`只能在同一个包内使用,不能在不同的包之间使用。
    
    ######2.文件存储方式
    
    在`Android`中,其提供了`openFileInput` 和 `openFileOuput `方法读取设备上的文件,下面看个例子代码,具体如下所示: 
                 `String FILE_NAME = "tempfile.tmp";`  //确定要操作文件的文件名
                 `FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); `//初始化
                 `FileInputStream fis = openFileInput(FILE_NAME);` //创建写入流
    
       上述代码中两个方法只支持读取该应用目录下的文件,读取非其自身目录下的文件将会抛出异常。需要提醒的是,如果调用
    
    `FileOutputStream` 时指定的文件不存在,`Android` 会自动创建它。另外,在默认情况下,写入的时候会覆盖原文件内容,如果想把
    
    新写入的内容附加到原文件内容后,则可以指定其模式为`Context.MODE_APPEND`。
    ######3.**SQLite[数据库](http://lib.csdn.net/base/14)方式**
    ` SQLite`是`Android`所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库。
    

    /*

    • 什么是SQLiteDatabase?
    • 一个SQLiteDatabase的实例代表了一个SQLite的数据库,通过SQLiteDatabase实例的一些方法,我们可以执行SQL语句,
    • 对数据库进行增、删、查、改的操作。需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的。
      */

    /*

    • 什么是SQLiteOpenHelper ?
    • 这个类主要生成一个数据库,并对数据库的版本进行管理。
    • 当在程序当中调用这个类的方法getWritableDatabase()或者getReadableDatabase()方法的时候,如果当时没有数据,那么Android系统就会自动生成一个数据库。
    • SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的3个函数,
    • onCreate(SQLiteDatabase):在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。 
      
    • onUpgrade(SQLiteDatabase, int, int):当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。 
      
    • onOpen(SQLiteDatabase):这是当打开数据库时的回调函数,一般也不会用到。  
      

    */

    (1)建立一个内部类,主要生成一个数据库   
    

    private static class DatabaseHelper extends SQLiteOpenHelper {

        DatabaseHelper(Context context) {  
            super(context, DATABASE_NAME, null, DATABASE_VERSION);  
        }  
    
        //在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。   
        @Override  
        public void onCreate(SQLiteDatabase db) {  
    
            String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE  
                    + " text not null, " + BODY + " text not null " + ");";  
            Log.i("haiyang:createDB=", sql);  
            db.execSQL(sql);  
        }  
    
        @Override  
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
        }  
    }  ```
    

    (2)重新建立数据表

     private void CreateTable() {  
            //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,  
            //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。  
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
            String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE  
                    + " text not null, " + BODY + " text not null " + ");";  
            Log.i("haiyang:createDB=", sql);  
      
            try {  
                db.execSQL("DROP TABLE IF EXISTS diary");  
                db.execSQL(sql);  
                setTitle("数据表成功重建");  
            } catch (SQLException e) {  
                setTitle("数据表重建错误");  
            }  
        }  ```
    (3)删除数据表 
    

    private void dropTable() {
    //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,
    //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String sql = "drop table " + TABLE_NAME;
    try {
    db.execSQL(sql);
    setTitle("数据表成功删除:" + sql);
    } catch (SQLException e) {
    setTitle("数据表删除错误");
    }
    } ```
    (4)插入两条数据

     private void insertItem() {  
            //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,  
            //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。  
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
            String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY  
                    + ") values('haiyang', 'android的发展真是迅速啊');";  
            String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY  
                    + ") values('icesky', 'android的发展真是迅速啊');";  
            try {  
                // Log.i()会将参数内容打印到日志当中,并且打印级别是Info级别  
                // Android支持5种打印级别,分别是Verbose、Debug、Info、Warning、Error,当然我们在程序当中一般用到的是Info级别  
                Log.i("haiyang:sql1=", sql1);  
                Log.i("haiyang:sql2=", sql2);  
                db.execSQL(sql1);  
                db.execSQL(sql2);  
                setTitle("插入两条数据成功");  
            } catch (SQLException e) {  
                setTitle("插入两条数据失败");  
            }  
        }  ```
    (5)删除其中的一条数据 
    

    private void deleteItem() {
    try {
    //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,
    //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    //第一个参数是数据库表名,在这里是TABLE_NAME,也就是diary。
    //第二个参数,相当于SQL语句当中的where部分,也就是描述了删除的条件。
    //如果在第二个参数当中有“?”符号,那么第三个参数中的字符串会依次替换在第二个参数当中出现的“?”符号。
    db.delete(TABLE_NAME, " title = 'haiyang'", null);
    setTitle("删除title为haiyang的一条记录");
    } catch (SQLException e) {

        }  
    
    }  ```
    

    (6)查询数据

        /* 
         * Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null)语句将查询到的数据放到一个Cursor 当中。 
            这个Cursor里边封装了这个数据表TABLE_NAME当中的所有条列。  
            query()方法相当的有用,在这里我们简单地讲一下。 
                第一个参数是数据库里边表的名字,比如在我们这个例子,表的名字就是TABLE_NAME,也就是"diary"。 
                第二个字段是我们想要返回数据包含的列的信息。在这个例子当中我们想要得到的列有title、body。我们把这两个列的名字放到字符串数组里边来。 
                第三个参数为selection,相当于SQL语句的where部分,如果想返回所有的数据,那么就直接置为null。 
                第四个参数为selectionArgs。在selection部分,你有可能用到“?”,那么在selectionArgs定义的字符串会代替selection中的“?”。 
                第五个参数为groupBy。定义查询出来的数据是否分组,如果为null则说明不用分组。 
                第六个参数为having ,相当于SQL语句当中的having部分。 
                第七个参数为orderBy,来描述我们期望的返回值是否需要排序,如果设置为null则说明不需要排序。 
         */  
          
        private void showItems() {  
      
            SQLiteDatabase db = mOpenHelper.getReadableDatabase();  
            String col[] = { TITLE, BODY };  
            //查询数据  
            Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null);  
            Integer num = cur.getCount();  
            setTitle(Integer.toString(num) + " 条记录");  
        }  ```
    ######4.内容提供器(Content provider)方式
      在`Android`的设计“哲学”里是鼓励开发者使用内部类的,这样不但使用方便,而且执行效率也高。
    
       (1).什么是`ContentProvider `
    
       数据在`Android`当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。难道两个程序之间就没有办法对于数据进行交换?解决这个问题主要靠`ContentProvider`。
     一个`Content Provider`类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此`Content Provider`的各种数据类型。也就是说,一个程序可以通过实现一个`Content Provider`的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,当然,中间也会涉及一些权限的问题。
          下边列举一些较常见的接口,这些接口如下所示。
        `  query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder)`:通过Uri进行查询,返回一个`Cursor`。 
         ` insert(Uri url, ContentValues values)`:将一组数据插入到Uri 指定的地方。 
         ` update(Uri uri, ContentValues values, String where, String[] selectionArgs)`:更新Uri指定位置的数据。 
          `delete(Uri url, String where, String[] selectionArgs)`:删除指定Uri并且符合一定条件的数据。
    
       (2).什么是`ContentResolver`
          外界的程序通过`ContentResolver`接口可以访问`ContentProvider`提供的数据,在`Activity`当中通过`getContentResolver()`可以得到当前应用的`ContentResolver`实例。
          `ContentResolver`提供的接口和`ContentProvider`中需要实现的接口对应,主要有以下几个。 
          `query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder`):通过Uri进行查询,返回一个`Cursor`。
          `insert(Uri url, ContentValues values)`:将一组数据插入到Uri 指定的地方。
          `update(Uri uri, ContentValues values, String where, String[] selectionArgs)`:更新Uri指定位置的数据。
          `delete(Uri url, String where, String[] selectionArgs)`:删除指定Uri并且符合一定条件的数据。
    
       (3).`ContentProvider`和`ContentResolver`中用到的Uri 
          在`ContentProvider`和`ContentResolver`当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
         我们看下面的例子。 
              `content://contacts/people/ ` 这个Uri指定的就是全部的联系人数据。
             ` content://contacts/people/1 `这个Uri指定的是ID为1的联系人的数据。 
    
       在上边两个类中用到的Uri一般由3部分组成。
             第一部分是:`"content://" `。
             第二部分是要获得数据的一个字符串片段。 
            最后就是ID(如果没有指定ID,那么表示返回全部)。
    
       由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在`Android`当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码: 
      `   Contacts.People.CONTENT_URI `(联系人的URI)。
    
      (4)实现的功能
    
       在这个例子里边,首先在系统的联系人应用当中插入一些联系人信息,然后把这些联系人的名字和电话再显示出来
    
     (5)实现方法
    
      protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            //getContentResolver()方法得到应用的ContentResolver实例。  
            // query(Phones.CONTENT_URI, null, null, null, null)。它是ContentResolver里的方法,负责查询所有联系人,并返回一个Cursor。这个方法参数比较多,每个参数的具体含义如下。  
            //·  第一个参数为Uri,在这个例子里边这个Uri是联系人的Uri。  
            //·  第二个参数是一个字符串的数组,数组里边的每一个字符串都是数据表中某一列的名字,它指定返回数据表中那些列的值。  
            //·  第三个参数相当于SQL语句的where部分,描述哪些值是我们需要的。  
            //·  第四个参数是一个字符串数组,它里边的值依次代替在第三个参数中出现的“?”符号。  
            //·  第五个参数指定了排序的方式。  
            Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);  
            startManagingCursor(c); //让系统来管理生成的Cursor。  
            ListAdapter adapter = new SimpleCursorAdapter(  
                    this,  
                    android.R.layout.simple_list_item_2,   
                    c,   
                    new String[] { Phones.NAME, Phones.NUMBER },   
                    new int[] { android.R.id.text1, android.R.id.text2 });  
            setListAdapter(adapter); //将ListView和SimpleCursorAdapter进行绑定。  
        }  ```
    ######5. 网络存储方式
    简单讲就是把一段数据通过POST发送的方式保存到服务器,要用到的时候在去服务器中请求拿到数据。
    

    查看原文请点击

    相关文章

      网友评论

          本文标题:Android学习:四大组件、六大布局、五大存储

          本文链接:https://www.haomeiwen.com/subject/viyzkxtx.html