前言
临近考试,根据老师发的考试说明和例题整理出如下的复习资料
安卓的平台架构
由下至上分别是:
- linux内核
- 库
- Android运行时
- 应用程序框架
- 应用程序
下面简述每层的作用:
linux内核
Android 平台的基础是 Linux 内核。例如,Android Runtime (ART) 依靠 Linux 内核来执行底层功能,例如线程和低层内存管理。
使用 Linux 内核可让 Android 利用主要安全功能,并且允许设备制造商为著名的内核开发硬件驱动程序。
Android运行时
Android 包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。
程序库
Android包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过Android应用程序框架为开发者提供服务。
应用程序框架
Android系统提供给应用开发者的本身就是一个框架,所有的应用开发都必须遵守这个框架的原则。
应用程序
所有的应用程序都是使用JAVA语言编写的,每一个应用程序由一个或者多个活动组成,活动必须以Activity类为超类,活动类似于操作系统上的进程,但是活动比操作系统的进程要更为灵活,与进程类似的是,活动在多种状态之间进行切换。
安卓的四大组件
- 活动----Activity
他是一种可以包含用户界面的组件,主要用于和用户进行交互
- 服务----Service
Service是在一段不定的时间运行在后台,不和用户交互应用组件。
- 广播----Broadcast Receiver
- 内容提供者----Content Provider
用于在不同的应用程序之间实现数据共享的功能。提供了一套完整的机制,允许一个程序访问俩一个程序中的数据,同时还能保证被访问数据的安全性
安卓清单文件的作用
- 它为应用指定Java包。此包名称唯一标识应用
- 它描述了应用的不同组件。它指定了实现这些组件并公布它们的功能。这些声明帮助Android系统识别和组件及它们可以启动的条件。
- 它指定应用访问API的受保护部分和其他应用交换所需的权限。
- 它指定应用可在其上运行的API的最低级别
- 它指定链接应用所必须依据的库
常用的布局和控件以及其使用方法
四种最基本的布局
总共有四种最基本的布局,分别是:
线性布局(LinearLayout)
相对布局(RelativeLayout)
帧布局(FrameLayout)
百分比布局
因为考试说明里面只有两种,所以接下来只介绍两种
LinearLayout(线性布局)
- 指定各个节点的排列方向
android:orientation="horizontal"
- 设置右对齐
android:layout_gravity="right"
- 当竖直布局时,只能左右对齐和水平居中,顶部底部对齐竖直居中无效
- 当水平布局时,只能顶部底部对齐和竖直居中
- 使用match_parent时注意不要把其他组件顶出去
- 线性布局非常重要的一个属性:权重
android:layout_weight="1"
权重设置的是按比例分配剩余的空间
RelativeLayout
- 组件默认左对齐、顶部对齐
- 设置组件在指定组件的右边
android:layout_toRightOf="@id/tv1"
- 设置在指定组件的下边
android:layout_below="@id/tv1"
- 设置右对齐父元素
android:layout_alignParentRight="true"
- 设置与指定组件右对齐
android:layout_alignRight="@id/tv1"
常用的控件
TextView、EditText、Button、ImageButton、ToggleButton、RadioButton、RadioGroup、Checkbox、Spinner、ListView、ImageView、WebView、ScrollView
重要的属性有:
android:id:指明控件id
android:layout_width::指明控件的宽度
android:layout_height:指明控件的高度
相关控件及其使用方法可以参考
Android笔记---常用控件以及用法
创建一个活动(Activity)的步骤
- 创建活动,编写自己的Acivity类,继承Activity类
- 将UI与活动关联。调用setContentView方法
- 在清单文件中注册活动
活动(Activity)的生命周期
活动的状态
- 运行状态
当一个活动处于返回栈的栈顶,也就是在屏幕的前台显示时,称为运行状态
- 暂停状态
如果一个Activity失去焦点(不在栈顶),但是依然可见(一个新的非全屏的Activity,例如一个对话框形式的活动,或者一个透明的Activity 被放置在栈顶),叫做暂停状态(Paused)
- 停止状态
如果一个Activity被另外的Activity完全覆盖掉(不在栈顶且不可见),叫做停止状态(Stopped)
- 销毁状态(了解)
当一个活动从返回栈移除之后就变成的销毁状态
活动的7个生命周期的方法
活动的生命周期的图
Intent的使用
- 显式Intent
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
上述代码的作用是打开活动SecondActivity
- 隐式Intent
相比隐式Intent,隐式Intent并不明确指出想要启动哪一个活动,而是指定了一系列抽象的action和category等信息,然后交由系统去分析这个Intent,并找到合适的活动去启动
使用隐式Intent
首先打开AndroidManifest.xml,添加代码:
指定好当前活动可以相应的action和category
回到MainActivity.java中
Intent intent=new Intent("com.lyy.intent.ACTION_START");
startActivity(intent);
将Intent传入我们自定义的action字符串即可
-
Intent的系统action
利用Intent传值的两种方式
利用Intent直接传值
MainActivity.java —— 负责传递数据
Intent intent1=new Intent(MainActivity.this,Main2Activity.class);
intent1.putExtra("extra","hello");
startActivity(intent1);
Main2Activity —— 负责接收数据
Intent intent = getIntent();
String data = intent.getStringExtra("extra");
Log.d(TAG, "onCreate: " + data);
利用Bundle和Intent传值
MainActivity.java —— 负责传递数据
Intent intent2 = new Intent(MainActivity.this, Main2Activity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "lyy");
bundle.putInt("age", 18);
bundle.putString("address", "China");
intent2.putExtras(bundle);
startActivity(intent2);
Main2Activity —— 负责接收数据
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String nameString = bundle.getString("name");
int age = bundle.getInt("age");
String addressString = bundle.getString("address");
原生方法操作SQLite数据库
创建一个类用于继承SQLiteOpenHelper类,并重写两个方法onCreate()和onUpgrade()方法用于创建和升级数据库,代码示例如下:
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book(" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)";
public static final String CREATE_CATEGORY = "create table Category(" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
对数据库的数据进行增删改查(CRUD)操作
添加数据
先实例化之前创建的DatabaseHelper对象,并调用其getWritableDatabase()方法,设置该数据库可写
SQLiteDatabase db = databaseHelper.getWritableDatabase();
随后创建ContentValues对象,用来添加数据
ContentValues values = new ContentValues();
//开始组装第一组数据
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
最后执行db数据库的insert()方法
db.insert("Book", null, values);//插入第一条数据
更新数据
创建ContentValues对象,并组装要更新的数据
ContentValues values1 = new ContentValues();
values1.put("price", 10.99);
调用update()方法
db.update("Book", values1, "name = ?", new String[]{"The Da Vinci Code"});
第一个参数指定表名,第二个指定要更新的数据,第三个指定要更新的行,如果没有,则默认更新所有行。
删除数据
直接调用delete方法即可
db.delete("Book", "pages > ?", new String[]{"500"});
查询数据(最复杂)
通过调用db的query()方法,获得一个cursor对象
//查询Book表中的所有数据
Cursor cursor = db.query("Book", null, null, null, null, null, null);
在这里需要传入7个参数,作用分别如下:
然后在对cursor对象进行遍历,获得查询到的值
while (cursor.moveToFirst()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
}
最后,一定记得要回收cursor对象
cursor.close();
文件存储
写文件
public void save() {
String data = "Data to save";
try {
FileOutputStream fos = openFileOutput("data", Context.MODE_PRIVATE);
fos.write(data.getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
读文件
public void load() {
String fileName = "abc.txt";
try {
FileInputStream fis = openFileInput(fileName);
byte[] reader = new byte[fis.available()];
if (fis.read(reader) != -1) {
String data = new String(reader);
System.out.println(data);
}
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
共享首选项(SharedPreferences)
利用键值对的方式来存储数据,文件都是存放在/data/data/
内容提供器
主要用于在不同的应用程序之间实现数据的共享功能。目前是Android实现跨程序共享数据的标准方式。一般有两种:
1.使用现有的内容提供其来读取和操作相应程序中的数据
- 创建自己的内容提供器给我们的程序的数据提供外部访问接口
使用本机内容提供者
Android平台提供了一些本机内容提供者有:浏览器、联系人、通话记录、媒体存储、设置。
联系人内容提供者的URI各部分:
- content://contacts/people/20
- content:// 指定数据受内容提供者控制。它是标准前缀
- contacts:指原生Contacts内容提供者
- people指contacts列表可用记录
- 20指Contacts列表中的第二十条记录。它是指定记录的ID
- 使用getContentResolver()方法获取ContentResolver对象,通过该对象的以下方法访问内容提供者
public final Uri insert(Uri url, ContentValues values) public final int delete(Uri url, String where, String[] selectionArgs) public final int update(Uri uri, ContentValues values, String where,String[] selectionArgs) public final Cursor query(Uri uri, String[] projection,String selection, String[] selectionArgs, String sortOrder)
使用自定义内容提供器
创建内容提供器
新建一个类去继承ContentProvider,并重写其6个方法,代码如下:
public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
- onCreate()
初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,
返回 true 表示内容提供器初始化成功,返回 false 则表示失败。注意,只有当存在
ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。- query()
从内容提供器中查询数据。使用 uri 参数来确定查询哪张表,projection 参数用于确
定查询哪些列,selection 和 selectionArgs 参数用于约束查询哪些行,sortOrder 参数用于
对结果进行排序,查询的结果存放在 Cursor 对象中返回。- insert()
向内容提供器中添加一条数据。使用 uri 参数来确定要添加到的表,待添加的数据
保存在 values 参数中。添加完成后,返回一个用于表示这条新记录的 URI。- update()
更新内容提供器中已有的数据。使用 uri 参数来确定更新哪一张表中的数据,新数
据保存在 values 参数中,selection 和 selectionArgs 参数用于约束更新哪些行,受影响的
行数将作为返回值返回。- delete()
从内容提供器中删除数据。使用 uri 参数来确定删除哪一张表中的数据,selection
和 selectionArgs 参数用于约束删除哪些行,被删除的行数将作为返回值返回。- getType()
根据传入的内容 URI 来返回相应的 MIME 类型。
指定内容提供者的URI
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
}
..........
}
实现查询方法
public class MyProvider extends ContentProvider {
..........
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
// 查询table1表中的所有数据
break;
case TABLE1_ITEM:
// 查询table1表中的单条数据
break;
case TABLE2_DIR:
// 查询table2表中的所有数据
break;
case TABLE2_ITEM:
// 查询table2表中的单条数据
break;
default:
break;
}
return null;
}
....
}
处理对MINE类型数据的请求
public class MyProvider extends ContentProvider {
...........
@Nullable
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider. table1 ";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider. table1 ";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider. table2 ";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider. table2 ";
default:
break;
}
return null;
}
................
}
注册自定义内容提供器
广播接收者(BroadcastReceiver)
广播分为两种:普通广播和有序广播
- 普通广播
完全异步执行的广播,在广播发出后,所有的广播接收器几乎在同一时刻都会受到这条广播,没有先后顺序之分,无法被截断,效率较高
- 有序广播
同步执行的广播,在广播发出后,同一时刻只会有一个广播接收器可以收到消息,且只有当该广播接收器中的逻辑执行完毕后,这条广播才会继续传递下去,有先后顺序之分,可以被截断。
注册广播接收器的方式有两种:
- 动态注册
在代码中注册,新建一个类,并让其继承BroadcastReceiver类,同时重新其父类中的onReceive()方法即可,这样当有广播到来的时候,onReceive()方法便会得到执行,同时需要主活动中进行广播的注册,因为全程是在代码中运行,灵活性强,便被成为动态注册,示例如下:
private class NetWorkChangeReceiverextends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// do something......
}
}
在onCreate()方法中执行:
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
netWorkChangeReceiver = new NetWorkChangeReceiver();
registerReceiver(netWorkChangeReceiver, intentFilter);
在onDestory()方法中执行:
unregisterReceiver(netWorkChangeReceiver);//注销广播
- 静态注册
和动态注册相同的是,静态注册也需要创建一个类去继承BroadcastReceiver类并实现其中的onReceive()方法,但是注册的过程被放到了AndroidManifest.xml文件中了,示例如下:
MyBroadcastReceiver.java
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
}
}
AndroidManifest.xml
动态注册和静态注册的区别:
动态注册不是常驻型广播,也就是说广播跟随程序的生命周期。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行
服务
创建一个服务
新建一个类MyService集成Service,并重写其onBind()方法即可
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
当然,最为四大组件之一的Service,服务在创建的时候需要在AndroidManifest.xml文件中进行注册才能生效
启动一个服务
调用startService()方法即可启动服务,传入一个intent参数即可
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
关闭一个服务
调用stopService()方法即可
Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);
绑定一个服务
- 定义一个内部类,该内部类实现接口”ServiceConnection”,同时实现接口里的两个方法:”onServiceConnection”(在这个方法方法体里得到Binder对象,由该对象就能得到服务实例)、”onServiceDisConnection”(当服务被意外断开,系统将会回调这个方法)。
- 分别创建服务类变量、内部类变量(内部类变量需要实例化),在”onServiceConnection”方法里面用得到的服务实例将服务类变量实例化
- 在需要绑定服务的地方,调用bindService()方法,将内部类变量传入。
public class MainActivity extends Activity implements View.OnClickListener {
//第二步:创建内部类变量以及服务类的变量
//同时实例化内部类变量
private MService mService = null;
private MServiceConnection mServiceConnection =
new MServiceConnection();
//窗体的初始化方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.bt_bind_the_service)
.setOnClickListener(this);
}
//点击事件响应函数
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bt_bind_the_service:
Intent intent =
new Intent(MainActivity.this, MService.class);
//第三步:绑定服务那个时候将接口的实例传入
bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE);
break;
}
}
//第一步:定义一个内部类去实现接口”ServiceConnection”
class MServiceConnection implements ServiceConnection{
//这个方法里面那个service
//就是服务类里面的onBind()方法所返回的IBinder变量了
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//还是第二步:将服务类的变量实例化:
//将IBinder向下转型
MService.MyBinder myBinder = (MService.MyBinder)service;
//调用公有方法就能获得服务类的实例
mService = myBinder.getService();
Log.i("test","连接服务");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "意外断开");
}
}
}
解绑一个服务
相对于绑定一个服务和活动来说,解除服务和活动之间的绑定就显得容易很多,只需要执行unbindService()方法,并传入一个ServiceConnection()对象即可。
unbindService(connection);
服务的生命周期
详见 Android:Service生命周期最全面解析