第一个安卓项目
环境:
- Android Studio 2024.2
- Java 21.0.5
- SDK:API 24
- 虚拟机:Pixel 9 Pro API 27 / Android 8.1
项目目录
新建项目,可得到目录内容:
app
├── manifests
│ └── AndroidManifest.xml
├── java
│ ├── com.dta.first
│ │ └── MainActivity.java
│ ├── com.dta.first (androidTest)
│ ├── com.dta.first (test)
│ └── java (generated)
├── res
│ ├── drawable
│ ├── layout
│ ├── mipmap
│ ├── values
│ └── xml
└── res (generated)
Gradle Scripts
manifests目录
其中manifest文件用来存放一些安卓应用程序的配置文件,通常每个安卓应用都包含AndroidManifest.xml
,是一个全局配置文件,会在内部定义一些组件、权限、图标等配置。
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
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.FirstApplication" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
</manifest>
|
其中application节点包含了应用项目中application组建中的根结点
配置 |
含义 |
android:allowBackup=”true” |
允许备份,默认为true |
android:icon=”@mipmap/ic_launcher” |
应用程序图标 |
android:label=”@string/app_name” |
应用程序的名字 |
android:roundIcon=”@mipmap/ic_launcher_round” |
圆形图标 |
android:theme=”@style/Theme.FirstApplication” |
应用主题 |
activity节点(安卓里重要的组成部分),默认界面
1 2 3 4
| # 主界面 <action android:name="android.intent.action.MAIN" /> # 启动界面 <category android:name="android.intent.category.LAUNCHER" />
|
java目录
然后还有java目录,其下存放的就是我们的代码,创建时我选择了一个默认界面,因此运行后可以得到:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.dta.first;
import android.os.Bundle;
import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); } }
|
可以看到默认的代码,其中继承了一个AppCompatActivity
类,重写了一个onCreate
方法,onCreate就是应用程序启动后打开第一个界面时会回调该方法。
res目录
drawable:存放应用所需的图片资源(如 PNG、JPG 等)。
layout:存放 XML 格式的布局文件,定义了应用的 UI 结构。
mipmap:存放应用图标(通常是不同分辨率的图标文件)。
values:存放资源值文件(如 strings.xml、colors.xml 等),用于定义字符串、颜色、样式等资源。
xml:存放自定义的 XML 配置文件(如导航、网络安全配置等)。
**res (generated)**:这是由工具生成的资源文件夹,内容通常是编译时生成的。
Gradle Scripts目录
其下可以配置一些需要打包或者要引入一些外部的类库,或指定gradle版本等。包含项目的构建脚本,主要用于定义依赖关系、构建配置和任务。
编写应用程序
布局
先看看默认的布局:

此时,我希望这个App最上方,显示出来我的项目名称,和他的logo,就可以通过调整themes下的style中继承的类即可修改:


在layout中可以修改页面的样式,TextView中就是页面中间默认的那个存放了Hello World的文本框,我们也可以在其中加一些内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <TextView android:id="@+id/sample_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/btn_change" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="change" tools:ignore=",MissingConstraints" /> </Button>
|
可以看到,我加了一个按钮组件,id为btn_change,其中的text设置为change:

java代码
默认代码中存在以下:
1
| setContentView(R.layout.activity_main);
|
这个就是显示系统默认的布局文件,也就是默认关联到layout下的activity_main。此处也可以在onCreate下通过关联id操作组件:
为了顺利绑定,需要设置botton的id:
1 2 3
| <Button android:id="@+id/btn_change" >
|
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
| public class MainActivity extends AppCompatActivity {
private Button btn_change; private TextView tv_helloworld;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_change = findViewById(R.id.btn_change);
tv_helloworld = findViewById(R.id.sample_text);
btn_change.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tv_helloworld.setText("我被点击了~"); } }); } }
|
功能简述:点击btn后,textview中内容变成我被点击了~
:

常见组件
四大组件
Activity(活动)
Activity是Android应用的核心组件之一,负责管理用户界面和用户交互。每个Activity代表一个屏幕或界面。每个Activity之间通过Intent进行通信。
主要功能:管理UI,处理用户输入,控制界面跳转和生命周期。
生命周期:包括onCreate、onStart、onResume等方法,用于管理从创建到销毁的整个过程。
关键点:
每个Activity都有一个Intent,用于启动自身或其他Activity。
startActivityForResult可以在不同Activity之间返回数据。
Activity的四种基本状态:
(1)Active/Running:一个新的Activity启动入栈后,会显示在屏幕最前端,处于栈顶的页面为可见并可与用户交互的激活状态,叫做活动状态或者运行状态。
(2)Paused:当Activity失去焦点,被一个非全屏幕的Activity或者一个透明的Activity被放置在栈顶时,被叫做暂停状态。 但是此时依旧存在所有的状态,依然可见,但是已失去了焦点故不可与用户交互。
(3)Stopped:如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态。依然保持所有的状态和成员信息,但是不再可见。
(4)Killed:如果一个Activity是Paused或者Stopped状态,系统可以将该Activiy从内存中删除。
当一个Activity状态被创建、销毁或者启动另一个Activity时,它在这四种状态之间进行转换,这种转换的发生依赖于用户程序的动作。
BroadCastReceiver(广播接收器)
BroadCastReceiver用于接收系统或应用发出的广播事件,并根据事件触发相应逻辑。用于不同组件之间通信(包括应用内/不同应用之间);用于与Android系统在特定情况下的通信(如当电话呼入时、网络可用时);还可用于多线程通信。
主要功能:监听系统或自定义事件,例如电量变化、网络状态、电话呼入等。
注册方式:
静态注册:在AndroidManifest.xml中声明。应用即使未运行,系统广播依然能触发。
动态注册:在代码中使用registerReceiver()注册,随应用运行和销毁。
典型广播:
系统广播:android.intent.action.BOOT_COMPLETED(设备启动完成)
自定义广播:应用内部自定义事件广播。
实现原理:
Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型
模型中有三个角色:
- 消息订阅者(广播接收者)
- 消息发布者(广播发布者)
- 消息中心(AMS,Activity Manager Service)
过程:
- 广播接收者通过Binder机制在AMS注册
- 广播发送者通过Binder机制向AMS发送广播
- AMS根据广播发送者要求,在已注册列表中,寻找合适的广播接收者(寻找依据:IntentFilter/Permission)
- AMS将广播发送到合适的广播接收者相应的消息循环队列中
Service(服务)
Service用于在后台执行长时间运行的任务,无需与用户直接交互。是一种长生命周期的,没有可视界面,运行与后台的一种服务程序。一个Service可以和多个客户绑定,当多个客户都解除绑定后,系统会销毁Service。
主要功能:进行耗时操作(如下载、播放音乐)或持续性任务(如后台同步)。
类型:
前台服务:用户可感知,带有通知栏提示。
后台服务:用户不可感知,在后台运行。
生命周期:
onStartCommand:启动服务的方法,常用于执行任务。
onBind:绑定服务,提供与其他组件的通信接口。
onDestroy:销毁服务,释放资源。
关键点:Android 8.0(API 26)后,限制后台服务,需要使用前台服务或JobScheduler。
Content Provider(内容提供者)
ContentProvider用于在不同应用间安全地共享数据。
主要功能:提供统一的接口,允许其他应用访问或修改数据。
访问方式:通过URI访问,支持CRUD(增删改查)操作。
典型应用:访问联系人数据、共享媒体文件或应用数据库。
核心方法:
- query:查询数据。
- insert:插入数据。
- update:更新数据。
- delete:删除数据。
权限控制:通过<permission>
标签限制外部访问,防止数据泄露。
实操部分——Activity的使用
在 Android 应用开发中,Activity 是最基本的组件之一,代表应用的一个界面。在本节中,我们将通过三个类:MainActivity、SubActivity02 和 SubActivity03,实战演示:
- 如何启动一个新的 Activity;
- 如何从另一个 Activity 返回数据;
- 如何使用 ActivityResultLauncher 实现更现代的返回机制
一、启动另一个 Activity(标准跳转)
在 MainActivity 中点击按钮 btn_start,通过 Intent 启动 SubActivity02:
1 2 3 4 5 6 7 8
| btn_start = findViewById(R.id.btn_startActicity); btn_start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, SubActivity02.class); startActivity(intent); } });
|
这段代码使用了传统方式 startActivity(intent) 进行跳转。被启动的 SubActivity02:
1 2 3 4 5 6
| @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sub02); Log.i("ttttage", "SubActivity02 onCreate"); }
|
在 Logcat 中会输出日志:
1
| I/ttttage: SubActivity02 onCreate
|
二、启动 Activity 并获取结果(返回数据)
除了普通跳转,有时候我们还需要 启动一个 Activity 并在它关闭后获取结果,这时候可以使用:
方法一(推荐):ActivityResultLauncher
1 2 3 4 5 6 7 8
| ActivityResultLauncher launcher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult o) { Log.i("ttttag", o.getData().getStringExtra("key1")); } });
|
当点击 btn_startForResult 按钮时,会启动 SubActivity03:
1 2 3 4 5 6 7 8
| btn_startForResult = findViewById(R.id.btn_startActicityGetResult); btn_startForResult.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, SubActivity03.class); launcher.launch(intent); } });
|
在 SubActivity03 中,我们模拟用户点击一个 TextView 之后返回数据:
1 2 3 4 5 6 7 8 9 10 11
| tv = findViewById(R.id.tv_setResult); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(SubActivity03.this, MainActivity.class); intent.putExtra("key1", "这是结果1,来自SubActivity03"); setResult(234, intent); SubActivity03.this.finish(); } });
|
结果在 MainActivity 的回调中处理,如前所述,直接打印:
1
| I/ttttag: 这是结果1,来自SubActivity03
|
方法二(旧方法):startActivityForResult + onActivityResult
1 2 3 4 5 6 7 8 9 10
| @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data);
if (data == null) return; if (resultCode == requestCode && requestCode == 234) { String ret = data.getStringExtra("key1"); Log.d("ttttag", ret); } }
|
虽然可以使用,但 Google 已经建议使用 ActivityResultLauncher 来替代这种方式
完整代码
MainActivity.java
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 84 85
| package com.dta.test02;
import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button;
public class MainActivity extends AppCompatActivity { Button btn_start, btn_startForResult;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
btn_start = findViewById(R.id.btn_startActicity); btn_start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, SubActivity02.class); startActivity(intent); } });
ActivityResultLauncher<Intent> launcher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getData() != null) { String ret = result.getData().getStringExtra("key1"); Log.i("ttttag", "新方式返回数据:" + ret); } } });
btn_startForResult = findViewById(R.id.btn_startActicityGetResult); btn_startForResult.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, SubActivity03.class); launcher.launch(intent); } }); }
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data);
if (data == null) return;
if (resultCode == requestCode && requestCode == 234) { String ret = data.getStringExtra("key1"); Log.d("ttttag", "旧方式返回数据:" + ret); } }
public native String stringFromJNI(); }
|
SubActivity02.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.dta.test02;
import android.app.Activity; import android.os.Bundle; import android.util.Log;
import androidx.annotation.Nullable;
public class SubActivity02 extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sub02);
Log.i("ttttage", "SubActivity02 onCreate"); } }
|
SubActivity03.java
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
| package com.dta.test02;
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView;
import androidx.annotation.Nullable;
public class SubActivity03 extends Activity { TextView tv;
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sub03);
tv = findViewById(R.id.tv_setResult); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent resultIntent = new Intent(); resultIntent.putExtra("key1", "这是结果1,来自SubActivity03"); setResult(234, resultIntent); finish(); } }); } }
|
效果
主页面:

点击第一个按钮,跳转到Activity02,且能在日志中看到SubActivity02 onCreate
:


点击第三个按钮,跳转到Activity03,且点击03页面中的textview,会关闭03页面,并返回Main页面,且带回数据到log:


回到主页面,03页面关闭,并发送了结果回到主页面:

实操部分——Service的使用
MainActivity.java代码节选
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 { Button btn_startService, btn_stopService; Button btn_bindService, btn_unbindService; MyBindService.MyBinder myBinder;
btn_startService = findViewById(R.id.btn_startService); btn_startService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, MyService01.class); startService(intent); } });
btn_stopService = findViewById(R.id.btn_stopService); btn_stopService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, MyService01.class); stopService(intent); } });
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { if (service instanceof MyBindService.MyBinder) { myBinder = (MyBindService.MyBinder) service; } }
@Override public void onServiceDisconnected(ComponentName name) { } };
btn_bindService = findViewById(R.id.btn_bindService); btn_bindService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, MyBindService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } });
btn_unbindService = findViewById(R.id.btn_unbindService); btn_unbindService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { unbindService(conn); } catch (Exception e) { throw new RuntimeException(e); } } }); }
|
MyService01.java
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
| package com.dta.test02.Service;
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log;
import androidx.annotation.Nullable;
public class MyService01 extends Service { String logTag = "ttttttag";
@Override public void onCreate() { super.onCreate(); Log.i(logTag, "MyService01 onCreate"); }
@Override public void onDestroy() { super.onDestroy(); Log.i(logTag, "MyService01 onDestroy"); }
@Nullable @Override public IBinder onBind(Intent intent) { return null; } }
|
MyBindService.java
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
| package com.dta.test02.Service;
import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log;
import androidx.annotation.Nullable;
public class MyBindService extends Service { String logTag = "tttttag";
private MyBinder binder = new MyBinder();
public class MyBinder extends Binder { public MyBinder() { Log.i(logTag, "MyBinder 构造方法调用"); }
public MyBindService getServiceBinder() { return MyBindService.this; } }
@Nullable @Override public IBinder onBind(Intent intent) { return binder; }
@Override public boolean onUnbind(Intent intent) { Log.i(logTag, "onUnbind"); return super.onUnbind(intent); }
@Override public void onCreate() { super.onCreate(); }
@Override public void onDestroy() { super.onDestroy(); } }
|
实操部分——Broadcast的使用
MainActivity.java节选
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
| public class MainActivity extends AppCompatActivity { Button btn_sendBroadcast, btn_orderBroadcas; btn_sendBroadcast = findViewById(R.id.btn_broadcast); btn_sendBroadcast.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, MyReceiver.class); intent.putExtra("key2", "这是无序广播内容1"); intent.setAction("guolvRuleaaa"); sendBroadcast(intent); } });
OrderReceiver01 orderReceiver01 = new OrderReceiver01(); OrderReceiver02 orderReceiver02 = new OrderReceiver02(); OrderReceiver03 orderReceiver03 = new OrderReceiver03();
IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("guolvRuleaaa");
registerReceiver(orderReceiver01, intentFilter, RECEIVER_EXPORTED); registerReceiver(orderReceiver02, intentFilter, RECEIVER_EXPORTED); registerReceiver(orderReceiver03, intentFilter, RECEIVER_EXPORTED);
btn_orderBroadcast = findViewById(R.id.btn_orderBroadcast); btn_orderBroadcast.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction("guolvRuleaaa");
String data = "这是原始数据"; Bundle extData = new Bundle();
sendOrderedBroadcast(intent, null, new MyReceiver(), null, 0, data, extData); } }); }
|
MyReceiver.java
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
| package com.dta.test02.MyReceiver;
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log;
public class MyReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction();
if ("guolvRuleaaa".equals(action)) { String data = getResultData();
Log.i("tttttag", "接收到内容:" + data); } } }
|
OrderReceiver01/02/03.java
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
| package com.dta.test02.MyReceiver;
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log;
public class OrderReceiver01 extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction();
if ("guolvRuleaaa".equals(action)) { String data = getResultData(); Log.i("ttttttaaaag", "接收到内容:" + data);
setResultData("内容已经被 OrderReceiver01/02/03 修改");
} } }
|
实操部分——ContentProvider的使用
MainActivity.java
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 84
| public class MainActivity extends AppCompatActivity {
Button btn_add, btn_del, btn_update, btn_select;
static final Uri uri = Uri.parse("content://com.dta.db.authority/user");
btn_add = findViewById(R.id.btn_add); btn_add.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentValues values = new ContentValues(); values.put("uid", uid); values.put("name", "王五"); values.put("age", "20"); values.put("score", "78");
ContentResolver contentResolver = getContentResolver(); contentResolver.insert(uri, values); } });
btn_del = findViewById(R.id.btn_del); btn_del.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentResolver contentResolver = getContentResolver(); int delCount = contentResolver.delete(uri, "age < ?", new String[]{"18"}); if (delCount > 0) { Log.i("ttttagggg", "del Count : " + delCount); } } });
btn_update = findViewById(R.id.btn_update); btn_update.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentValues values = new ContentValues(); values.put("age", "66");
ContentResolver contentResolver = getContentResolver(); int updateCount = contentResolver.update(uri, values, "uid = ?", new String[]{"3"}); if (updateCount > 0) { Log.i("ttttaggg", "update Count : " + updateCount); } } });
btn_select = findViewById(R.id.btn_select); btn_select.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentResolver contentResolver = getContentResolver(); Cursor cursor = contentResolver.query(uri, new String[]{"id", "name", "age", "score"}, null, null, null);
if (cursor == null) return;
while (cursor.moveToNext()) { @SuppressLint("Range") int uid = cursor.getInt(cursor.getColumnIndex("uid")); @SuppressLint("Range") String name = cursor.getString(cursor.getColumnIndex("name")); @SuppressLint("Range") int age = cursor.getInt(cursor.getColumnIndex("age")); @SuppressLint("Range") int score = cursor.getInt(cursor.getColumnIndex("score"));
Log.i("tttttag", "uid=" + uid + ", name=" + name + ", age=" + age + ", score=" + score); } } }); }
|
代码运行效果
- 新增数据 → 点击后往数据库的 user 表插入一条 uid、姓名、年龄、分数。
- 删除数据 → 删除所有 age < 18 的用户。
- 更新数据 → 修改 uid=3 的用户的年龄为 66。
- 查询数据 → 读取并输出所有用户的 id、name、age、score。
以下为另一module db的内容:
DBHelper.java
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
| package com.dta.db;
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;
public class DBHelper extends SQLiteOpenHelper {
private static final String DBNAME = "myDbName.db"; private static final int VERSION = 1; public static final String TABLE_USER = "user";
public DBHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) { super(context, DBNAME, factory, VERSION); }
@Override public void onCreate(SQLiteDatabase db) { String sqlStr = "CREATE TABLE IF NOT EXISTS " + TABLE_USER + " (uid INTEGER PRIMARY KEY AUTOINCREMENT," + " name VARCHAR(30)," + " age INTEGER," + " score DOUBLE )";
db.execSQL(sqlStr); }
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
|
代码运行效果
- 新增数据 → 点击后往数据库的 user 表插入一条 uid、姓名、年龄、分数。
- 删除数据 → 删除所有 age < 18 的用户。
- 更新数据 → 修改 uid=3 的用户的年龄为 66。
- 查询数据 → 读取并输出所有用户的 id、name、age、score。
- DBHelper 是 ContentProvider 数据存储的核心,负责底层数据库的创建和维护。
- 当 MyProvider 第一次被调用时,如果数据库文件不存在,就会触发 onCreate() 建表。
- 在这个例子中,user 表结构和 MainActivity 按钮的 CRUD(增删改查)操作是一一对应的。
MyProvider.java
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
| package com.dta.db;
import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.text.TextUtils; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.Nullable;
public class MyProvider extends ContentProvider {
private static final UriMatcher uriMatcher;
private static final String AUTHORITY_PROVIDER = "com.dta.db.authority";
private static final int CODE_PROVIDER_USER = 11;
private DBHelper dbHelper; private SQLiteDatabase db;
static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY_PROVIDER, DBHelper.TABLE_USER, CODE_PROVIDER_USER); }
@Override public boolean onCreate() { dbHelper = new DBHelper(getContext(), "", null, 1); db = dbHelper.getWritableDatabase();
db.execSQL("INSERT INTO " + DBHelper.TABLE_USER + " VALUES(1, '张三', 19, 87)"); db.execSQL("INSERT INTO " + DBHelper.TABLE_USER + " VALUES(2, '李四', 18, 86)");
return true; }
@Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { String tableName = getTableName(uri); if (TextUtils.isEmpty(tableName)) { return null; } return db.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null); }
@Nullable @Override public String getType(@NonNull Uri uri) { return ""; }
public String getTableName(Uri uri) { if (uriMatcher.match(uri) == CODE_PROVIDER_USER) { return DBHelper.TABLE_USER; } return ""; }
@Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { String tableName = getTableName(uri); if (TextUtils.isEmpty(tableName)) { return null; } long insertCount = db.insert(tableName, null, values); Log.i("ttttaaggg", insertCount > 0 ? "insert success" : "insert fail"); return uri; }
@Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { String tableName = getTableName(uri); if (TextUtils.isEmpty(tableName)) { return 0; } int deleteCount = db.delete(tableName, selection, selectionArgs); Log.i("ttttaaggg", deleteCount > 0 ? "delete success" : "delete fail"); return deleteCount; }
@Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { String tableName = getTableName(uri); if (TextUtils.isEmpty(tableName)) { return 0; } int updateCount = db.update(tableName, values, selection, selectionArgs); Log.i("ttttaaggg", updateCount > 0 ? "update success" : "update fail"); return updateCount; } }
|
- ContentResolver 与 ContentProvider 的关系
- MainActivity 用 ContentResolver 调用 insert、delete、update、query。
- ContentResolver 会自动把请求转发到匹配 authorities 的 ContentProvider(这里就是 MyProvider)。
- UriMatcher 的作用
- 能根据传入的 URI 判断要操作的表。
- 好处是可以扩展多个表,只要多加 addURI() 即可。
- 调用链
1
| MainActivity → ContentResolver → MyProvider → DBHelper → SQLite 数据库
|