1、Service
service使用场景:
1、用于长期执行某些操作,并且甚至与ui线程也就是主线程没有交互。比如启动app直接去下载文件。
2、跨进程间通信,比如app A程序中service被app B中程序调用。
service 默认是运行在调用者进程的主线程中,所以不能在service中做耗时操作,不然ui线程会卡死,出现ANR程序无响应等现象,一般是采用在service里面创建一个Thread来执行耗时任务。
使用HandlerThread处理耗时:
// MyService.java
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "MyService";
private HandlerThread handlerThread;
private Handler handler;
@Override
public void onCreate() {
super.onCreate();
// 创建一个 HandlerThread
handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
// 获取 Handler
handler = new Handler(handlerThread.getLooper());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 在 Handler 中执行耗时任务
handler.post(new Runnable() {
@Override
public void run() {
// 执行耗时操作
Log.d(TAG, "Service is running...");
try {
Thread.sleep(5000); // 模拟耗时5秒的操作
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "Task completed!");
// 停止服务
stopSelf(); // 可选:完成任务后停止服务
}
});
// 如果服务被系统杀死,自动重新创建
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
// 停止 HandlerThread
handlerThread.quitSafely();
}
@Override
public IBinder onBind(Intent intent) {
// 返回 null,因为这个示例不需要绑定服务
return null;
}
}
创建一个thread处理耗时:
private TestRunning thread = new TestRunning ();
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("ztt","startId="+startId);//startId是唯一的
thread.start();
return START_REDELIVER_INTENT;
}
/**
* 耗时操作
*/
class TestRunning extends Thread{
@Override
public void run() {
//耗时业务处理
}
}
任何activity都可以控制同一个service,而系统也只会创建一个对应service的实例。
Service有两种启动方式:
- startService方式
- bindService方式
onCreate():如果多次执行了Context的startService方法启动Service,Service方法的onCreate方法只会在第一次创建Service的时候调用一次,以后不会调用。通常在这里完成一些初始化操作。
onStartCommand:如果多次执行了Context的startService方法,那么Service的onStartCommand方法也会相应的多次调用。可以在该方法中根据传入的Intent参数进行实际的操作,比如会在此处创建一个线程用于下载数据、播放音乐等。
onBind:service中的onBind方法是个抽象方法,所以Service类是一个抽象类,基于语法规定抽象方法必须要重写,所以即使用不到这个方法,通过startService启动service时,在重写onBind方法时,只需要将其返回值设为null即可。onBind方法主要是用于给BindService方法调用Service时才会用到。
onDestory:service销毁时会调用的。
onStartCommand中常用的返回值有:START_NOT_STICKY、START_SICKY和START_REDELIVER_INTENT,这三个都是静态常理值。
1、START_NOT_STICKY:表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,如果想重新实例化该Service,就必须重新调用startService来启动。
使用场景:表示当Service在执行工作中被中断几次无关紧要或者对Android内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受的话,这是可以在onStartCommand返回值中设置该值。如在Service中定时从服务器中获取最新数据。
2、START_STICKY:表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,这时onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。
使用场景:如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY:比如一个用来播放背景音乐功能的Service就适合返回该值。
3、START_REDELIVER_INTENT:表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。
使用场景:如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。
1.1 startService方式启动service
使用service步骤:
- 定义一个类继承service
- Androidmanifest.xml文件中配置service
- 使用context的startService(Intent)方法启动service
- 不使用时,调用stopService(Intent)方法停止服务
使用start方式启动的生命周期:
onCreate()->onStartCommand()->onDestory()
注意:onCreate方法只会调用一次
服务一旦开启就和调用者没有任何关系了。调用者退出或者挂了,服务还在后台长期的运行,调用者不能调用服务里面的方法。
1.2 bindService方式启动Service
使用Service的步骤:
- 定义一个类继承Service
- 在Androidmanifest.xml文件中注册service
- 使用context的bindService(Intent,ServiceConnection,int)方法启动service
- 不使用时,调用unbindService(ServiceConnection)方法停止该服务。
bind方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可以调用服务里面的方法。
如果调用者和service不在同一个进程中,service在单独的进程中的main线程,是一种跨进程通信方式。
- 在服务的内部创建一个内部类,提供一个方法,可以间接调用服务的方法
- 把暴露的接口文件的扩展名改为.aidl文件,并去掉访问修饰符
- 实现服务的onbind方法,继承Binder和实现aidl定义的接口,提供给外界可调用的方法
- 在activity中绑定服务。bindService()
- 在服务成功绑定的时候会回调onServiceConnected方法传递一个IBinder对象。
- aidl定义的接口.Stub.asInterface(binder)调用接口里面的方法
例如:
定义一个aidl接口:
// IMyAidlInterface.aidl
package com.example.myapp;
interface IMyAidlInterface {
int add(int a, int b);
}
在service中实现这个AIDL接口:
// MyAidlService.java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyAidlService extends Service {
private final IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
AndroidManifest.xml中声明Service
<service
android:name=".MyAidlService"
android:exported="true" />
客户端绑定到Service,获取Binder对象,通过Binder对象和service通信
// MainActivity.java
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapp.IMyAidlInterface;
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface myAidlInterface;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
myAidlInterface = null; // 服务断开
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定Service
Intent intent = new Intent(this, MyAidlService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
// 调用AIDL 方法
private void callAidlMethod() {
if (myAidlInterface != null) {
try {
int result = myAidlInterface.add(1, 2);
// 处理结果
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection); // 解绑Service
}
}
1.3 扩展——IntentService
IntentService 是 Android 中的一种特殊的 Service,用于处理异步请求。它在后台线程中处理请求,因此不会阻塞主线程,适合执行短时的异步任务。
先看Service本身存在两个问题:
- service不会专门启动一条单独的进程,service与它所在应用位于同一个进程中。
- service也不是专门一条新线程,因此不应该在service中直接处理耗时的任务。
IntentService特点:
会创建独立的worker线程来处理所有的Intent请求。
会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题。onHandleIntent运行在IntentService所持有的工作线程中,而非主线程
所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service。
为service的onBinde()提供默认实现,返回null。
为service的onStartCommand提供默认实现,将请求Intent添加到队列中。当多次启动IntentService,产生多个job,IntentService只能一个一个处理,也就是按照先后顺序进行处理.
应用场景:
网络请求: 下载文件、获取服务器数据等。IntentService 可以在后台处理网络操作,避免主线程被阻塞。
数据处理: 处理大数据或复杂计算,例如图像处理、数据解析等。
文件操作: 读取或写入大量数据到文件。
定期任务: 执行调度性任务,比如定期检查更新。
例子:
重写onHandleIntent方法,执行异步任务。
// MyIntentService.java
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 执行耗时操作
Log.d(TAG, "IntentService is running...");
// 模拟耗时任务
try {
Thread.sleep(3000); // 休眠3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "Task completed!");
}
}
AndroidManifest.xml:
<service android:name=".MyIntentService" />
启动IntentService
// MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动 IntentService
Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
}
}
注意:
- 一次性任务: IntentService 适合处理短时间的异步任务。如果任务非常长或需要定期重复,考虑使用JobIntentService 或 WorkManager。
- UI 操作: IntentService 在后台线程运行,不允许直接更新UI。如果需要在任务完成后更新 UI,请使用 Handler 或其他方法将结果传回主线程。
- 任务顺序: 启动的任务会在线程池中排队,按顺序执行,不能并行运行。
2、Activity
2.1 常见生命周期
比较重要的是生命周期,以下是常见场景的生命周期情况:
- 正常运行:onCreate() -> onStart() - > onResume(),activity进入运行状态
- activity被其他activity覆盖其上(DialogActivity)或者锁屏:系统调用onPause()方法,暂停当前Activity的运行。
- 当前activity由被覆盖状态回到前台或者解锁屏:系统调用onResume()方法,再次进入运行状态。
- 当前acitvity转到新的activity界面或按Home键回到主屏,自身退居后台:系统会调用onPause()->onStop()方法,进入停滞状态。
- 用户退回到此activity:系统调用onRestart()->onStart()->onResume()方法,再次进入运行状态。
- 当前activity处于被覆盖状态或者后台不可见状态,参见第2、4条,此时系统内存不足,杀死当前activity,而后用户退回当前activity:再次调用onCreate()->onStart()->onResume()方法,进入运行状态。
- 用户退出当前activity:系统调用onPause()->onStop()->onDestory()方法,结束当前activity。
2.2 异常生命周期
什么是异常生命周期?答:比如当系统资源配置发生改变以及系统内存不足时,activity就可能被杀死。
2.2.1 情况1
资源相关的系统配置发生改变导致activity被杀死并重新创建。
例子:当前activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,activity就会被销毁并且重新创建,也可以阻止系统重新创建activity。
详细解释:
系统配置发生改变之后,activity调用onPause()->onStop()->onDestory()销毁activity。异常情况下终止activity,系统会调用onSaveInstance()来保存当前activity状态,onSaveInstance会在onStop之前调用。当activity重新创建之后,系统会调用onRestoreInstanceState,并且把activity销毁时onSaveInstance方法保存的Bundle对象作为参数同时传递给onRestoreInstance()和onCreate()方法。onRestoreInstance()方法是在onStart()方法之后回调。
相关应用:
可以重写onSaveInstance()方法,保存我们想保存的数据在Bunble,然后在onCreate()或者onRestoreInstance()方法里面恢复数据。
注意:
在onSaveInstanceState和onRestoreInstanceState方法中,系统自动做了一些恢复工作:例如:文本框EditeText中用户输入的数据,ListView滚动的位置等。这些view相关的状态系统都能默认恢复。
2.2.2 情况 2
资源内存不足导致低优先级的activity被杀死。
数据存储和恢复是和情况1是一样的。
从高到底优先级:
- 前台activity:正在和用户交互的activity,优先级最高。
- 可见但非前台activity:例如activity中弹出了一个对话框,导致acticity可见但是位于后台无法和用户直接交互。
- 后台activity:已被暂停的activity,例如执行了onStop()。
2.2.3 怎么阻止系统重新创建activity?
activity指定configChage属性来不让系统重新创建activity.
android : configChanges = "orientation"
2.3 理解
onRestart():表示activity正在重新启动,一般情况下,当前activity从不可见重新变成可见状态时,onRestart()就会被调用,这种情形一般是用户行为所导致的,比如用户按HOME键切换到桌面然后重新打开App或者按back键。
onStart():activity可见了,但是还没有出现在前台,还无法和用户交互。
onPause():表示activity正在停止,此时可以做一些存储数据,停止动画等工作,注意不要做耗时操作,会影响新的activity的显示。onPause()执行完之后,新activity才会执行onResume().
从是否可见来说,onStart()和onStop()是配对的;
从是否在前台来说,onResume()和onPause()是配对的。
旧的activity调用onPause()之后,新activity才会调用onResume()。
后面找时间再整理下Activity和Fragment嵌套时的生命周期。
2.4 activity的启动模式
主要用于Android开发中,定义了Activity的启动行为和如何处理与其他activity之间的什么周期。
-
standard:适用于常规的Activity启动,没有特定限制。
-
singleTop:当需要优化用户体验,避免在已存在的Activity之上重复创建同时又不希望用户离开当前情景时使用。当用户在一个活动中多次点击同一个按钮时,避免重复开启同一个Activity。
-
singleTask:当需要控制Activity Cream-Zaar的行为,确保全局只有一个实例时使用,如浏览器的浏览历史。现类似浏览器的行为,允许用户在返回时跳回到一个已经存在的Activity,而不是创建新的实例
-
singleInstance: 适用于需要确保一个Activity全局唯一并且与任何其他Activity都独立的情况下,如拨号器或联系人应用的主Activity。
2.4.1 standard模式
这种模式下,activity默认会进入启动它的activity所属的任务栈中。适用于大多数情况。
注意:在非activity类型的conetext(比如ApplicationConetxt)并没有所谓的任务栈,所以不能通过ApplicationConext去启动standard模式的activity。
2.4.2 singleTop模式:栈顶复用模式
如果要启动的activity位于任务栈的栈顶的时候,activity不会被重新创建,而是调用**onNewIntent()**方法。
注意:这个activity的onCreate,onStart,onResume方法不会被回调,因为他们并没有发生改变。
2.4.3 singleTask模式:栈内复用模式。
启动的activity在一个独立的栈(任务中),如果此Activity已经存在(栈顶),就只会启动其onNewIntent方法,取代原来的Activity,并清除这个任务栈中该Activity之上的所有Activity。
例如:Activity A,系统首先会寻找A想要的任务栈,如果没有则创建一个新的任务栈,然后把activity A压入栈;如果存在任务栈,然后再看看有没有activity A的实例,如果实例存在,那么就会把A调到栈顶并调用它的onNewIntent()方法,如果不存在则把它压入栈。
2.4.4 singleInstance模式:单实例模式。
与singleTask类似,但是被启动的Activity将会位于一个独立的任务中,且不能和其他Activity共享任务栈。如果任务栈中已存在该Activity,新启动的Intent都会发送到这个已经存在的实例。
2.4.5 在AndroidManifest.xml中设置模式
在 AndroidManifest.xml 中,可以使用 android:launchMode 属性来设置 Activity 的启动模式。具体语法如下:
<activity
android:name=".YourActivity"
android:launchMode="singleTop">
</activity>
2.4.6 在代码中启动activity时设置模式
// 启动StandardActivity
Intent intent = new Intent(this, StandardActivity.class);
startActivity(intent);
// 启动SingleTopActivity
Intent singleTopIntent = new Intent(this, SingleTopActivity.class);
startActivity(singleTopIntent);
// 启动SingleTaskActivity
Intent singleTaskIntent = new Intent(this, SingleTaskActivity.class);
startActivity(singleTaskIntent);
// 启动SingleInstanceActivity
Intent singleInstanceIntent = new Intent(this, SingleInstanceActivity.class);
startActivity(singleInstanceIntent);
在各个Activity中,可以重写onNewIntent方法来处理新的Intent数据,尤其是在使用singleTop、singleTask或者singleInstance模式时:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 处理新的Intent
// 例如更新UI或获取Intent中传递的数据
}
3、BroadcastReceiver
广播分为普通广播和有序广播。
普通广播:是完全异步的,可以在统一时刻被所有接收者接收到,消息传递效率高。缺点:接收者不能将处理结果传递给下一个接收者,且无法终止广播Intent的传播。
有序广播:按照接收者声明的优先级别,声明在intent-filter元素的android:priority属性中,数值越大优先级别越高,取值范围-2147483648(最低优先级)到 2147483647(最高优先级),也可以调用IntentFilter对象的setPriority()进行设置,被接收者依次接收广播。
如:A的优先级高于B,B的优先级高于C,那么,广播先传给A,再传给B,最后传给C.A得到广播后,可以往广播里存入数据,当广播传给B时,B可以从广播中得到A存入的数据。
步骤:
- 自定义一个类继承BroadcastReceiver
- 重写onReceive方法
- 在AndroidManifest.xml中注册或者代码中动态注册广播
注:xml中注册的优先级高于动态注册广播。
普通广播例子:
1、创建和注册广播接收器
// MyReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "普通广播接收到消息: " + intent.getStringExtra("data"));
}
}
2、AndroidManifest.xml中注册接收器
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.example.NORMAL_BROADCAST"/>
</intent-filter>
</receiver>
3、发送普通广播
// MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 发送普通广播
findViewById(R.id.send_normal_broadcast).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.NORMAL_BROADCAST");
intent.putExtra("data", "Hello, Normal Broadcast!");
sendBroadcast(intent);
}
});
}
}
发送有序广播例子:
1、创建和注册有序广播接收器
// MyOrderedReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MyOrderedReceiver extends BroadcastReceiver {
private static final String TAG = "MyOrderedReceiver";
@Override
public void onReceive(Context context, Intent intent) {
// 处理接收到的广播
Log.d(TAG, "有序广播接收到消息: " + intent.getStringExtra("data"));
// 继续传播广播(可选)
// or 修改广播数据
// setResultData("New Data");
}
}
2、在AndroidManifest.xml中注册有序广播接收器
<receiver android:name=".MyOrderedReceiver" android:exported="true">
<intent-filter>
<action android:name="com.example.ORDERED_BROADCAST"/>
</intent-filter>
</receiver>
3、发送有序广播
// MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 发送有序广播
findViewById(R.id.send_ordered_broadcast).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.ORDERED_BROADCAST");
intent.putExtra("data", "Hello, Ordered Broadcast!");
// 发送有序广播
sendOrderedBroadcast(intent, null);
}
});
}
}
广播的生命周期:如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。
注意:
如果需要在onReceiver完成一些耗时操作,应该通过发送Intent 给 Service,在Service中开启一个新线程处理耗时操作,不应该在BroadcastReceiver中开启一个新的线程,因为BroadcastReceiver生命周期很短,在执行完onReceiver以后就结束,如果开启一个新的线程,可能出现BroadcastRecevier退出以后线程还在,而如果BroadcastReceiver所在的进程结束了,该线程就会被标记为一个空线程,根据Android的内存管理策略,在系统内存紧张的时候,会按照优先级,结束优先级低的线程,而空线程无异是优先级最低的,这样就可能导致BroadcastReceiver启动的子线程不能执行完成。
3.1 静态注册广播和动态注册
动态注册步骤:
1、创建一个广播接收器类,继承BroadcastReceiver.
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 处理接收到的广播
String message = intent.getStringExtra("message");
Log.d("MyBroadcastReceiver", "Received message: " + message);
}
}
2、在activity或者service中注册接收器,registerReceiver()方法
public class MyActivity extends AppCompatActivity {
private MyBroadcastReceiver myReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
myReceiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter("com.example.SOME_ACTION");
registerReceiver(myReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver); // 注销接收器
}
}
3、发送广播
Intent intent = new Intent("com.example.SOME_ACTION");
intent.putExtra("message", "Hello, Broadcast!");
sendBroadcast(intent);
静态注册和动态注册区别:
动态注册广播不是常驻型广播,动态广播跟随activity的生命周期。注意: 在activity结束前,移除广播接收器,防止造成内存泄漏。
静态注册是常驻型,也就是说当应用程序关闭后,如果有广播来,程序也会被系统调用自动运行。
当广播为有序广播时:
1、优先级高的先接收
2、同优先级的广播接收器,动态优先于静态
3、同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
当广播为普通广播时:
1、无视优先级,动态广播接收器优先于静态广播接收器
2、同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
4、ContentProvider
ContentProvider 它主要的作用就是将程序的内部的数据和外部进行共享,为数据提供外部访问接口,被访问的数据主要以数据库的形式存在,而且还可以选择共享哪一部分的数据。这样一来,对于程序当中的隐私数据可以不共享,从而更加安全。contentprovider是android中一种跨程序共享数据的重要组件。
4.1 使用系统的ContentProvider
系统的ContentProvider有很多,如通话记录,短信,通讯录等等,都需要和第三方的app进行共享数据。既然是使用系统的,其具体实现我们不需要过多关注。使用步骤如下:
- 获取ContentResolver实例。
- 确定URL的内容,并解析为具体的URL实例。
- 通过ContentResolver实例来调用相应的方法,传递相应的参数,但是第一个参数总是URL,它制定了我们要操作的数据的具体地址。
可以通过读取系统通讯录的联系人信息,显示在Listview中来实践这些知识。不要忘记在读取通讯录的时候,在清单文件中要加入相应的读取权限。
4.2 自定义ContentProvider
1、创建ContentProvider,继承自ContentProvider:
public class BooksProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.booksprovider";
private static final String PATH_BOOKS = "books";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH_BOOKS);
private SQLiteDatabase database;
// 数据库和表的相关常量
private static final String TABLE_BOOKS = "books";
private static final String DATABASE_NAME = "books.db";
private static final int DATABASE_VERSION = 1;
// SQLiteOpenHelper 类
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + TABLE_BOOKS + " (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT)";
db.execSQL(createTable);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKS);
onCreate(db);
}
}
@Override
public boolean onCreate() {
DatabaseHelper helper = new DatabaseHelper(getContext());
database = helper.getWritableDatabase();
return database != null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return database.query(TABLE_BOOKS, projection, selection, selectionArgs, null, null, sortOrder);
}
@Override
public Uri insert(Uri uri, ContentValues values) {
long id = database.insert(TABLE_BOOKS, null, values);
return ContentUris.withAppendedId(CONTENT_URI, id);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return database.update(TABLE_BOOKS, values, selection, selectionArgs);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return database.delete(TABLE_BOOKS, selection, selectionArgs);
}
@Override
public String getType(Uri uri) {
return "vnd.android.cursor.dir/books";
}
}
2、AndroidManifest.xml注册
<provider
android:name=".BooksProvider"
android:authorities="com.example.booksprovider"
android:exported="true" />
3、使用
ContentValues values = new ContentValues();
values.put("title", "1984");
values.put("author", "George Orwell");
// 插入数据
Uri newBookUri = getContentResolver().insert(BooksProvider.CONTENT_URI, values);
// 查询数据
Cursor cursor = getContentResolver().query(BooksProvider.CONTENT_URI, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String title = cursor.getString(cursor.getColumnIndex("title"));
String author = cursor.getString(cursor.getColumnIndex("author"));
Log.d("BooksProvider", "Book: " + title + " by " + author);
}
cursor.close();
}