Android基础之四大组件剖析及使用

本文深入讲解Android四大组件:Activity、Service、BroadcastReceiver和ContentProvider的功能、使用方法及生命周期,涵盖启动模式、通信机制、数据共享等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要摘录自《第一行代码》、《Android开发艺术探索》。
学习 Android 都是以四大组件开始的,四大组件分别是活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)、内容提供器(Content Provider)。

  • 活动(Activity):应用程序的页面,主要用于与用户交互;
  • 服务(Service):没有UI页面,主要用于在后台执行耗时、不需要与用户交互的操作;
  • 广播接收器(Broadcast Receiver):用于应用接收外部消息,如网络连接广播等;
  • 内容提供器(Content Provider):主要用于应用程序之间数据共享;

一、Activity

Activity 是可以包含用户界面的组件,主要用于和用户进行交互。

1.1 Activity 的生命周期

  • onCreate:表示窗口正在被创建,比如加载layout布局文件啊(setContentView)。 所以我们可以在这个方法中,做一些初始化的操作。
  • onStart:表示Activity正在被启动,即将开始,此时的窗口已经可见了,但是还没有出现在前台,所以无法和用户进行交互。也就是说此时的窗口正处在 不可见—>可见 的过程中。
  • onRestart:表示窗口正在重新启动。在什么场景下会调用这个呢?比如:从A页面进入B页面,然后点击BACK键(或者自己的返回上一页的按钮)回到A页面,那么就会调用A页面的onRestart方法了。再比如:点击HOME键回到桌面,然后通过点击任务栏或者点击应用图标再次进入A页面,都可以触发调用这个方法
  • onResume:表示此时的窗口已经可见了,显示在前台并且进行活动了,我们也可以与窗口进行交互了。
  • onPause:表示窗口正在停止,这是我们可以做一些存储数据、或者停止动画等一些不太耗时的操作,因为会影响到下一个Activity的显示。onPause执行完成之后,新的Activity的onResume才会执行。
  • onStop:表示窗口即将停止,此时,可以做一些稍微重量级的回收工作,但是也不能太耗时。
  • onDestroy:表示窗口即将被销毁。这是Activity生命周期中的最后一步了。这里,我们可以做一些回收工作和最终的资源释放工作。
    参考 Activity的生命周期_dandan的专栏-优快云博客_activity的生命周期
    https://blog.youkuaiyun.com/fdd11119/article/details/80682390

1.2 使用 Activity

1.2.1 新建工程默认生成

新建的工程都会默认生成一个 MainActivity

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

1.2.2 自己创建一个 Activity

注册到 manifest ,包名要和路径对应上,这里我是在 activity 包下创建的。

<activity android:name=".activity.SecondActivity"></activity>

新建 SecondActivity,重写 onCreate 方法,加载自己的布局文件 activity_second.xml

public class SecondActivity extends AppCompatActivity {

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

1.3 启动 Activity 的方法

1.3.1 同一应用内启动,依据类名启动:

startActivity(new Intent(MainActivity.this, SecondActivity.class));

1.3.2 同一应用内,依据 action 启动:

SecondActivity 配置如下:

<activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.test.intent.action.SECOND"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

启动方法如下:

Intent secondIntent = new Intent("com.test.intent.action.SECOND");
startActivity(secondIntent);

1.3.3 不同应用,依据 action 启动

不用应用之间也可以使用 action 方式启动,不过需要事先知道目标应用的 action 。
比如,打开原生设置的 wifi 页面,

startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));

1.3.4 不同应用,A 应用启动 B 应用的首页面(需要知道 B 应用的包名):

Intent packageIntent = getPackageManager().getLaunchIntentForPackage("packageName");
startActivity(packageIntent);

1.3.5 不同应用,A 应用启动 B 应用的特定页面(需要知道 B 应用的包名、页面的类名):

Intent intent1= new Intent();
intent1.setComponent(new ComponentName("android.systemupdate.service",
		"android.systemupdate.activitys.SettingActivity"));
startActivity(intent1);

1.3.6 启动应用的小技巧

如果需要启动的应用不存在就会报错或者闪退,
建议在使用 Intent 的时候,添加 非空判断 和 Intent.resolveActivity判断。

Intent secondIntent = new Intent("com.test.intent.action.SECOND");
if (secondIntent  != null 
		&& secondIntent.resolveActivity(getPackageManager()) != null) {
	startActivity(secondIntent);
}

1.4 Activity 的启动模式

1.4.1 启动模式简单介绍

standard: 标准模式,一调用 startActivity() 方法就会产生一个新的实例。

singleTop: 来了intent , 每次都创建新的实例,仅一个例外:当栈顶的activity 恰恰就是该activity 的实例(即需要创建的实例)时,不再创建新实例。这解决了栈顶复用问题。

singleTask: 来了 intent 后,检查栈中是否存在该 activity 的实例,如果存在就把 intent 发送给它,否则就创建一个新的该activity的实例,放入一个新的 task 栈的栈底。肯定位于一个 task 的栈底,而且栈中只能有它一个该 activity 实例,但允许其他activity加入该栈。解决了在一个 task 中共享一个activity。

singleInstance: 这个跟 singleTask 基本上是一样,只有一个区别:在这个模式下的 Activity实例所处的 task 中,只能有这个 activity 实例,不能有其他的实例。一旦该模式的activity的实例已经存在于某个栈中,任何应用在激活该 activity 时都会重用该栈中的实例,解决了多个 task 共享一个 activity 。

1.4.2 Activity 的 flag

Activity 的 flag 有很多,有的可以设定启动模式,有的可以影响 Activity 的运行状态。
FLAG_ACTIVITY_NEW_TASK:指定为 singleTask 启动模式,效果和在 xml 中指定该启动模式相同;

FLAG_ACTIVITY_SINGLE_TOP:指定为 singleTop 启动模式,效果和在 xml 中指定该启动模式相同;

FLAG_ACTIVITY_CLEAR_TOP:启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。这个标记一版会和 singleTask 一起出现,在这种情况下,被启动的 Activity 如果已有实例存在,会回调其 onNewIntent 方法。被启动的 Activity 如果采用 standard 模式启动,那么连同它之上的 Activity 都要出栈,系统会创建新的实例并放入栈顶。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:添加这个标记的 Activity 不会出现在历史任务列表中。等同于在 xml 中设置 android:excludeFromRecents="true"

从 Service 里启动 Activity ,需要设置 Intent.FLAG_ACTIVITY_NEW_TASK

参考 Activity的启动方式和flag详解_singwhatiwanna-优快云博客
https://blog.youkuaiyun.com/singwhatiwanna/article/details/9294285

1.4.3 配置启动模式

方法一:通过注册文件配置

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

方法二:通过 Intent 设置

Intent secondIntent = new Intent("com.test.intent.action.SECOND");
secondIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //
if (secondIntent.resolveActivity(getPackageManager()) != null) {
	startActivity(secondIntent);
}

方法二的优先级高于方法一。
限定范围不同,
方法一无法直接设置 FLAG_ACTIVITY_CLEAR_TOP 标识;
方法二无法设置为 singleInstance ;

1.5. Activity 之间传递数据

1.5.1 使用 intent.putExtra

发送方:

Intent testIntent = new Intent(MainActivity.this, TestActivity.class);
testIntent.putExtra("testIntentFrom", MainActivity.class.getSimpleName());
startActivity(testIntent);

接收方:

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

        Intent from = getIntent();
        if (from != null) {
            String date = from.getStringExtra("testIntentFrom");
            if (date != null) {
                // deal with data
            }
        }
    }

1.5.2 使用 Bundle

Bundle 方式和 Intent 的区别:
Intent 方式主要传递基本数据类型;
Bundle 方式既可以传递基本数据类型,也可以传递复杂的数据,如序列化对象、数组等。

发送方:

Intent bIntent = new Intent(MainActivity.this, TestActivity.class);
Bundle bundle = new Bundle();
bundle.putString("testBundle", MainActivity.class.getSimpleName() + "bundle");
bIntent.putExtras(bundle);
startActivity(bIntent);

接收方

Intent from = getIntent();
Bundle tBundle = from.getExtras();
if (tBundle != null) {
	String bundleDate = tBundle.getString("testBundle");
	if (bundleDate != null) {
		// deal with bundleDate 
	}
}

1.5.3 使用 Serializable

先将需要传递的对象通过 Serializable 实现序列化。
Person 对象实现 Serializable 接口

public class Person implements Serializable {

    int id ;
    String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

发送方

Person person = new Person(20, "luo");

Intent bIntent = new Intent(MainActivity.this, TestActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("keyPerson", (Serializable) person);
bIntent.putExtras(bundle);
startActivity(bIntent);

接收方

Intent from = getIntent();
Bundle tBundle = from.getExtras();
if (tBundle != null) {
	Person person = (Person) tBundle.getSerializable("keyPerson");
	if (person != null) {
		int id = person.getId();
		String name = person.getName());
    }
}

1.5.4 使用 Parcelable

先将需要传递的对象通过 Parcelable 实现序列化。
NewPerson 对象实现 Parcelable 接口

public class NewPerson implements Parcelable {

    int id;
    String name;

    public NewPerson() {
        super();
    }

    public NewPerson(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(id);
        parcel.writeString(name);
    }

    public static final Parcelable.Creator<NewPerson> CREATOR = new Parcelable.Creator<NewPerson>(){
        @Override
        public NewPerson createFromParcel(Parcel parcel) {
            NewPerson newPerson = new NewPerson();
            newPerson.id = parcel.readInt();
            newPerson.name = parcel.readString();
            return newPerson;
        }

        @Override
        public NewPerson[] newArray(int i) {
            return new NewPerson[i];
        }
    };
}

发送方

NewPerson newPerson = new NewPerson(30, "luoluo");

Intent bIntent = new Intent(MainActivity.this, TestActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelable("keyNewPerson", newPerson);
bIntent.putExtras(bundle);
startActivity(bIntent);

接收方

Intent from = getIntent();
Bundle tBundle = from.getExtras();
NewPerson nPerson = (NewPerson) tBundle.getParcelable("keyNewPerson");
if (nPerson != null) {
	int id = nPerson.getId() ;
	String name = nPerson.getName();
}

1.5.5 传递数据与回传

发送方,使用 startActivityForResult 方法,并重写 onActivityResult 方法。

Intent testIntent = new Intent(MainActivity.this, TestActivity.class);
testIntent.putExtra("testIntent", MainActivity.class.getSimpleName());
startActivityForResult(testIntent, 101);

	@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 101) {
            if (resultCode == 100) {
                int rValue = data.getIntExtra("key_return", 0);
                Log.d("luoah", "[MainActivity.java] -- onActivityResult -- rValue:" + rValue);
            }
        }
    }

接收方(接收后再回传),使用 setResult 方法回传数据

Intent from = getIntent();
if (from != null) {
	if (from.hasExtra("testIntent")) {
		String date = from.getStringExtra("testIntent");
		if (date != null) {
        	// deal with data
         }
    }

	Intent returnIntent = new Intent();
    returnIntent.putExtra("key_return", 99);
    setResult(100, returnIntent);
}

二、Service

2.1 服务的定义:

服务一般没有用户界面,主要用于执行不需要与用户交互但是需要一直执行的任务,如下载等。
必要的时候,服务可以通过 NotificationWindowManager 等方式显示UI 。

2.2 Service 基本用法

2.2.1 创建一个Service

Servie 生命周期为 onCreate(创建) – onStartCommand(开始执行) – onDestroy(结束)。
服务未创建的话会先执行 onCreate 方法再执行 onStartCommand
如果服务已经创建了,再 startService 不会执行 onCreate 方法,但会执行 onStartCommand 方法

public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

2.2.2 添加到注册文件

android:exported="true" 是声明可以被其他进程调起。

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

2.2.3 在 Activity 里启动

Intent sIntent = new Intent(MainActivity.this, MyService.class);
startService(sIntent);

2.2.4 停止服务

Intent stopIntent = new Intent(MainActivity.this, MyService.class);
stopService(sIntent);

2.3 Activity 和 Service 通信

在 2.2.1 、2.2.2 基础上修改,
创建一个 DownloadBinder ,修改 IBinder 方法返回 DownloadBinder 。
DownloadBinder 就是 Activity 与 Service 通信的桥梁。

public class MyService extends Service {

    public class DownloadBinder extends Binder{

        public void startDownload(){
        
        }

        public int getProgress(){
            return 30;
        }
    }

    private DownloadBinder mBinder = new DownloadBinder();

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

Activity 里的启动方法修改如下,与之前的差别是创建了 ServiceConnection 来保持 Activity 和 Service 的连接, 只要这个连接不断开,服务就是一直运行的。

private MyService.DownloadBinder mBinder;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mBinder = (MyService.DownloadBinder) iBinder;
            mBinder.startDownload();
            int progress = mBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    
Intent bIntent = new Intent(MainActivity.this, MyService.class);
bindService(bIntent, mServiceConnection, BIND_AUTO_CREATE);

停止方法为

unbindService(mServiceConnection);

或者在 Service 的合适时机调用 stopSelf();

2.4 前台服务

前台服务会在系统状态栏显示。后台服务一般没有UI显示。
系统在内存不足时,会优先回收与用户没交互的后台服务,前台服务会在通知栏显示,有UI交互,所以使用前台服务不容易被回收。

2.4.1 注册到 manifest

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

2.4.2 添加前台通知

需要注意的是,8.0 之后使用通知 Notification ,需要设置 NotificationChannel ,否则无法正常显示。

public class ForService extends Service {

    private static final int NOTIFICATION_ID_23 = 23;
    private static final int NOTIFICATION_ID_24 = 24;
    private static final String CHANNEL_NAME = "channel_name";
    private static final String CHANNEL_ID = "channel_id";

    @Override
    public void onCreate() {
        super.onCreate();
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            manager.createNotificationChannel(channel);

            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("This is Title")
                    .setContentText("This is ContentText")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setWhen(System.currentTimeMillis())
                    .setContentIntent(pendingIntent);
            startForeground(NOTIFICATION_ID_23, builder.build());
        } else {
            Notification notification = new NotificationCompat.Builder(this, "channelId")
                    .setContentTitle("This is Title")
                    .setContentText("This is ContentText")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setWhen(System.currentTimeMillis())
                    .setContentIntent(pendingIntent)
                    .build();
            startForeground(NOTIFICATION_ID_24, notification);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

2.4.3 启动

使用 startService 方法启动即可。

Intent sIntent = new Intent(MainActivity.this, ForService.class);
startService(sIntent);

2.4.4 停止

在合适的时机调用 stopService 或者直接在 Service 里执行 stopSelf()

2.5 IntentService

IntentService 的使用方法和后台Service一样。
他们的区别是:IntentService 在执行完之后会自动销毁,不用在外部组件中调用 stopService 方法来销毁。

2.5.1 创建 MyIntentService

继承 IntentService,并重写其中的 onHandleIntent(Intent intent) 方法,具体的操作在这里实现。调用方可以通过 Intent 将参数传递过来。

public class MyIntentService extends IntentService {

	private static final String MIS_TAG = MyIntentService.class.getSimpleName();
	
    public MyIntentService() {
        super(MIS_TAG);
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super(name);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
    	//
    }
}

2.5.2 注册到 manifest

和后台 Service 注册方法一样。

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

2.5.3 调用

启动方式和后台Service一样

Intent isIntent = new Intent(MainActivity.this, MyIntentService.class);
startService(isIntent);

2.5.4 源码简单分析

为什么 IntentService 不用外部组件调用 stopService 方法来销毁。看源码就知道了。
IntentService 初始化的时候创建了 Handler ,Handler 执行完任务后调用了 stopSelf 来销毁服务。
生命周期为 onCreateonStartCommandonStartonHandleIntentonDestroy

public abstract class IntentService extends Service {
	
	private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;

	private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper); 
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj); // 3
            stopSelf(msg.arg1); // 4
        }
    }
	
	@Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);//
    }
	
	@Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg); //2
    }

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId); //1
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    
    protected abstract void onHandleIntent(@Nullable Intent intent);
    
	//省略其余代码
}

2.6 Service 保活

安卓版本更新之后,出于节约内存、省电等因素考虑, Service 保活越来越难。
当系统内存特别低的时候,各种保活手段都会失效的。
下述方法只是做个简单介绍,提供一些思路,不一定能实现。

2.6.1 系统级 Service 保活

我们系统级Service 默认保活,简单粗暴,不会轻易被查杀。
但如果系统内存很低很低了,还是会被 kill 掉的。

2.6.2 使用 android:persistent=“true” 保活(不推荐)

我们系统级 Service 如果想在 2.6.1 的基础上更好的保活,可以在 application 标签里配置 android:persistent="true"

<application
    android:persistent="true|false">
</application>

默认 false ,不要轻易设置为 true!!!
这里只是扩展介绍下,不推荐此做法,因为这个属性会导致应用无法进行版本升级

系统一般只会在一个 Service 里这样设置,这个 Service 集中处理各种操作,不会每个系统级 Service 都这样设置。

配置了 android:persistent=“true” 的应用,

  • 启动会很早,比主页启动得还早;
    分析下源码或者打 log 就知道了。
    参考 AN 8.0 源码,在frameworks/base/servicescore/java/com/android/server/am/ActivityManagerService.javasystemReady 函数里,对应注释 1 、2 处。
public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
	//省略其他
	synchronized (this) {
            // Only start up encryption-aware persistent apps; once user is
            // unlocked we'll come back around and start unaware apps
            startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE); // 1

            // Start up initial activity.
            mBooting = true;
            // Enable home activity for system user, so that the system can always boot. We don't
            // do this when the system user is not setup since the setup wizard should be the one
            // to handle home activity in this case.
            if (UserManager.isSplitSystemUser() &&
                    Settings.Secure.getInt(mContext.getContentResolver(),
                         Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
                ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
                try {
                    AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
                            UserHandle.USER_SYSTEM);
                } catch (RemoteException e) {
                    throw e.rethrowAsRuntimeException();
                }
            }
            startHomeActivityLocked(currentUserId, "systemReady"); // 2
			//省略其他
     }
}
  • 被 kill 掉之后会重启;
    这就要求代码写得很规范、健壮,防止 Service 循环重启报错,导致系统死机。

参考 谈谈Android中的persistent属性_张许平的博客-优快云博客_android:persistent
https://blog.youkuaiyun.com/salmon_zhang/article/details/90741912

2.6.3 提高 Service 优先级

在 AndroidManifest.xml 中通过 android:priority = “1000” 这个属性来提高 Service 优先级;
使用前台服务,前台服务有UI交互,不容易被系统查杀。

Android 中将进程的等级,由高到低分别是:前台进程、可视进程、次要服务进程、后台进程、内容供应节点以及空进程。当系统进程空间紧张时,会按照优先级自动进行进程回收。
具体可参考 ActivityManager.RunningAppProcessInfo 相关说明。

/** @hide */
@IntDef(prefix = { "IMPORTANCE_" }, value = {
            IMPORTANCE_FOREGROUND,       //正在与用户交互的app
            IMPORTANCE_FOREGROUND_SERVICE,    //前台服务,如播放音乐
            IMPORTANCE_TOP_SLEEPING,
            IMPORTANCE_VISIBLE,          //可见进程,但不一定与用户交互,如走了 onPause 但未走 onStop
            IMPORTANCE_PERCEPTIBLE,
            IMPORTANCE_CANT_SAVE_STATE,
            IMPORTANCE_SERVICE,      // 后台服务
            IMPORTANCE_CACHED, 
            IMPORTANCE_GONE,
})

2.6.4 双进程守护

创建 2 个 Service ,A Service 挂掉的时候启动 B Service 。
参考 Android 的保活的两种解决方案 - 心夢無痕 - 博客园
https://www.cnblogs.com/xinmengwuheng/p/7070113.html

三、BroadcastReceiver

广播分为两种:标准广播 和 有序广播。
标准广播:异步执行的广播,广播发出后,所有的广播接收器几乎在同一时刻接收到广播,没有先后顺序,是无法截断的。
有序广播:同步执行的广播,广播发出后同一时刻只有一个广播接收器收到广播消息,当这个广播接收器处理完之后才会继续传播。这种情况下,广播接收器是有先后顺序的,优先级高的优先接收到广播,并且可以截断广播;广播被截断后,优先级低的就接收不到了。

3.1 静态注册

需要注册到 manifest ,当收到对应的 action 就会启动了。安卓 7.0 之后对静态注册的广播做了限制,静态注册方式不一定能实现。

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

<receiver android:name=".receiver.MyReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
            <intent-filter android:priority="100">
                <action android:name="com.test.action.START_RECEIVER"/>
            </intent-filter>
        </receiver>

android:priority="100" 是设置广播接收器的优先级,取值 -1000 ~ 1000 ,优先级越高越早接收到广播。
接收某些系统广播需要声明权限,某些广播只有系统应用才可以接收。
本例里,接收开机广播 android.intent.action.BOOT_COMPLETED
需声明权限 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

MyReceiver 类实体,一般在接收到广播后执行启动 Activity 或者 Service 的操作,不能执行耗时的操作,否则会报错。

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
    
    }
}

3.2 动态注册

动态注册的广播接收器不用注册到 manifest 。
自定义一个广播接收器,然后添加过滤器,注册与反注册。
切记注册与反注册要配套出现,否则会内存泄漏。

public class ReceiverActivity extends AppCompatActivity {

    private DynamicReceiver mMyReceiver;
    static class DynamicReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
        	
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_receiver);
        
        mReceiver = new DynamicReceiver();
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction("com.test.action.BROADCAST");
        registerReceiver(mReceiver, mFilter);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }
    
    public void onSendBroadcast(View view){
        Intent bIntent = new Intent("com.test.action.BROADCAST");
        sendBroadcast(bIntent);

        Intent oIntent= new Intent("com.test.action.START_RECEIVER");
        sendOrderedBroadcast(oIntent, null);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mReceiver!= null) {
            unregisterReceiver(mReceiver);
        }
    }
}

这里我还发送了 com.test.action.START_RECEIVER 广播测试,低版本(6.0)模拟器是可以启动 MyReceiver 的,高版本模拟器(8.0)不行。

提一下,public void onSendBroadcast(View view){ } 是我在布局文件里添加的 button 点击事件。

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

    <Button
        android:id="@+id/btn_broadcast"
        android:text="send broadcast"
        android:onClick="onSendBroadcast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

3.3 发送广播

参考上述的例子,
发送标准广播:

Intent bIntent = new Intent("com.test.action.BROADCAST");
sendBroadcast(bIntent);

如果发送标准广播,又希望只有特定的应用可以接收,可以这样设置:

Intent bIntent = new Intent("com.test.action.BROADCAST");
bIntent.setPackage("xxxxx"); // 设置包名
sendBroadcast(bIntent);

发送有序广播:

Intent oIntent = new Intent("com.test.action.START_RECEIVER");
sendOrderedBroadcast(oIntent, null);

3.4 本地广播

本地广播是出于安全性考虑,本地广播只能在应用内部传递,广播接收器也只能接受本程序内部发出的广播。

  • 可以明确知道发送的广播不会离开我们的程序,不必担心数据泄露;
  • 其他程序无法将广播发送到我们应用内部,不必担心外来隐患;
  • 发送本地广播比全局广播更高效。

使用方法和普通广播使用方法基本一样,差异在于要使用 LocalBroadcastManager ,参考注释即可。

public class LocalRecActivity extends AppCompatActivity {

    private LocalReceiver mLocalReceiver;
    static class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
        
        }
    }

    private LocalBroadcastManager mManager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_local_rec);
        mManager = LocalBroadcastManager.getInstance(this); // 1

        mLocalReceiver = new LocalReceiver();
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction("com.test.action.LOCAL_BROADCAST");
        mManager.registerReceiver(mLocalReceiver, mFilter); // 2
    }

    public void onLocalSend(View view){
        Intent lIntent = new Intent("com.test.action.LOCAL_BROADCAST");
        mManager.sendBroadcast(lIntent);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mLocalReceiver != null) {
            mManager.unregisterReceiver(mLocalReceiver); // 3
        
    }
}

tip :LocalBroadcastManager 直接打没有代码提示,要引入依赖包。

import androidx.localbroadcastmanager.content.LocalBroadcastManager;
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

3.5 看下源码log

先将系统广播 log 开关打开(有源码,任性改 :D)

--- a/frameworks/base/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/frameworks/base/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -39,7 +39,7 @@ class ActivityManagerDebugConfig {
     static final String TAG_AM = "ActivityManager";
 
     // Enable all debug log categories.
-    static final boolean DEBUG_ALL = false;
+    static final boolean DEBUG_ALL = true;
 
     // Enable all debug log categories for activities.
     static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;

发送一个自定义的标准广播 com.test.action.MY_ACTION ,这个广播的 log 如下

V ActivityManager: Broadcast: Intent { act=com.test.action.MY_ACTION flg=0x10 } ordered=false userid=0
V ActivityManager: Enqueueing broadcast: com.test.action.MY_ACTION replacePending=false
I ActivityManager: Broadcast intent Intent { act=com.test.action.MY_ACTION flg=0x10 } on background queue
V ActivityManager: Enqueueing parallel broadcast BroadcastRecord{ebf4305 u0 com.test.action.MY_ACTION}
V BroadcastQueue: Processing parallel broadcast [background] BroadcastRecord{ebf4305 u0 com.test.action.MY_ACTION}
V BroadcastQueue: Delivering non-ordered on [background] to registered BroadcastFilter{7553187 u0 ReceiverList{21a1dc6 5717 com.test.testviomikey/10048/u0 remote:23027a1}}: BroadcastRecord{ebf4305 u0 com.test.action.MY_ACTION}
I BroadcastQueue: Delivering to BroadcastFilter{7553187 u0 ReceiverList{21a1dc6 5717 com.test.testviomikey/10048/u0 remote:23027a1}} : BroadcastRecord{ebf4305 u0 com.test.action.MY_ACTION}
V BroadcastQueue: Done with parallel broadcast [background] BroadcastRecord{ebf4305 u0 com.test.action.MY_ACTION}
D luoah   : [MainActivity.java] -- onReceive -- intent.getAction():com.test.action.MY_ACTION

可以看到,发送广播涉及 ActivityManager 、BroadcastQueue , mark 一下,暂不分析。
如果需要拦截某些广播,可以考虑在 BroadcastQueue 里添加过滤条件。

四、ContentProvider

4.1 定义

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

4.2 常规使用(CRUD)

4.2.1 自定义一个 ContentProvider

注册到 manifest ,其中 com.test.myprovider 为 MyProvider的 “标识符” (说法不一定准确,见谅),对 MyProvider 的操作都涉及到这个。

<provider
            android:name=".provider.MyProvider"
            android:authorities="com.test.myprovider"
            android:enabled="true"
            android:exported="true"/>

具体实现

public class MyProvider extends ContentProvider {

    private static final String AUTHORITY = "com.test.myprovider";
    private static final int TABLE_CODE_1 = 1;
    private static final int TABLE_CODE_2 = 2;

    private static UriMatcher uriMatcher;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, TABLE_NAME_BOOK, TABLE_CODE_1);
        uriMatcher.addURI(AUTHORITY, TABLE_NAME_DRAWING, TABLE_CODE_2);
    }
    private SQLiteOpenHelper helper;
    @Override
    public boolean onCreate() {
        Log.d("luoah", "[MyProvider.java] -- onCreate -- ");
        helper = new MyDbHelper(getContext(), "BookData.db", null, 1);
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        SQLiteDatabase database = helper.getReadableDatabase();
        Cursor cursor = null;
        switch (uriMatcher.match(uri)){
            case TABLE_CODE_1:
                cursor = database.query(TABLE_NAME_BOOK, projection, selection, selectionArgs,null,null, sortOrder);
                break;
            case TABLE_CODE_2:
                cursor = database.query(TABLE_NAME_DRAWING, projection, selection, selectionArgs,null,null, sortOrder);
                break;
            default:break;
        }

        return cursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        SQLiteDatabase db = helper.getReadableDatabase();
        Uri returnUri = null;
        switch (uriMatcher.match(uri)){
            case TABLE_CODE_1:
                long newBookId = db.insert(TABLE_NAME_BOOK, null, values);
                returnUri = Uri.parse("content://" + AUTHORITY + "/table_book/" + newBookId);
                break;
            case TABLE_CODE_2:
                long newDrawingId = db.insert(TABLE_NAME_DRAWING, null, values);
                returnUri = Uri.parse("content://" + AUTHORITY + "/table_drawing/" + newDrawingId);
                break;
            default:break;
        }
        return returnUri;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        SQLiteDatabase db = helper.getWritableDatabase();
        int deletedRows = 0;
        switch (uriMatcher.match(uri)) {
            case TABLE_CODE_1:
                deletedRows = db.delete(TABLE_NAME_BOOK, selection, selectionArgs);
                break;
            case TABLE_CODE_2:
                deletedRows = db.delete(TABLE_NAME_DRAWING, selection, selectionArgs);
                break;
            default:
                break;
        }
        return deletedRows;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        SQLiteDatabase db = helper.getReadableDatabase();
        int updatedRows = 0;
        switch (uriMatcher.match(uri)) {
            case TABLE_CODE_1:
                updatedRows = db.update(TABLE_NAME_BOOK, values, selection, selectionArgs);
                break;
            case TABLE_CODE_2:
                updatedRows = db.update(TABLE_NAME_DRAWING, values, selection, selectionArgs);
                break;
            default:
                break;
        }
        return updatedRows;
    }
}

4.2.2 自定义一个 SQLiteOpenHelper

public class MyDbHelper extends SQLiteOpenHelper {

    protected static final String TABLE_NAME_BOOK = "table_book";
    protected static final String TABLE_NAME_DRAWING = "table_drawing";

    public static final String CREATE_BOOK = "create table " + TABLE_NAME_BOOK + " ("
            + "id integer PRIMARY KEY, "
            + "name text, "
            + "price real, "
            + "pages integer, "
            + "author text)";

    public static final String CREATE_DRAWING = "create table " + TABLE_NAME_DRAWING + " ("
            + "id integer primary key autoincrement, "
            + "author text, "
            + "price real, "
            + "name text)";

    private Context mContext;

    public MyDbHelper(Context context, String name,
                            SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d("luoah", "[MyDbHelper.java] -- onCreate -- ");
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_DRAWING);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.d("luoah", "[MyDbHelper.java] -- onUpgrade -- ");
        db.execSQL("drop table if exists " + TABLE_NAME_BOOK);
        db.execSQL("drop table if exists " + TABLE_NAME_DRAWING);
        onCreate(db);
    }
}

4.2.3 增删改查操作

MyProvider 对应的Uri

Uri commonUri = Uri.parse("content://com.test.myprovider/table_book");
  • 新增
    新增的数据需要与 MyDbHelper 里创建的表的数据格式对应上。
ContentValues values = new ContentValues();
values.put("id", 1);
values.put("name", "JamesAutobiography");
values.put("price", 100);
values.put("pages", 1000);
values.put("author", "James");
getContentResolver().insert(commonUri, values);

values.clear();
values.put("id", 2);
values.put("name", "AnthonyAutobiography");
values.put("price", 98);
values.put("pages", 980);
values.put("author", "Anthony");
Uri uri = getContentResolver().insert(commonUri, values);
  • 查询
Cursor cursor = getContentResolver().query(commonUri, null,null,null,null);
if (cursor != null) {
	while (cursor.moveToNext()) {
		int id = cursor.getInt(cursor.getColumnIndex("id"));
		String name = cursor.getString(cursor.getColumnIndex("name"));
		int price = cursor.getInt(cursor.getColumnIndex("price"));
		int pages = cursor.getInt(cursor.getColumnIndex("pages"));
		String author = cursor.getString(cursor.getColumnIndex("author"));
	}
	cursor.close();
}
  • 修改
    本例的意思是,把表里 author 为 James 的那组数据的 name 、price 、pages 修改为给定的值。
ContentValues values = new ContentValues();
values.put("name", "JamesAutobiography_1");
values.put("price", 150);
values.put("pages", 1800);
getContentResolver().update(commonUri, values,"author = ?", new String[]{ "James" });
  • 删除
    本例的意思是,将表里 author 为 Anthony 的那组数据删除。
getContentResolver().delete(commonUri, "author = ?", new String[]{ "Anthony" });

4.3 call方法使用

4.3.1 定义一个抽象ContentProvider

定义 getId() 、getName() 方法供子类实现;
重写了 call 方法,用于返回 Bundle 数据;

public abstract class AbstractProvider extends ContentProvider {

    private static final String METHOD = "method";
    private static final String ID = "id";
    private static final String NAME = "name";

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
        if (extras == null) {
            extras = new Bundle();
        }
        if (method.equals(METHOD)) {
            switch (arg) {
                case ID:
                    extras.putInt("keyId", getId());
                    break;
                case NAME:
                    extras.putString("keyName", getName());
                    break;
                default:break;
            }
        }
        return extras;
        //return super.call(method, arg, extras);
    }

    public abstract int getId();

    public abstract String getName();
}

4.3.2 实现抽象类

主要关注 getId() 、getName() 方法,这里简单返回数据。

public class ContentProviderImp extends AbstractProvider {


    @Override
    public boolean onCreate() {
        Log.d("luoah", "[ContentProviderImp.java] -- onCreate -- Process.myUid():" + Process.myUid());
        Log.d("luoah", "[ContentProviderImp.java] -- onCreate -- Process.myPid():" + Process.myPid());
        return super.onCreate();
    }

    @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;
    }

    @Nullable
    @Override
    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
        return super.call(method, arg, extras);
    }

    @Override
    public int getId() {
        return 2020;
    }

    @Override
    public String getName() {
        return "Android4ComponentDemo";
    }
}

注册到 manifest

<provider
            android:name=".provider.calltest.ContentProviderImp"
            android:authorities="com.test.calltest.provider"
            android:exported="true" />

4.3.3 调用方法

String PROVIDER_AUTHORITY = "content://com.test.calltest.provider";

Bundle iBundle;
iBundle = getContentResolver().call(Uri.parse(PROVIDER_AUTHORITY), "method", "id", null);

Bundle sBundle;
sBundle = getContentResolver().call(Uri.parse(PROVIDER_AUTHORITY), "method", "name", null);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值