Android第一行代码阅读笔记

1、Android系统简介

1.1、Android系统架构

Linux内核层:Android 系统基于Linux内核,这一层为Android 设备的各种硬件提供了底层的驱动,如显示驱动、音频驱动、相机驱动、蓝牙驱动、WiFi驱动、电源管理。

系统运行库层:这一层通过一些C/C++库来为Android系统提供主要的特性支持,如SQLite提供数据库的支持,OpenGL|ES库提供3D绘图的支持,Webkit库提供浏览器内核的支持。以及一些核心库允许开发者构建Android应用。

应用框架层:这一层提供了构建应用程序可能用到的各种API。

应用层:所有安装在手机上的应用程序都是属于这一层的。

1.2、Android系统四大组件

活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供者(Content Provider)

2、活动

活动是一种包含用户界面的组件,用于和用户交互。

2.1、创建一个Activity

1、新建一个empty activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class FirstActivity extends AppCompatActivity {

/**
* 添加菜单监听事件
* @param item
* @return
*/
@SuppressLint("NonConstantResourceId")
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
//设置菜单多个选项按钮
switch (item.getItemId()){
case R.id.add_item:
//Toast方法用于按钮点击后设置短暂文字提醒
Toast.makeText(this,"you clicked add button",Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this,"you removed the button",Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}

/**
* 创建菜单按钮
* @param menu
* @return
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);//引入布局文件
Button button1=(Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
/*Toast提供了事件文本提醒*/
//Toast.makeText(FirstActivity.this,"you clicked button1",Toast.LENGTH_SHORT).show();
//finish();//活动销毁

/*通过按钮事件重新开启一个activity,Intent用于启动活动,服务,广播。以下显式构造一个Intent,传入FirstActivity.this作为上下文,SecondActivity.class
作为目标活动。*/
//Intent intent=new Intent(FirstActivity.this,SecondActivity.class);

/*隐式Intent */
//Intent intent1=new Intent("com.example.activitytest.My_Category");
//intent1.addCategory("com.example.activity.My_Category1");

/*跳转外部url*/
//Intent intent2=new Intent(Intent.ACTION_VIEW);
//intent2.setData(Uri.parse("https://www.bilibili.com"));


startActivity(intent2);
}
});
}

}

2、布局文件

Android程序设计讲究逻辑和视图分离,最好一个活动对应一个布局。下面手动创建一个Layout resource file,根元素默认为LinearLayout。

1
2
3
4
5
6
7
8
9
10
11
<?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/button1" //定义一个id名为button1
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="确认"/>
</LinearLayout>

创建一个菜单布局

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/add_item"
android:title="add" />
<item android:id="@+id/remove_item"
android:title="remove"/>
</menu>

3、在AndroidManifest文件中注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitytest">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ActivityTest">
<activity android:name=".ThirdActivity">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"/> <!--data标签指定了数据的协议必须是http协议-->
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.My_Category"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.example.activitytest.My_Category1"/>
</intent-filter>
</activity>
<activity android:name=".FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

2.2、活动间数据传递

1、Intent

Intent用于在Android各组件之间进行交互,不仅可以指明当前组件需要执行的动作,还可以在不同组件之间传递数据。具体如:

  • 启动Activity。如startActivity(intent)onActivityResults(int requestCode,int resultCode,Intent intent)

  • 启动Service。

  • 传递广播Broadcast。

1.1、Intent类型:

显式Intent:可以通过类名找到相应的组件。

1
2
Intent intent = new Intent(context,XXActivity.class); 
startActivity(intent);

隐式Intent:不指定具体的组件,但是会声明将要执行的操作。

1
2
3
4
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri data = Uri.parse("tel:" + "135xxxxxxxx");
intent.setData(data);
startActivity(intent);

1.2、PendingIntent

PendingIntent可以看作是对Intent的一个封装,但它不是立刻执行某个行为,而是满足某些条件或触发某些事件后才执行指定的行为(启动特定Service,Activity,BroadcastReceiver)。我们可以把Pending Intent交给其他程序,其他程序按照PendingIntent进行操作

使用场景:

  1. 当用户点击通知栏的时候,才执行的Intent(系统的NotificationManager执行的Intent)。
  2. 当用户操作悬浮在主屏幕中的小工具,才执行的Intent。
  3. 在未来某一个特定时间执行的Intent(系统的AlarmManager执行的Intent)

PengdingIntent实例化:PengdingIntent.getActivity()PengdingIntent.getService()pengdingIntent.getBroadcast()

Intent使用简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*向下一个活动传输数据*/
//FirstActivity
String data="hello ! secondActivity";
Intent intent3=new Intent(FirstActivity.this,SecondActivity.class);
intent3.putExtra("extra_data",data);

//SecondActivity

/**

* 在另一活动中取出传递数据。
* getIntent方法获取用于启动SecondActivity的Intent。
*/
Intent intent=getIntent();
String data=intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//向上一个活动返回数据,使用startActivityForResult()方法,在第二个活动销毁后回调onActivityResult()方法。
//FirstActivity
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);

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

//SecondActivity
public void onClick(View v){
Intent intent=new Intent();
intent.putExtra("data_return","hello,FirstActivity");
setResult(RESULT_OK,intent);
finish();
}

2.3、活动的生命周期

**返回栈:**一个任务就是一组存放在栈里的活动集合,这个栈称为返回栈。

活动状态:运行、暂停、停止、销毁

onCreate():在活动第一次被创建的时候调用,完成活动的初始化操作。如加载布局、绑定事件。

onStart():在活动由不可见变为可见的时候调用。

onResume():在活动准备好和用户进行交互时调用。此时的活动处于返回栈的栈顶,并且处于运行状态。

onPause():在系统准备去启动或恢复另一个活动的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据。

onStop():在活动完全不可见的时候调用。和onPause()方法的区别在于如果启动的是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会被执行。

onDestroy():在活动被销毁之前调用,之后活动的状态将变为销毁状态。

onRestart():在活动由停止状态变为运行状态之前调用,用以重启活动。

**总结:**以上方法除了重启,具有两两相对的关系。具体如下:

  • 完整生存期onCreate()onDestroy()方法之间经历的就是完整生存期。一个活动会在onCreate() 方法中完成各种初始化操作,而在onDestroy()中完成释放内存的操作。
  • 可见生存期onStart()onStop()方法之间经历的就是可见生存期。在可见生存期内,活动对于用户是可见的,即便有可能无法与用户进行交互。一般在onStart()方法中对资源进行加载,在onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
  • 前台生存期onResume()onPause()方法之间经历的就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动可以和用户进行交互。

2.4、活动的启动模式

standard:活动默认的启动模式。在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置,对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。

singleTop:当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。

singleTask:使用singleTop模式如果活动没有处于栈顶,还是可能会创建多个活动实例,那有没有什么办法可以让某个活动在整个应用程序的上下文只存在一个实例呢?当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已存在则使用该实例,并把在这个活动之上的所有活动出栈,如果没有就会创建一个新的活动。

singleInstance:指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。假设程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,便可以使用singleInstance模式。在该模式下,会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用同一个返回栈,也就解决了共享活动实例的问题。

3、广播机制

Android应用可以通过广播从系统或其他APP接收或发送消息,类似发布订阅模式。系统在某些状态改变如开机、充电时会发出广播,应用也可以自定义广播。

广播类型:标准广播、有序广播

标准广播:一种完全异步执行的广播,在广播发出后,所有的广播接收器几乎都会在同一时刻收到这条广播消息,接收者之间无先后顺序而言。效率较高,同时无法被截断。

有序广播:一种同步执行的广播。在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器的逻辑执行完毕后广播才会继续传递,有先后顺序,优先级高的先收到广播信息。并且前面的广播接收器还可以截断正在传递的广播。

本地广播:只在本APP发送和接收的广播。注册为本地广播的接收器无法收到标准广播。

注册广播的方式:

静态注册:在AndroidManifest.xml中注册

动态注册:在代码中注册,动态注册的广播需要在onDestroy()方法中调用unregisterReceiver()取消注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
动态注册一个能够监听网络变化的程序。
**/
public class MainActivity extends AppCompatActivity {

private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;

class NetworkChangeReceiver extends BroadcastReceiver{

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"network changes",Toast.LENGTH_SHORT).show();
}
}

@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);//动态注册的广播接收器一定要取消注册
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter=new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver=new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
}
}
//注:访问系统的网络状态需要声明权限,在AndroidManifest.xml文件中加入如下代码:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
静态注册实现开机启动
步骤:1、在Android Studio中右击包--new--other--broadcast receiver
**/
public class BootCompleteReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context,"Boot Complete!",Toast.LENGTH_SHORT).show();
}
}
//2、在AndroidManifest.xml文件中可以看到<receiver>标签。添加<intent-filter>内容
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
//3、添加权限声明
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

本地广播:发出的广播只能在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。以解决安全性问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
本地广播
**/
public class MainActivity extends AppCompatActivity {

private IntentFilter intentFilter;
private LocalReceiver localReceiver;//定义本地广播内部类
private LocalBroadcastManager localBroadcastManager;

class LocalReceiver extends BroadcastReceiver{

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"receive local broadcast",Toast.LENGTH_SHORT).show();
}
}

@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager=LocalBroadcastManager.getInstance(this);//获取实例

Button button=(Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);//发送本地广播
}
});
intentFilter=new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver=new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver,intentFilter);//注册本地广播监听器
}
}
1
2
3
4
Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);//发送自定义广播
sendOederedBroadcast(intent,null);//发送有序广播
localBroadcastManager.sendBroadcast(intent);

4、数据存储

4.1、Java IO回顾

文件流:直接操作文件的流,分为字节流(FileInputStream和FileOutputStream)和字符流(FileReader和FileWriter)。其中字节流可用于操作一切文件,而字符流只能用于操作文本文件。

img

程序从输入流读取数据,向输出流写入数据。

FileInputStream:从文件读取数据。

1
InputStream f=new FileInputStream("F:/java/hello.txt"); //读取磁盘文件

FileOutputStream:创建一个文件并向文件中写数据。

BufferedReader/BufferedWriter:处理流,将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。

BufferedInputStream/BufferedOutputStream:处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高 读写效率。

4.2、文件存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class MainActivity extends AppCompatActivity {

private EditText editText;

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

editText=(EditText) findViewById(R.id.edit);

String inputText=load();
if(!TextUtils.isEmpty(inputText)){
editText.setText(inputText);
editText.setSelection(inputText.length());
Toast.makeText(this,"restore success",Toast.LENGTH_SHORT).show();
}
}

/**
读文件
**/
private String load() {
FileInputStream in=null;
BufferedReader reader=null;
StringBuilder content=new StringBuilder();
try{
in=openFileInput("data");
reader=new BufferedReader(new InputStreamReader(in));
String line="";
while ((line=reader.readLine())!=null){
content.append(line);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(reader!=null){
try{
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
return content.toString();
}

/**
页面关闭时存储文本框输入内容。
**/
@Override
protected void onDestroy() {
super.onDestroy();
String inputText=editText.getText().toString();
save(inputText);
}

/**
存储文本
**/
public void save(String inputText){
FileOutputStream out=null; //写
BufferedWriter writer=null;
try{
out=openFileOutput("data", Context.MODE_PRIVATE); //返回一个FileOutputStream对象。
writer=new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if(writer!=null){
writer.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}

4.3、SharedPreferences存储

获取SharedPreferences对象的方式:(1)Context类中的getSharedPreferences()方法。(2)Activity类中的getPreferences()方法。PreferenceManager类中的getDefaultSharedPreferences()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MainActivity extends AppCompatActivity {

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

Button saveData=(Button) findViewById(R.id.save_data);
saveData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();//第一个参数指定SharedPreference文件的名称,如不存在则创建。第二个参数默认为MODE_PRIVATE,和传入0效果等同,表示只有当前程序可对该文件进行读写。
editor.putBoolean("married",false);
editor.putInt("age",28);
editor.putString("name","Tom");
editor.apply();
}
});

Button restoreData=(Button) findViewById(R.id.restore_data);
restoreData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences pref=getSharedPreferences("data",MODE_PRIVATE);
String name=pref.getString("name","");
int age=pref.getInt("age",0);
boolean married=pref.getBoolean("married",false);
Log.d("MainActivity","name is"+name);
Log.d("MainActivity","age is"+age);
Log.d("MainActivity","married is"+married);
}
});
}
}

4.4、SQLite数据库存储

SQLite是一款轻量级的关系型数据库,运算速度快,占用资源少,支持SQL语法以及数据库的ACID特性,用于存储大量复杂的关系型数据。

1、创建数据库

  • SQLiteOpenHelper帮助类实现对数据库进行创建和升级。
  • SQLiteOpenHelper是一个抽象类,提供onCreate()onUpgrade()两个抽象方法,在使用时需要创建一个自己的帮助类继承它。
  • SQLiteOpenHelper中还有两个非常重要的实例方法:getReadableDatabase()getWriteableDatabase()。用于创建或打开一个现有的数据库(如无则创建),并返回一个可对数据库进行读写操作的对象。当磁盘已满不可写入时,getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWriteableDatabase()将抛出异常。
  • SQLiteOpenHelper的构造方法接收4个参数,分别是Context、数据库名、Cursor(返回查询数据)、当前数据库版本号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//新建MyDatabaseHelper类继承自SQLiteOpenHelper实现数据库创建与升级。

public class MyDatabaseHelper extends SQLiteOpenHelper {
//定义SQL语句,real为浮点型。
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(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mcontext=context;
}

//数据库创建成功之后onCreate方法将不再被执行,更新需要在onUpgrade中实现。被将数据库版本号变更为比1大的数字。
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mcontext,"Create success",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);

}
}

2、增删改查(CURD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class MainActivity extends AppCompatActivity {

private MyDatabaseHelper dbHelper;


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

dbHelper=new MyDatabaseHelper(this,"BookStore.db",null,2);//需要更改,版本设置为2
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});

//添加
Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("name","细雨湿流光");
values.put("author","jun");
values.put("price",34);
values.put("pages",344);
db.insert("Book",null,values);
values.clear();
}
});

//修改
Button updateData=(Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("name","时光笔迹"); //将作者为jun的这行数据更新书名为“时光笔迹”
db.update("Book",values,"author=?",new String[]{"jun"});
}
});

//删除
Button deleteData=(Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db=dbHelper.getWritableDatabase();
db.delete("Book","pages>?",new String[]{"400"});
}
});

//查询
Button queryData=(Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db=dbHelper.getWritableDatabase();
//查询Book表中的所有数据
Cursor cursor=db.query("Book",null,null,null,null,null,null);
if(cursor.moveToFirst()){//将数据指针移到第一行的位置。
do{
String name=cursor.getString(cursor.getColumnIndex("name"));
String author=cursor.getString(cursor.getColumnIndex("author"));
int pages=cursor.getInt(cursor.getColumnIndex("pages"));
double price=cursor.getDouble(cursor.getColumnIndex("price"));

Log.d("MainActivity",name);
Log.d("MainActivity",author);
Log.d("MainActivity", String.valueOf(pages));
Log.d("MainActivity", String.valueOf(price));
}while (cursor.moveToNext());
}
cursor.close();
}
});
}
}

3、使用SQL操作数据库

1
2
3
4
5
6
7
8
//添加
db.execSQL("insert into Book (name,author,pages,price) values(?,?,?,?)",new String[]{"xxx","xxx","xxx","xxx"});
//修改
db.execSQL("update Book set price=? where name=?",new String[]{"10","xxx"});
//删除
db.execSQL("delete from Book where pages>?",new String[]{"500"});
//查询
db.rawQuery("select * from Book",null);

4.5、使用LitePal操作数据库

1、配置LitePal

1、app/build.gradle,在dependencies中添加

1
implementation 'org.litepal.android:core:1.3.2'

2、app/src/main 目录下新建assets目录,并在该目录新建litepal.xml 文件

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value="BookStore"></dbname>
<version value='1'></version>
<list>
<mapping class="com.example.litepalapplication.Book"></mapping>
</list>
</litepal>

3、修改AndroidManifest.xml 文件,application标签下添加

1
2
3
<application
android:name="org.litepal.LitePalApplication"
</application>

2、创建JavaBean

1
2
3
4
5
6
7
8
9
public class Book extends DataSupport { //继承DataSupport
private int id;
private String author;
private double price;
private int pages;
private String name;

//此处省略get/set方法
}

3、增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class MainActivity extends AppCompatActivity {

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

Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Connector.getDatabase();
}
});

Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book=new Book();
book.setName("xxx");
book.setAuthor("xxx");
book.setPages(22);
book.setPrice(56);
book.save();
}
});

Button updateData=(Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book=new Book();
book.setPrice(23);
book.updateAll("name=? and author=? ","xxx","xxx");
}
});

Button deleteData=(Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DataSupport.deleteAll(Book.class,"price<?","15");
}
});

Button queryData=(Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Book> books=DataSupport.findAll(Book.class);//查询所有
for(Book book:books){
Log.d("MainActivity",book.getName());
}
Book firstBook=DataSupport.findFirst(Book.class);//查询第一行
Book lastBook=DataSupport.findLast(Book.class);//查询最后一行
List<Book> books1=DataSupport.select("name","author").find(Book.class);//查询指定列数据
List<Book> books2=DataSupport.where("pages>?","400").find(Book.class);//条件查询
List<Book> books3=DataSupport.order("price desc").find(Book.class);//结果降序排列,asc升序
List<Book> books4=DataSupport.limit(3).find(Book.class);//只查表中前三条数据
List<Book> books5=DataSupport.limit(3).offset(1).find(Book.class);//查询偏移1位的三条连续数据,即第2、3、4条数据。
List<Book> books6=DataSupport.select("name","author","pages").where("pages>?","50").order("pages").limit(10).offset(1).find(Book.class);//组合查询


}
});
}
}

5、内容提供器

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

5.1、运行时权限

普通权限:不会直接威胁到用户安全和隐私的权限,对于这部分权限申请,系统会自动帮我们授权,而不需要用户再去手动操作。

危险权限:会触及用户隐私和安全,如获取联系人信息、定位设备的物理位置。需要用户手动授权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//打电话示例(需要权限)
//AndroidManifest.xml文件中添加:
//<uses-permission android:name="android.permission.CALL_PHONE"/>
public class MainActivity extends AppCompatActivity {

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

Button makeCall=(Button) findViewById(R.id.make_call);
makeCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//ContextCompat.checkSelfPermission方法判断用户是否授权,接收两个参数,第一个是Context,第二个是具体的权限名。已授权则调用打电话,没有则申请授权,ActivityCompat.requestPermissions方法接收三个参数,第一个参数为Activity的实例,第二个是String数组,放入申请的权限名,第三个为请求码,唯一值即可。
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},1);
}else{
call();
}
}
});
}

private void call(){
try{
Intent intent=new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (SecurityException e){
e.printStackTrace();
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
call();
}else{
Toast.makeText(this,"You denied the permission",Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}

5.2、访问其他程序中的数据

如果一个应用程序通过内容提供器对其数据提供了外部访问接口,那么其他应用程序都可以对这部分数据进行访问。Android系统自带的电话簿、短信、媒体库都提供了这样的访问接口供第三方应用访问数据资源。

ContentResolver

应用程序在访问内容提供器中共享的数据时需要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取该类的实例。并且,ContentResolver提供了一系列方法用以对数据进行增删改查操作,在CRUD方法中接收内容URI参数。

URI字符串:由authority和path组成,示例:content://com.example.app.provider/table1。

在得到URI字符串后,需要将其解析为Uri对象才可作为参数传入。

1
Uri uri=Uri.parse("content://com.example.app.provider/table1");

查询table1中的数据:

1
2
3
4
5
6
7
Cursor cursor=getContentResolver().query(uri,projection,selection,
selectionArgs,sortOrder);
//uri:指定查询某个应用下的某个表
//projection:指定查询的列名
//selection:指定where的约束条件
//selectionArgs:为where中的占位符提供具体值
//sortOrder:指定查询结果的排序方式

从Cursor中取出数据:

1
2
3
4
5
6
7
if(cursor!=null){
while(cursor.moveToNext()){
String column1=cursor.getString(cursor.getColumnIndex("column1"));
int column2=cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}

添加数据:

1
2
3
4
ContentValues values=new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);

更新数据:

1
2
3
ContentValues values=new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1=? and column2=?",new String[]{"text","1"});

删除数据:

1
getContentResolver().delete(uri,"column2=?",new String[]{"1"});

简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//获取手机通讯录
//AndroidManifest.xml文件中添加:
//<uses-permission android:name="android.permission.READ_CONTACTS"/>
public class MainActivity extends AppCompatActivity {

ArrayAdapter<String> adapter;
List<String> contactList=new ArrayList<>();

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

ListView contactsView=(ListView) findViewById(R.id.contacts_view);
adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,contactList);
contactsView.setAdapter(adapter);

if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else{
readContacts();
}
}

protected void readContacts(){
Cursor cursor=null;
try{
cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if(cursor!=null){
while (cursor.moveToNext()){
String displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactList.add(displayName+"\n"+number);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 1:
if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
readContacts();
}else{
Toast.makeText(this,"you denied the permission",Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}

6、多媒体

6.1、使用通知

1
implementation "com.android.support:support-compat:28.0.0"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private NotificationManager manager;// 创建通知管理器

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "chat";
String channelName = "聊天消息";
int importance = NotificationManager.IMPORTANCE_HIGH;
createNotificationChannel(channelId, channelName, importance);
}

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

@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
NotificationManager notificationManager = (NotificationManager) getSystemService(
NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}

@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.chat: //聊天消息
Intent intent=new Intent(MainActivity.this,OtherActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(MainActivity.this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this, "chat")
.setAutoCancel(true)
.setContentTitle("长相思")
.setContentText("汴水流,泗水流,流到瓜州古渡头,吴山点点愁")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
//设置红色
.setColor(Color.parseColor("#F00606"))
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.build();
manager.notify(1, notification);
break;
}
}
}

7、服务

服务是Android中实现程序后台运行的解决方案,适合去执行那些不需要和用户交互而且还要求长期运行的任务。

服务并不是运行在一个独立的进程当中,而是依赖创建服务时所在的应用程序进程。当某个应用出现进程被杀掉时,所有依赖于该进程的服务也会停止运行。

7.1、Android多线程编程

Android不允许在子线程中进行UI操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//示例:点击按钮更新textview的文本。
//
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private TextView textView;
public static final int UPDATE_TEXT=1;

private Handler handler=new Handler(){
public void handleMessage(Message msg){//handleMessage是在主线程中运行
switch (msg.what){
case UPDATE_TEXT:
textView.setText("Nice to meet you");
break;
default:
break;
}
}
};

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

textView=(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();
break;
default:
break;
}
}
}

异步消息处理机制:

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

Handler:用于发送和处理消息。发送消息使用sendMessage()方法,处理消息handleMessage()

MessageQueue:消息队列,用于存放所有通过Handler发送的消息。每个线程只有一个MessageQueue对象。

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

使用AsyncTask:

AsyncTask是一个抽象类,基于异步消息处理机制从子线程切换到主线程。具有3个泛型参数:Params、Progress、Result

Params:用于在后台任务中传入参数。

Progress:如需显示当前的进度,则使用这里指定的泛型作为进度单位。

Result:任务执行完后,如需对结果进行返回,则指定这里的泛型作为返回值类型。

1
2
3
class DownloadTask extends AsyncTask<void,Integer,Boolean>{
...
}

重写方法:

onPreExecute():在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。

doInBackground(Params...):这个方法中的所有代码都会在子线程中执行,在这里处理耗时任务,任务一旦完成就可以通过return语句来将任务的执行结果返回。这个方法不可以进行UI操作。

onProgressUpdate(Progress...):当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate(Progress…)方法就会很快被调用,可进行UI操作。

onPostExecute(Result):任务执行完毕并通过return进行返回时,这个方法就会很快被调用。

7.2、服务的基本用法

定义一个服务MyService,该服务继承Service,new——service——service。

重写onCreate() :服务创建时调用、onStartCommand() :每次服务启动的时候调用、onDestroy():服务销毁的时候调用。

活动与服务通信:

示例:MyService中构建一个下载功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MyService extends Service {

private DownloadBinder mBinder=new DownloadBinder();
//下载(开始下载、获取进度)
//创建一个Binder对象对下载功能进行管理。
class DownloadBinder extends Binder{
public void startDownload(){
Log.d("MyService","startDownload executed");
}
public int getProgress(){
Log.d("MyService","getProgress executed");
return 0;
}
}

public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}

@Override
public void onCreate() {
super.onCreate();
Log.d("MyService","onCreate executed");
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService","onDestroy executed");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService","onStartCommand exected");
return super.onStartCommand(intent, flags, startId);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

//活动与服务绑定,进而调用Binder中的方法。
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {//绑定
downloadBinder=(MyService.DownloadBinder)service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}

@Override
public void onServiceDisconnected(ComponentName name) {//解绑

}
};

@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);
Button bindService=(Button) findViewById(R.id.bind_service);
Button unbindService=(Button) findViewById(R.id.unbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService.setOnClickListener(this);
unbindService.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;
case R.id.bind_service:
Intent bindIntent=new Intent(this,MyService.class);// 将活动和服务绑定
bindService(bindIntent,connection,BIND_AUTO_CREATE);// 绑定后自动创建服务,使得MyService的onCreate方法得到执行。
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}

7.3、服务的生命周期

  • 一旦调用Context的startService()方法,服务就会开始启动,并回调onStartCommand()方法。如果服务还未创建,onCreate()方法会先于onStartCommand()方法调用。
  • 服务启动了会一直保持运行状态,直到stopService()stopSelf()方法被调用。每个服务只存在一个实例,无论调用多少次启动,只需调用一次停止,服务就会停止。
  • 在调用stopService()unbindService()方法时,onDestroy()方法会被执行。当启动和绑定同时被调用,则相应的同时调用停止服务和解绑onDestroy()方法才会被执行。

7.4、前台服务

服务在系统出现内存不足时可能回收掉正在后台运行的服务。如果希望服务一直保持运行,则需要使用到前台服务。

前台服务会有一个正在运行的图标一直在系统状态栏显示,类似于通知的效果。

1
2
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MyService extends Service{
private NotificationManager notificationManager;
private Notification notification;
private NotificationChannel notificationChannel;
private final static String NOTIFICATION_CHANNEL_ID = "CHANNEL_ID";
private final static String NOTIFICATION_CHANNEL_NAME = "CHANNEL_NAME";
private final static int FOREGROUND_ID=1;
....
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService","onStartCommand exected");
super.onStartCommand(intent, flags, startId);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
notificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(notificationChannel);
}
intent=new Intent(this,MainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,0);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){
notification=new Notification.Builder(this,NOTIFICATION_CHANNEL_ID).setContentTitle("今日天气").setContentText(" 天气清和、微风不噪").setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)).setContentIntent(pendingIntent).build();

}
notification.flags |= Notification.FLAG_NO_CLEAR;
startForeground(1,notification);
return Service.START_STICKY;
}
}