(一) Android 四大组件

Activity

1. 生命周期

正常情况下:

  • 启动 Activity:系统先调用onCreate(),然后调用onstart(),最后调用onResume(),Activity 就此进入运行状态。
  • 退出 Activity:系统先调用onPause(),然后调用onStop(),最后调用onDestroy(),Activity 就此销毁。
  • 当前设备锁屏或者点击 Home 键使程序进入后台,依次执行onPause()onStop(), 重新回到 Activity,依次执行onRestart()onStart()onResume()(Activity 不被回收的情况下)。
  • 当前 Activity 被 Dialog 主题的 Activity 覆盖时,执行onPause(),回到前台执行onResume()
  • 当 Activity 不在前台或者不可见时被系统回收后,再次回到此 Activity ,系统会依次执行onCreate()onStart()onResume()
  • 启动一个新 Activity,旧 Activity 会先执行onPause(),然后新 Activity 再启动。
  • 按是否可见分类:onStart()onStop()
  • 按是否前台分类:onResume()onPause

注意:当 Activity 弹出对话框时,并不会回调 onPause

异常情况下:

当系统内存不足,或者系统配置发生改变(如旋转方向),Activity 会被杀死。

  • 由于是异常情况下终止的,系统会调用onSaveInstanceState()来保存当前 Activity 的状态,这个方法的调用时机是在onStop()之前,当 Activity 重新创建后,系统会把销毁时保存的 Bundle 对象作为参数传递给onCreate()onRestoreInstanceState(),建议在onRestoreInstanceState()中做数据恢复,毕竟专门用来恢复实例状态的。另外,每个 View 本身都有onSaveInstanceState()onRestoreInstanceState()方法,因此系统都会默认恢复 View 的基本状态。
  • 防止重新创建 Activity:在 AndroidManifest.xml 中指定android:configChanges="orientation",似乎有些设备需要多指定一个参数,即android:configChanges:="orientaion|screenSize"

2. 启动模式

总共 4 种启动模式:Standard,SingleTop,SingleTask,SingleInstance。

  • Standard:默认的启动模式,在这种模式下,Activity 会进入启动它的 Activity 所在的任务栈。
  • SingleTop:如果新 Activity 位于任务栈的栈顶时,Activity 不会被创建,并且它的onNewIntent()方法会被回调,其余生命周期方法均不会回调。
  • SingleTask:如果 Activity 在一个任务栈中存在,那么多次启动此 Activity 都不会创建新实例,但系统会回调onNewIntent()。此外,位于此 Activity 之上的所有 Activity 均会出栈,此时 Activity 位于栈顶。
  • SingleInstance:这种模式下的 Activity 只能单独存在于一个任务栈中,由于栈内复用特性,此后的请求均不会创建新的实例。

注意:默认情况下,所有 Activity 所需的任务栈的名字为应用的包名,可以在 AndroidManifest.xml 中通过android:taskAffinity=""来指定任务栈。

Service

被启动的 Service 默认是在主线程下工作的,因此如果需要执行耗时操作,应当另开一个子线程来执行,以免阻塞主线程导致出现 ANR(Application Not Response)。任何 Activity 都可以控制同一个 Service,并且系统中也只会存在一个 Service 实例。

启动模式

1. startService()

  • 普通模式:在这种模式下启动 Service 后,即使 Activity 销毁后,Service 也会在后台继续执行任务,直到在 Activity 中手动调用stopService()或者在在 Service 类中调用stopSelf(),服务才会停止,Activity 无法与 Service 进行通信。
  • onCreate中可以做一些初始化,onStartCommand()中放置执行任务的代码,onDestroy()中进行资源的释放。
  • 在这种方式下启动 Service,生命周期是onCreate()->onStartCommand()->onDestroy(),当 Service 已经被启动后,无论执行多少次startServvice(),都不会走onCreate(),而是会调用onStartCommand()

2. bindService()

  • 绑定模式:在这种模式下启动 Service,当 Activity 销毁后,Service 也会跟着销毁,不再使用 Service 时,调用unbindService()停止。这种模式下可以进行 Activity 和 Service 的通信。
  • 这这种方式下启动 Service,生命周期是onCreate()->onBind()->onUnbind()->onDestroy()onStartCommand()不会有调用机会。

注意:当startService()bindService()一起被调用后,若想停止服务,必须同时调用stopService()unbindService(),这样服务才会停止,顺序没有严格要求,但一定要同时调用。

示例

  • 创建一个 Service
public class MyService extends Service {

    private static final String TAG = "MyService";
    private IBinder mBinder;

    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null) {
            mBinder = new MyBinder();
        }
        Log.d(TAG, "-----onBind()-----");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "-----onUnbind()-----");
        return super.onUnbind(intent);
    }

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

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

    public class MyBinder extends Binder {

        // 这里可以定义想要通信的方法,在 Activity 中可以通过 Binder 实例调用
        public void print(String data) {
            Log.d(TAG, "print: " + data);
        }
    }
}
复制代码
  • 在 AndroidManifest.xml 中注册
<!--android:enabled 表示是否启用,android:exported 表示是否向外界公开-->
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="false" />
复制代码
  • 在 Activity 中定义 ServiceConnectioin 并进行绑定
public class MainActivity extends AppCompatActivity {

    private ServiceConnection mConnection;
    private MyService.MyBinder mBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mConnection = new MyConnection();
        initView();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }

    private void initView() {
        Button bindService = findViewById(R.id.btn3);
        Button unbindService = findViewById(R.id.btn4);
        bindService.setOnClickListener(view -> {
            bindService(new Intent(MainActivity.this, MyService.class), mConnection, BIND_AUTO_CREATE);
        });
        unbindService.setOnClickListener(view -> {
            unbindService(mConnection);
        });
    }

    private class MyConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 转换为自己定义的 Binder
            mBinder = (MyService.MyBinder) service;
            // 调用自己定义的方法进行通信
            mBinder.print("成功通信");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 这个方法只有在出现异常的时候才会被系统调用
        }
    }
}
复制代码

前台服务

服务几乎都是在后台运行的,系统优先级相对比较低,当系统出现内存不足时就容易被回收,如果希望服务可以一直保持运行状态,不会因为内存不足而被回收,这时就可以使用前台服务。与普通服务不同,前台服务会有一个正在运行的图标在通知栏里显示,类似通知。例如腾讯手机管家等通知,会在通知栏显示此时手机的内存状态等。

示例:在 Service 中通过startForeground()创建前台服务,然后在 Activity 中通过startService()bindService()开启,通过stopService()unbindService()关闭。

public class MyService extends Service {

    private static final String TAG = "MyService";
    private IBinder mBinder;

    @Override
    public void onCreate() {
        super.onCreate();
        createForegroundService();
    }
    
    private void createForegroundService() {
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
        Notification notification;
        // 使用了建造者模式,将需要定义到的部分提前设置好
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "Channel_1")
                .setContentTitle("前台服务")
                .setContentText("This is content text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .setContentIntent(pi);
        // 因为 Android 8.0 添加了 NotificationChannel(通知渠道)
        // 因此需要适配,不然在 8.0 上会显示不了通知
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel("Channel_1", "前台服务", NotificationManager.IMPORTANCE_DEFAULT);
            manager.createNotificationChannel(channel);
            notification = builder.build();
        } else {
            notification = builder.build();
        }
        startForeground(1, notification);
    }
    ......
}    
复制代码

IntentService

在前面就知道了,普通服务默认是运行在主线程中的,如果在服务里执行一些耗时操作就容易出现 ANR,当然也可以自己另开线程来执行,然后在合适的时机在 Service 内部调用stopSelf()来停止。但是这样稍显麻烦,为了可以简单地创建一个异步的、会自动停止的 Service,Android 提供了 IntentService 类,很好地解决了这个问题。

  • 通过创建一个 Service 继承自 IntentService,并重写onHandleIntent()方法即可,在这个方法中便可以处理耗时操作,并且当执行完毕后,Service 会自动停止。

示例

public class MyIntentService extends IntentService {

    public MyIntentService() {
        // 必须调用父类有参构造
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("MyIntentService", "currentThread: " + Thread.currentThread().getName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService", "-----onDestroy()-----");
    }
}
复制代码
  • 当执行完毕后,Service 自动停止,详情见 Logcat

远程服务

由于远程服务我并不熟悉,这里就先落下了,后面学习了再补充上去。

猜你喜欢

转载自juejin.im/post/5c7ac9d351882546c47daddc