第一行代码 Android (郭霖 著)

时间:2024-05-23 16:04:02

https://github.com/guolindev/booksource

第1章 开始启程----你的第一行Android代码 (已看)

第2章 先从看得到的入手----探究活动 (已看)

第3章 软件也要拼脸蛋----UI开发的点点滴滴 (已看)

第4章 手机平板要兼顾----探究碎片

第5章 全局大喇叭----详解广播机制

第6章 数据存储全方案----详解持久化技术

第7章 跨程序共享数据----探究内容提供器

第8章 丰富你的程序----运用手机多媒体

第9章 看看精彩的世界----使用网络技术

第10章 后台默默的劳动者----探究服务 (已看)

第11章 Android特色开发----基于位置的服务

第12章 最佳的UI体验----Material Design实战

第13章 继续进阶----你还应该掌握的高级技巧

第14章 进入实战----开发酷欧天气

第15章 最后一步----将应用发布到360应用商店

第1章 开始启程----你的第一行Android代码

谷歌终于在2008年推出了Android系统的第一个版本

  1.1 了解全貌----Andorid王国简介

    1.1.1 Android系统架构

第一行代码 Android (郭霖 著)

  1. Linux内核层
  2. 系统运行库层
  3. 应用框架层
  4. 应用层

    1.1.2 Andorid已发布的版本

第一行代码 Android (郭霖 著)

第一行代码 Android (郭霖 著)

    1.1.3 Andorid应用开发特色

  1. 四大组件  活动(Activity), 服务(Service), 广播接收器(Broadcast Receiver), 内容提供器(ContentProvidedr)
  2. 丰富的系统控件
  3. SQLite数据库
  4. 强大的多媒体
  5. 地理位置定位

  1.2 手把手带你搭建开发环境

    1.2.1 准备所需要的工具

  • JDK
  • Android SDK
  • Android Studio

    1.2.2 搭建开发环境

https://developer.android.com/studio/index.html

  1.3 创建你的第一个Android项目

    1.3.1 创建Hello World项目

Minimum API Level: API 15:Android 4.0.3(IceCreamSandwich)

    1.3.2 启动模拟器

Nexus 5X API 24 Android 7.0 x86

    1.3.3 运行Hello World

    1.3.4 分析你的第一个Android程序

任何一个新建的项目都会默认使用Andorid模式的项目结构,但这并不是项目真实的目录结构,而是被Android Studio转换过的.

第一行代码 Android (郭霖 著)

项目真实的目录结构,Project

第一行代码 Android (郭霖 著)

1. .gradle和.idea

  这两个目录下放置的都是Android Studio自动生成的一些文件, 我们无须关心, 也不要去手动编辑

2. app

  项目中的代码, 资源等内容几乎都是放置在这个目录下的, 我们后面的开发工作也基本都是在这个目录下进行的,待会儿还会对这个目录单独展开进行详解

3. build

  这个目录你也不需要过多关心, 它主要包含了一些在编译时自动生成的文件

4. gradle

  这个目录下包含了gradle wrapper的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是会自动根据本地的缓存情况决定是否需要联网下载gradle.

5. .gitignore

  这个文件是用来将指定的目录或文件排除在版本控制之外的

6. build.gradle

  这是项目全局的gradle构建脚本,通常这个文件的内容是不需要修改的.

7. gradle.properties

  这个文件是全局的gradle配置文件,在这里配置的属性将会影响到项目中所有的gradle编译脚本

8. gradlew和gradlew.bat

  这两个文件是用来在命令行界面中执行gradle命令的,其中gradlew是在Linux或Mac系统中使用的,gradlew.bat是在Windows系统中使用的

9. HelloWorld.iml

  iml文件是所有IntelliJ IDEA项目都会自动生成的一个文件(Android Studio是基于IntelliJ IDEA开发的),用于标识这是一个IntelliJ IDEA项目,我们不需要修改这个文件中的任何内容

10. local.properties

  这个文件用于指定本机中的Android SDK路径, 通常内容都是自动生成的,我们并不需要修改.除非你本机中的Android SDK位置发生了变化, 那么就将这个文件中的路径改成新的位置即可

11. settings.gradle

  这个文件用于指定项目中所有引入的模块. 由于Hello World项目中就只有一个app模块,因此该文件中也就只引入了app这一个模块.通常情况下模块的引入都是自动完成的,需要我们手动去修改这个文件的场景可能比较少

app目录下的内容

1. build

  这个目录和外层的build目录类似, 主要也是包含了一些在编译时自动生成的文件,不过它里面的内容会更多更杂,我们不需要过多关心

2. libs

  如果你的项目中使用到了第三方jar包,就需要把这些jar包都放在libs目录下,放在这个目录下的jar包都会被自动添加到构建路径里去

3. androidTest

  此处是用来编写Android Test测试用例的,可以对项目进行一些自动化测试

4. java

  毫无疑问,java目录是放置我们所有的Java代码的地方, 展开该目录, 你将看到我们刚才创建的HelloWorldActivity文件就在里面

5. res

  这个目录下的内容就有点多了.简单点说, 就是你在项目中使用到的所有图片, 布局, 字符串等资源都要存放在这个目录下, 当然这个目录下还有很多子目录,图片放在drawable目录下,布局放在layout目录下, 字符串放在values目录下,所以你不用担心会把整个res目录弄得乱糟糟的

6. AndroidManifest.xml

  这是你整个Android项目的配置文件,你在程序中定义的所有四大组件都需要的这个文件里注册, 另外还可以在这个文件中给应用程序添加权限声明.由于这个文件以后会经常用到, 我们用到的时候再做详细说明

7. test

  此处是用来编写Unit Test测试用例的,是对项目进行自动化测试的另一种方式

8. .gitignore

  这个文件用于将app模块内的指定目录或文件排除在版本控制之外, 作用和外层的.gitignore文件类似

9. app.iml

  IntelliJ IDEA项目自动生成的文件,我们不需要关心或修改这个文件中的内容

10. build.gradle

  这是app模块的gradle构建脚本,这个文件中会指定很多项目构建相关的配置

11. proguard-rules.rpo

  这个文件用于指定项目代码的混淆规则,当代码开发完成后打成安装包文件,如果不希望代码被别人破解, 通常会将代码进行混淆,从而让破解者难以阅读

<activity android:name=".HelloWorldActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

AndoridManifest.xml

public class HelloWorldActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.hello_world_layout);
    }
}

HelloWorldActivity

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HelloWorldActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text = "Hello World!"/>
</android.support.constraint.ConstraintLayout>

hello_world_layout.xml

    1.3.5 详解项目中的资源

所有以drawable开头的文件夹都是用来放图片的,所有以mipmap开头的文件都是用来放应用图标的,所有以values开头的文件夹都是用来放字符串,样式,颜色等配置的,layout文件夹是用来放布局文件的.

之所以有这么多mipmap开头的文件夹,主要是为了让程序能够更好地兼容各种设备. drawable文件夹也是相同的道理

<resources>
    <string name="app_name">HelloWorld</string>
</resources>

res/values/strings.xml

  • 在代码中通过R.string.hello_world可以获得该字符串的引用
  • 在xml中通过@string/hello_world可以获得该字符串的引用

    1.3.6 详解build.gradle文件

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion
    defaultConfig {
        applicationId "com.voidgame.helloworld"
        minSdkVersion
        targetSdkVersion
        versionCode
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

app/build.gradle

  1.4 前行必备----掌握日志工具的使用

    1.4.1 使用Android的日志工具Log

  • Log.v().  用于打印那些最为琐碎的,意义最小的日志信息.对应级别verbose,是Android日志里面级别最低的一种
  • Log.d().  用于打印一些调试信息,这些信息对你调试程序和分析问题应该是有帮助的.对应级别debug,比verbose高一级
  • Log.i().  用于打印一些比较重要的数据,这些数据应该是你非常想看到的,可以帮你分析用户行为数据.对应级别info,比debug高一级
  • Log.w().  用于打印一些警告信息, 提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方. 对应级别warn,比info高一级
  • Log.e().  用于打印程序中的错误信息,比如程序进入到了catch语句当中.当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复. 对应级别error, 比warn高一级

    1.4.2 为什么使用Log而不使用System.out

  1.5 小结与点评

第2章 先从看得到的入手----探究活动

  2.1 活动是什么

活动(Activity)是最容易吸引用户的地方,它是一种可以包含用户界面的组件,主要用于和用户进行交互.一个应用程序可以包含零个或多个活动,但不包含任何活动的应用程序很少见,谁也不想让自己的应用永远无法被用户看到吧?

  2.2. 活动的基本用法

    2.2.1 手动创建活动

public class FirstActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}

    2.2.2 创建和加载布局

第一行代码 Android (郭霖 著)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button_1" />
</LinearLayout>

public class FirstActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.first_layout);
    }
}

    2.2.3 在AndroidManifest文件中注册

        <activity android:name=".FirstActivity" android:label="This is FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    2.2.4 在活动中使用Toast

import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class FirstActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.first_layout);

        Button button1 = (Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

    2.2.5 在活动中使用Menu

第一行代码 Android (郭霖 著)

import android.view.Menu;
import android.view.MenuItem;

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.add_item:
                Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show();
                break;
            case R.id.remove_item:
                Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
        return true;
    }

    2.2.6 销毁一个活动

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

  2.3 使用Intent在活动之间穿梭

    2.3.1 使用显式Intent

Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据.Intent一般可用于启动活动,启动服务以及发送广播等场景

Intent大致可以分为两种: 显式Intent和隐式Intent

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });

使用这种方式来启动活动,Intent的"意图"非常明显,因此我们称之为显示Intent

    2.3.2 使用隐式Intent

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="com.example.activitytest.MY_CATEGORY" />
            </intent-filter>
        </activity>

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.activitytest.ACTION_START");
                intent.addCategory("com.example.activitytest.MY_CATEGORY");
                startActivity(intent);
            }
        });

    2.3.3 更多隐式Intent的用法

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse("http://www.baidu.com"));
                startActivity(intent);
            }
        });
        <activity android:name=".ThirdActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="http" />
            </intent-filter>
        </activity>

    2.3.4 向下一个活动传递数据

        Button button1 = (Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String data = "Hello SecondActivity";
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                intent.putExtra("extra_data", data);
                startActivity(intent);
            }
        });

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second_layout);

        Intent intent = getIntent();
        String data = intent.getStringExtra("extra_data");
        Log.d("SecondActivity", data);
    }

    2.3.5 返回数据给上一个活动

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.first_layout);

        Button button1 = (Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivityForResult(intent, );
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            :
                if (resultCode == RESULT_OK) {
                    String returnedData = data.getStringExtra("data_return");
                    Log.d("FirstActivity", returnedData);
                }
                break;
            default:
                    break;
        }
    }

        Button button2 = (Button) findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.putExtra("data_return", "Hello FirstActivity");
                setResult(RESULT_OK, intent);
                finish();
            }
        });

  2.4 活动的生命周期

    2.4.1 返回栈

第一行代码 Android (郭霖 著)

    2.4.2 活动状态

每个活动在其生命周期中最多可能有4种状态

  1. 运行状态

当一个活动位于返回栈的栈顶时,这时活动就处于运行状态.系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验

  2. 暂停状态

当一个活动不再处于栈顶状态,但仍然可见时,这时活动就进入了暂停状态.你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域.处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会考虑回收这种活动

  3. 停止状态

当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态.系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收

  4. 销毁状态

当一个活动从返回栈中移除后就变成了销毁状态.系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足

    2.4.3 活动的生存期

Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节

  • onCreate() 这个方法你已经看到过很多次了,每个活动中我们都重写了这个方法,它会在活动第一次被创建的时候调用.你应该在这个方法中完成活动的初始化操作,比如加载布局,绑定事件等
  • onStart() 这个方法在活动由不可见变为可见的时候调用
  • onResume() 这个方法在活动准备好和用户进行交互的时候调用.此时的活动一定位于返回栈的栈顶,并且处于运行状态
  • onPause() 这个方法在系统准备去启动或者恢复另一个活动的时候调用.我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用
  • onStop() 这个方法在活动完全不可见的时候调用.它和onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行
  • onDestroy() 这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态
  • onRestart() 这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了

以上7个方法中除了onRestart()方法,其他都是两两相对的,从而又可以将活动分为3种生存期

  • 完整生存期 活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期.一般情况下,一个活动会在onCreate()方法中完成各种初始化操作,而在onDestroy()方法中完成释放内存的操作
  • 可见生存期 活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期.在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互.我们可以通过这两个方法,合理地管理那些对用户可见的资源.
  • 前台生存期 活动在onResume()方法和onPause()方法之间所经历的就是前台生存期.在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的活动

第一行代码 Android (郭霖 著)

    2.4.4 体验活动的生命周期

public class MainActivity extends AppCompatActivity {

    public static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button startNormalActivity = (Button)findViewById(R.id.start_normal_activity);
        final Button startDialogActivity = (Button)findViewById(R.id.start_dialog_activity);

        startNormalActivity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, NormalActivity.class);
                startActivity(intent);
            }
        });

        startDialogActivity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, DialogActivity.class
                );
                startActivity(intent);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG, "onRestart");
    }
}

    2.4.5 活动被回收了怎么办

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    String tempData = "Something you just typed";
    outState.putString("data_key", tempData);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstancdeState);
    Log.d(TAG, "onCreate");
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null) {
        String tempData = savedInstanceState.getString("data_key");
        Log.d(TAG, tempData);
    }
    ...
}

  2.5 活动的启动模式

standard, singleTop, singleTask, singleInstance

    2.5.1 standard

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        Log.d("FirstActivity", this.toString());

        Button button1 = (Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent= new Intent(MainActivity.this, MainActivity.class);
                startActivity(intent);
            }
        });
    }

第一行代码 Android (郭霖 著)

第一行代码 Android (郭霖 著)

    2.5.2 singleTop

android:launchMode="singleTop"

第一行代码 Android (郭霖 著)

    2.5.3 singleTask

<activity android:name=".MainActivity" android:launchMode="singleTask">

第一行代码 Android (郭霖 著)

    2.5.4 singleInstance

<activity android:name=".MainActivity" android:launchMode="singleInstance">

第一行代码 Android (郭霖 著)

  2.6 活动的最佳实践

    2.6.1 知晓当前是在哪一个活动

Log.d("BaseActivity", getClass().getSimpleName());

    2.6.2 随时随地退出程序

public class ActivityCollector {
    public static List<Activity> activities = new ArrayList<>();

    public static void addActivity(Activity activity) {
        activities.add(activity);
    }

    public static void removeActivity(Activity activity) {
        activities.remove(activity);
    }

    public static void finishAll() {
        for (Activity activity: activities) {
            if (!activity.isFinishing()) {
                activity.finish();
            }
        }
    }
}

android.os.Process.killProcess(android.os.Process.myPid());

    2.6.3 启动活动的最佳写法

  2.7 小结与点评

第3章 软件也要拼脸蛋----UI开发的点点滴滴

  3.1 如何编写程序界面

  3.2 常用控件的使用方法

    3.2.1 TextView

    3.2.2 Button

    3.2.3 EditText

    3.2.4 ImageView

    3.2.5 ProgressBar

    3.2.6 AlertDialog

    3.2.7 ProgressDialog

  3.3 详解4中基本布局

第一行代码 Android (郭霖 著)

    3.3.1 线性布局

这个布局会将它所包含的控件在线性方向上依次排列

    3.3.2 相对布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Button 3" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/button3"
        android:layout_toLeftOf="@+id/button3"
        android:text="Button 1" />

</RelativeLayout>

第一行代码 Android (郭霖 著)

    3.3.3 帧布局

FrameLayout又称作帧布局,它相比于前面两种布局就简单太多了,因此它的应用场景也少了很多.这种布局没有方便的定位方式,所有的控件都会默认摆放在布局的左上角

    3.3.4 百分比布局

  3.4 系统控件不够用?创建自定义控件

第一行代码 Android (郭霖 著)

    3.4.1 引入布局

    3.4.2 创建自定义控件

  3.5 最常用和最难用的控件----List View

    3.5.1 ListView的简单用法

<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/list_view"
        />
</LinearLayout>

    private String[] data = { "Apple", "Banana", "Orange" };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, R.layout.support_simple_spinner_dropdown_item, data);
        ListView listView = (ListView)findViewById(R.id.list_view);
        listView.setAdapter(adapter);

    }

    3.5.2 定制ListView的界面

    3.5.3 提升ListView的运行效率

    3.5.4 ListView的点击事件

  3.6 更强大的滚动控件----Recycler View

    3.6.1 RecyclerView的基本用法

    3.6.2 实现横向滚动和瀑布流布局

    3.6.3 RecyclerView的点击事件

  3.7 编写界面的最佳实践

    3.7.1 制作Nine-Patch图片

    3.7.2 编写精美的聊天界面

  3.8 小结与点评

第4章 手机平板要兼顾----探究碎片

  4.1 碎片是什么

  4.2 碎片的使用方式

    4.2.1 碎片的简单用法

    4.2.2 动态添加碎片

    4.2.3 在碎片中模拟返回栈

    4.2.4 碎片和活动之间进行通信

  4.3 碎片的生命周期

    4.3.1 碎片的状态和回调

    4.3.2 体验碎片的生命周期

  4.4 动态加载布局的技巧

    4.4.1 使用限定符

    4.4.2 使用最小宽度限定符

  4.5 碎片的最佳实践----一个简易版的新闻应用

  4.6 小结与点评

第5章 全局大喇叭----详解广播机制

  5.1 广播机制简介

  5.2 接收系统广播

    5.2.1 动态注册监听网络变化

    5.2.2 静态注册实现开机启动

  5.3 发送自定义广播

    5.3.1 发送标准广播

    5.3.2 发送有序广播

  5.4 使用系统广播

  5.5 广播的最佳实践----实现强制下线功能

  5.6 Git时间----初识版本控制工具

    5.6.1 安装Git

    5.6.2 创建代码仓库

    5.6.3 提交本地代码

  5.7 小结与点评

第6章 数据存储全方案----详解持久化技术

  6.1 持久化技术简介

  6.2 文件存储

    6.2.1 将数据存储到文件中

    6.2.2 从文件中读取数据

  6.3 SharedPreferences存储

    6.3.1 将数据存储到SharedPreferences中

    6.3.2 从SharedPreferences中读取数据

    6.3.3 实现记住密码功能

  6.4 SQLite数据库存储

    6.4.1 创建数据库

    6.4.2 升级数据库

    6.4.3 添加数据

    6.4.4 更新数据

    6.4.5 删除数据

    6.4.6 删除数据

    6.4.7 查询数据

  6.5 使用LitePal操作数据库

    6.5.1 LitePal简介

    6.5.2 配置LitePal

    6.5.3 创建和升级数据库

    6.5.4 使用LitePal添加数据

    6.5.5 使用LitePal更新数据

    6.5.6 使用LitePal删除数据

    6.5.7 使用LitePal查询数据

  6.6 小结与点评

第7章 跨程序共享数据----探究内容提供器

  7.1 内容提供器简介

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性.目前,使用内容提供器是Android实现跨程序共享数据的标准方式

  7.2 运行时权限

    7.2.1 Android权限机制详解

第一行代码 Android (郭霖 著)

    7.2.2 在程序运行时申请权限

  7.3 访问其他程序中的数据

    7.3.1 ContentResolver的基本用法

    7.3.2 读取系统联系人

  7.4 创建自己的内容提供器

    7.4.1 创建内容提供器的步骤

    7.4.2 实现跨程序数据共享

  7.5 Git时间----版本控制工具进阶

    7.5.1 忽略文件

    7.5.2 查看修改内容

    7.5.3 撤销未提交的修改

    7.5.4 查看提交记录

  7.6 小结与点评

第8章 丰富你的程序----运用手机多媒体

  8.1 将程序运行到手机上

  8.2 使用通知

    8.2.1 通知的基本用法

    8.2.2 通知的进阶技巧

    8.2.3 通知的高级功能

  8.3 调用摄像头和相册

    8.3.1 调用摄像头拍照

    8.3.2 从相册中选择照片

  8.4 播放多媒体文件

    8.4.1 播放音频

    8.4.2 播放视频

  8.5 小结与点评

第9章 看看精彩的世界----使用网络技术

  9.1 WebView的用法

<uses-permission android:name="android.permission.INTERNET" />

    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></WebView>

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        WebView webView = (WebView)findViewById(R.id.web_view);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.loadUrl("http://www.baidu.com");
    }

  9.2 使用HTTP协议访问网络

    9.2.1 使用HttpURLConnection

public class Main2Activity extends AppCompatActivity implements View.OnClickListener {

    TextView responseText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        Button sendRequest = (Button)findViewById(R.id.send_request);
        responseText = (TextView)findViewById(R.id.response_text);
        sendRequest.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.send_request) {
            sendRequestWithHttpURLConnection();
        }
    }

    private void sendRequestWithHttpURLConnection() {
        Log.d("haha","jksdlfkjsdlkjfds");
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                BufferedReader reader = null;

                try {
                    URL url = new URL("https://www.baidu.com");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout();
                    connection.setReadTimeout();

                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));

                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    showResponse(response.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

    private void showResponse(final String response) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                responseText.setText(response);
                Log.d("xxx",response);
            }
        });
    }
}

    9.2.2 使用OkHttp

  9.3 解析XML格式数据

    9.3.1 Pull解析方式

    9.3.2 SAX解析方式

  9.4 解析JSON格式数据

    9.4.1 使用JSONObject

    9.4.2 使用GSON

  9.5 网络编程的最佳实践

  9.6 小结与点评

第10章 后台默默的劳动者----探究服务

  10.1 服务是什么

  10.2 Android多线程编程

    10.2.1 线程的基本用法

class MyThread extends Thread {
    @Override
    public void run() {
        // 处理具体的逻辑
    }
}

new MyThread().start();

class MyThread implements Runnable {
    @Override
    public void run() {
        // 处理具体逻辑
    }
}

MyThread myThread = new MyThread();
new Thread(myThread).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        // 处理具体的逻辑
    }
}).start();

    10.2.2 在子线程中更新UI

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/change_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Change Text"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World"
        android:textSize="20sp" />
</LinearLayout>

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    ;

    private TextView text;

    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = (TextView)findViewById(R.id.text);

        Button changeText = (Button)findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();
            default:
                break;
        }
    }
}

    10.2.3 解析异步消息处理机制

  1. Message

Message是线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据

  2. Handler

Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的.发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中

  3. MessageQueue

它主要用于存放所有通过Handler发送的消息.这部分消息会一直存在于消息队列中,等待被处理.每个线程中只会有一个MessageQueue对象

  4. Looper

Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中.每个线程中也只会有一个Looper对象

第一行代码 Android (郭霖 著)

    10.2.4 使用AsyncTask

  10.3 服务的基本用法

    10.3.1 定义一个服务

    10.3.2 启动和停止服务

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true" />

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button startService = (Button)findViewById(R.id.start_service);
        Button stopService = (Button)findViewById(R.id.stop_service);

        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                Intent startIntent = new Intent(this, MyService.class);
                startService(startIntent);
                break;
            case R.id.stop_service:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent);
                break;
            default:
                break;
        }
    }
}

    10.3.3 活动和服务进行通信

  10.4 服务的生命周期

  10.5 服务的更多技巧

    10.5.1 使用前台服务

    10.5.2 使用IntentService

  10.6 服务的最佳实践----完整版的下载示例

  10.7 小结与点评

第11章 Android特色开发----基于位置的服务

  11.1 基于位置的服务简介

  11.2 申请API Key

  11.3 使用百度定位

    11.3.1 准备LBK SDK

    11.3.2 确定自己位置的经纬度

    11.3.3 选择定位模式

    11.3.4 看得懂的位置信息

  11.4 使用百度地图

    11.4.1 让地图显示出来

    11.4.2 移动到我的位置

    11.4.3 让"我"显示在地图上

  11.5 Git时间----版本控制工具的高级用法

    11.5.1 分支的用法

    11.5.2 与远程版本库协作

  11.6 小结与点评

第12章 最佳的UI体验----Material Design实战

  12.1 什么是Material Desgin

  12.2 Toolbar

  12.3 滑动菜单

    12.3.1 DrawerLayout

    12.3.2 NavigationView

  12.4 悬浮按钮和可交互提示

    12.4.1 FloatingActionButton

    12.4.3 Snackbar

    12.4.4 CoordinatorLayout

  12.5 卡片式布局

    12.5.1 CardView

    12.5.2 AppBarLayout

  12.6 下拉刷新

  12.7 可折叠式标题栏

    12.7.1 CollapsingToolbarLayout

    12.7.2 充分利用系统状态栏空间

  12.8 小结与点评

第13章 继续进阶----你还应该掌握的高级技巧

  13.1 全局获取Context的技巧

  13.2 使用Intent传递对象

    13.2.1 Serializable方式

    13.2.2 Parcelable方式

  13.3 定制自己的日志工具

  13.4 调试Android程序

  13.5 创建定时人物

    13.5.1 Alarm机制

    13.5.2 Doze模式

  13.6 多窗口模式编程

    13.6.1 进入多窗口模式

    13.6.2 多窗口模式下的生命周期

    13.6.3 禁用多窗口模式

  13.7 Lambda表达式

  13.8 总结

第14章 进入实战----开发酷欧天气

  14.1 功能需求及技术可行性分析

  14.2 Git时间----将代码托管到GitHub上

  14.3 创建数据库和表

  14.4 遍历全国省市县数据

  14.5 显示天气信息

    14.5.1 定义GSON实体类

    14.5.2 编写天气界面

    14.5.3 将天气显示到界面上

    14.5.4 获取必应每日一图

  14.6 手动更新天气和切换城市

    14.6.1 手动更新天气

    14.6.2 切换城市

  14.7 后台自动更新天气

  14.8 修改图标和名称

  14.9 你还可以做的事情

第15章 最后一步----将应用发布到360应用商店

  15.1 生成正式签名的APK文件

    15.1.1 使用Android Studio生成

    15.1.2 使用Gradle生成

    15.1.3 生成多渠道APK文件

  15.2 申请360开发者账号

  15.3 发布应程序

  15.4 嵌入广告进行盈利

    15.4.1 注册腾讯广告联盟账号

    15.4.2 新建媒体和广告位

    15.4.3 接入广告SDK

    15.4.4 重新发布应用程序

  15.5 结束语