Android--基础知识总结

根据《第一行代码》的顺序结构总结关键知识点,防止忘记,方便查阅。

Activity

1. 生命周期

(1)Activty的状态

① 运行状态:当Activity位于返回栈的栈顶时,Activity处于运行状态。

② 暂停状态:当Activity不处于栈顶,但仍可见,Activity处于暂停状态。

③ 停止状态:当Activity不处于栈顶,并且完全不可见,Activity处于停止状态。此时系统会为Activity保存相应的状态和成员变量。

④ 销毁状态:当Activity从返回栈移除后变为销毁状态。

(2)Activty的生命周期

① onCreate():Activity 第一次被创建的时候调用。该方法中完成Activity 的初始化操作。

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

③ onResume():该方法在Activity 准备好和用户进行交互的时候调用,此时的Activity 位于返回栈的栈顶,并且处于运行状态。

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

⑤ onStop():该方法在Activity 完全不可见的时候调用。

⑥ onDestroy():该方法在Activity 被销毁之前调用。

⑦ onRestart():该方法在Activity 由停止状态变为运行状态之前调用,即重新启动Activity时调用。

image.png

2. 启动模式

(1)standard:每次启动Activity都会创建一个新的实例。

(2)singleTop:当Activity已经位于栈顶时不会创建新的实例,而是调用onNewInstance方法,但当Activity不在栈顶是还是会创建新的实例。

(3)singleTask:当Activity已经在任务栈中存在时,不会创建新的实例,会把该Activity后面入栈的Activity全部出栈,使该Activity位于栈顶,并调用onNewInstance方法。

(4)singleInstance:Activity以单例模式运行,整个系统中只存在一个实例。该模式会启动一个新的任务栈,并且该Activity是栈中唯一的Activity,适用于共享Activity的场景。

3. Intent

Intent一般用于启动Activity、启动Service、发送广播、传递数据等。

(1)显示Intent

明确指定目标组件的完整类名的Intent为显示Intent。

(2)隐式Intent

不明确指定目标组件,而是通过Action、Category、Data等属性来描述要执行的操作。

每个Intent中只能指定一个action,但额能指定多个category,只有某个Activity中添加的<actiong>和<category>与Intent中指定的action和category同时匹配,这个Activity才能响应Intent。如果没有Activity能响应Intent会发生崩溃。

隐式Intent还可以启动其他程序的Activity,比如在Intent中添加一个data为百度网址,百度浏览器的Activity会响应该Intent。

4. 返回结果

Activity可以通过startActivityForResult()方法启动一个新Activity,新Activity finish()之前,可以通过setResult()方法将数据返回给上一个Activity,而上一个Activity可以通过onActivityResult()方法接收新Activity返回的结果。

启动目标Activity

Intent intent = new Intent(CurrentActivity.this, TargetActivity.class);
startActivityForResult(intent, REQUEST_CODE);

在目标Activity中返回结果

Intent resultIntent = new Intent();
resultIntent.putExtra("result_key", "result_value");
setResult(RESULT_OK, resultIntent);
finish();

在源Activity中处理结果

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
        String result = data.getStringExtra("result_key");
    }
}

5. 任务栈

Android使用任务栈task来管理Activity,栈是后进先出(LIFO)的数据结构,当启动一个新的Activity就会入栈处于栈顶,系统总是显示处于栈顶的Activity给用户。

6. 保护和恢复实例状态

(1)onSaveInstanceState保存状态

Activity 中提供了一个onSaveInstanceState()回调方法,该方法保证Activity 被回收之前一定会被调用,用来保存临时数据(保存到系统内存中),在Activity被重新创建之后,通过onCreate()的参数savedInstanceState来恢复销毁之前的数据。

onSaveInstanceState()主要是应对屏幕旋转或系统资源不足的情况。如何用户主动退出Activity或终止应用程序(点击返回或调用finish()),则onSaveInstanceState()不会被调用。

(2)手动处理配置变更

除了使用onSaveInstanceState()保存Activity销毁前的临时数据,也可以在AndroidManifest.xml中为Activity配置android:configChanges属性,避免Activity被销毁重建。

<activity android:name=".MyActivity"
    android:configChanges="orientation|screenSize|keyboardHidden" />

Fregment

1. 生命周期

(1)onAttach():当 Fragment 与 Activity 关联时调用。

(2)onCreate():初始化 Fragment 时调用。

(3)onCreateView():创建 Fragment 的视图(加载布局)时调用,返回一个 View 对象。

(4)onViewCreated():在 onCreateView() 之后调用,表示视图已经创建完成。

(5)onActivityCreated():当 Activity 的 onCreate() 方法完成时调用。

(6)onStart():Fragment 可见时调用。

(7)onResume():Fragment 可与用户交互时调用。

(8)onPause():Fragment 失去焦点时调用。

(9)onStop():Fragment 不可见时调用。

(10)onDestroyView():当 与Fragment 关联的视图被移除时调用。

(11)onDestroy():Fragment 被销毁时调用。

(12)onDetach():当 Fragment 与 Activity 解除关联时调用。

image.png

2. Fragment用法

(1)静态添加Fragment

在布局文件中直接声明Fragment。

<fragment
    android:id="@+id/fragment_container"
    android:name="com.example.MyFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

(2)动态添加Fragment

通过 FragmentManager 和 FragmentTransaction 动态管理 Fragment。

// 获取 FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();

// 开始一个事务
FragmentTransaction transaction = fragmentManager.beginTransaction();

// 添加 Fragment
MyFragment fragment = new MyFragment();
transaction.add(R.id.fragment_container, fragment);

// 异步提交事务
transaction.commit();

(3)Fragment导航

可以通过Android Jetpack 的 Nanvigation 组件提供了更简单的 Fragment 导航方式。

<fragment
    android:id="@+id/fragment_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    // 指定了要使用的Fragment类是NavHostFragment
    android:name="androidx.navigation.fragment.NavHostFragment"
    // 指定了要使用的导航图(Navigation Graph)
    app:navGraph="@navigation/navigation_main"
    app:defaultNavHost="true"/>

Fragment Nanvigation仍然需要通过 FragmentManager 和 FragmentTransaction 动态管理 Fragment的添加、替换、移除等操作。

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, new AnotherFragment());
transaction.addToBackStack(null); // 将事务添加到返回栈
transaction.commit();

3. Fragment返回栈

(1)addToBackStack()

使用 addToBackStack() 将 Fragment 事务添加到返回栈。

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, new AnotherFragment());
transaction.addToBackStack(null); // 将事务添加到返回栈
transaction.commit();

(2)popBackStack()

可以通过 FragmentManager 的 popBackStack() 方法手动回退到上一个 Fragment.

(3)onBackPressed()

在 Activity 中重写 onBackPressed(),或者在 Fragment 中监听返回键事件。

4. Fragment嵌套

FragmentManager 支持嵌套 Fragment。主Fragment使用 getChildFragmentManager() 来管理子 Fragment。

5. Fragment重叠

(1)重叠原因

当旋转屏幕或语言更改的情况下Activity会被销毁重建,在Activity被销毁时系统会调用Activity的onSaveInstanceState()方法保存Activity的状态,包括Fragment的状态,当Activity重建时在onCreate()中会恢复这些状态,但Activity重新启动再次创建新的Fragment就导致Fragment重叠。

(2)解决方法

① 在Activity的onCreate()中检查Fragment是否已存在,不存在的话再添加新的Fragment。

if (savedInstanceState == null) {
    // 只有在没有保存状态时才添加 Fragment
    getSupportFragmentManager().beginTransaction()
        .add(R.id.container, new MyFragment(), TAG_FRAGMENT)
        .commit();
} else {
    // 如果已存在,直接获取 Fragment 实例
    mFragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
}

② 通过 FragmentManager 查找即将被创建的的 Fragment是否已存在。

    // 查找已存在的 Fragment
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
    if (fragment == null) {
        // 如果 Fragment 不存在,则添加新的 Fragment
        fragment = new MyFragment();
        getSupportFragmentManager().beginTransaction()
            .add(R.id.fragment_container, fragment)
            .commit();
    }

Broadcast

1. 广播类型

(1)标准广播(Normal Broadcast)

标准广播是一种完全异步执行的广播,广播发出后所有的广播接收器几乎同时接收到该广播,无先后顺序。

(2)有序广播(Ordered Broadcast)

有序广播是一种同步执行的广播,同一时刻只有一个广播接收器能收到,当前接收到广播的接收器逻辑执行完之后才会继续向后传递广播。优先级高的广播器会先接收到广播,还可以截断广播不再传递广播。

2. 发送广播

发送自定义广播,只需要将广播action添加到Intent,然后通过sendBroadcast()或sendOrderedBroadcast()方法发送广播即可。

发送标准广播:
Intent intent = new Intent("com.example.MY_ACTION");
sendBroadcast(intent);

发送有序广播:
Intent intent = new Intent("com.example.MY_ACTION");
sendOrderedBroadcast(intent, null);

3. 广播接收器

(1)静态注册

静态注册广播可以让程序在未启动的情况下也能接收到广播。

在应用中创建一个广播接收器类,然后在AndroidManifest.xml 文件中静态注册刚创建的 BroadcastReceiver类。

<receiver android:name=".MyBroadcastReceiver">
    <intent-filter>
        <action android:name="com.example.MY_ACTION" />
    </intent-filter>
</receiver>

(2)动态注册

动态注册广播接收器需要在Activity中创建BroadcastReceiver类来接收广播,然后在IntentFilter 中指定接收哪些广播,再将BroadcastReceiver实例和IntentFilter 传入registerReceiver()方法动态注册广播监听。

BroadcastReceiver myReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理接收到的广播
    }
};
IntentFilter intentFilter = new IntentFilter("com.example.MY_ACTION");
registerReceiver(myReceiver, intentFilter);

4. 广播的生命周期

广播的生命周期从注册广播开始到取消注册结束,在注册广播期间接收到的所有广播都可以通过onReceive()方法中的Intent参数看到,通过Intent的action筛选广播来处理。

当不需要接收广播时要通过unregisterReceiver ()方法取消广播注册,避免当注册BroadcastReceiver的Activity销毁时系统仍持有该广播接收器,而BroadcastReceiver又一直持有Activity的引用,造成内存泄漏。

持久化存储

1. 文件存储

文件存储适合存储简单的文本数据或二进制数据,Context类中提供了openFileOutput()方法用于将数据存储到指定文件中,存储和读取文件的默认位置为 /data/data/<packageName>/files/

存储文件输入的第二个参数为文件的操作模式:MODE_PRIVATE表示写入的内容覆盖原文件中的内容、MODE_APPEND表示往文件里追加内容,文件不存在的话创建新文件。

<写入文件>

FileOutputStream output = openFileOutput("data.txt", MODE_PRIVATE);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output));
try{
    writer.write("Hello, World!");
} catch (IOException e) {
    e.printStackTrace();
} finally {
    writer.close();
}


<读取文件>

StringBuilder stringBuilder = new StringBuilder();
FileInputStream input= openFileInput("data");
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
try{
    String line;
    whild ((line = reader.readLine()) != null){
        stringBuilder.append(line);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    reader.close();
}

2. SharedPreferences

SharedPreferences支持不同的数据类型存储,使用键值对方式存储。

(1)获取SharedPreferences

① 通过Context类中的getSharedPreferences()方法获取SharedPreferences对象,该方法与写入文件方法类似,需要传入文件名称和操作模式,SharedPreferences文件存放路径为 /data/data/<packageName>/shared_prefs/,操作模式只有MODE_PRIVATE一种。

② 通过Activity类中的getPreferences()方法获取对象,该方法只需要传一个操作模式,文件名为当前Activity类名。

(2)向SharedPreferences文件存储数据

① 调用SharedPreferences对象的edit()方法获取SharedPreferences.Editor对象。

② 通过putString()等方法向SharedPreferences.Editor对象添加数据。

③ 调用apply()方法将添加的数据提交,完成数据存储操作。

<写入数据>

SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("username", "小明");
editor.putInt("age", 25);
editor.apply();


<读取数据>

SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
String name = sharedPreferences.getString("username", "");
int age = sharedPreferences.getInt("age", 0);

3. 数据库存储

SQLite 数据库适用于存储结构化数据,可以通过 SQLiteOpenHelper 来管理数据库的创建和版本更新。

(1)创建SQLite数据库

public class DBHelper extends SQLiteOpenHelper {

    public DBHelper(Context context) {
        super(context, "mydatabase.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建表
        String createTable = "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)";
        db.execSQL(createTable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 该方法用来更新数据库,先删除已存在表再重新创建
        db.execSQL("DROP TABLE IF EXISTS users");
        onCreate(db);
    }
}

(2)写入SQLite数据库

SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "John Doe");
values.put("age", 30);
db.insert("users", null, values);

(3)读取SQLite数据库

SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query("users", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
    do {
        String name = cursor.getString(cursor.getColumnIndex("name"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
    } while (cursor.moveToNext());
}
cursor.close();

ContentProvider

ContentProvider 是 Android 中一种用于不同应用程序之间共享数据的机制。提供了统一的接口,使得应用程序可以安全地访问和操作其他应用程序的数据,可以被视为一种数据访问层,通过URI(统一资源标识符)来标识不同的资源。

1. 功能

(1)数据共享

ContentProvider 允许一个应用程序访问另一个应用程序的数据。这些数据可以是数据库、文件或其他持久化存储。

(2)封装数据访问

通过 ContentProvider,可以封装对底层数据存储(如 SQLite 数据库)的操作,提供统一的数据访问接口。应用程序不需要直接接触底层的实现细节,只需要使用 ContentResolver 来与 ContentProvider 进行交互。

(3)数据操作的权限管理

通过 ContentProvider,可以控制哪些应用程序有权访问某些数据。Android 系统会通过声明权限来控制不同应用程序之间的数据访问权限。

2. 组件

(1)URI

每个 ContentProvider 都有一个唯一的 URI,用于标识数据。

访问某包的某表中的数据:content://com.example.appdemo.provider/table

访问某包的某表中id为1的数据:
content://com.example.appdemo.provider/table/1

(2)CRUD 操作

ContentProvider 支持标准的 CRUD(创建、读取、更新、删除)操作:
insert():插入数据。
query():查询数据。
update():更新数据。
delete():删除数据。

(3)ContentResolver

ContentResolver 是用于与 ContentProvider 交互的主要接口。通过 ContentResolver,应用可以向 ContentProvider 发起请求,执行增、删、改、查等操作。

3. ContentProvider使用

(1)创建 ContentProvider 类。

public class MyContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        // 初始化数据库或其他数据源
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        // 实现查询操作
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // 插入数据
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // 更新数据
        return 0;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 删除数据
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // 返回数据类型
        return "vnd.android.cursor.dir/vnd.com.example.provider";
    }
}

(2)声明ContentProvider

<AndroidManifest.xml>

<provider
    android:name=".MyContentProvider"
    android:authorities="com.example.provider"
    android:exported="true" />

(3)使用 ContentProvider

ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.example.provider/data");
Cursor cursor = resolver.query(uri, null, null, null, null);

Service

1. Service类型

(1)Started Service(启动的服务):当一个应用组件(如 Activity)调用 startService() 启动服务时,服务被启动并继续运行,直到它调用 stopSelf() 或 stopService() 被停止。

(2)Bound Service(绑定的服务):当其他组件通过 bindService() 方法绑定到服务时,服务可以在多个组件之间共享。在组件调用 unbindService() 后,服务会被停止。

2. 生命周期

(1)onCreate():当服务第一次创建时调用。用于初始化资源。

(2)onStartCommand():当 startService() 被调用,启动Service时调用。用于执行实际的后台任务。

(3)onBind():当 bindService() 被调用时,返回一个 IBinder 对象,用于通信。如果服务不需要绑定,则返回 null。

(4)onUnbind():当所有客户端解绑服务时调用,用来释放资源。

(5)onDestroy():服务销毁时调用,清理资源,停止线程等。

3. Service使用

(1)注册:Service需要在AndroidMainfest.xml中注册才能生效。

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

(2)启动和停止

Context类中提供了startService()和stopService()方法来启动和停止服务。

<Activity.java>

startService(Intent(context, MyService::class.java))
stopService(Intent(context, MyService::class.java))

Service中也可以自己停止运行。

返回值选项:
START_NOT_STICKY:服务不会在系统杀死后自动重启,适用于不需要持续运行的任务。
START_STICKY:服务会在系统杀死后自动重启,适用于需要持续运行的任务。
START_REDELIVER_INTENT:服务会在系统杀死后自动重启,并重新传递上次的 Intent,适用于需要重新处理任务的场景

<MyService.java>

   public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        // 任务完成后停止服务
        stopSelf(startId);
        return START_NOT_STICKY;
    }

(3)与Activity通信

Service与Activity通信需要借助Service的onBind()方法实现。

① Service中创建Binder

public class MyService extends Service {
    private final IBinder mBinder = new MyBinder();

    public class MyBinder extends Binder {

        public void fun1(){
            Log.d("MyService", "fun1 executed!")
        }

        public void fun2(){
            Log.d("MyService", "fun2 executed!")
        }

    }

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

② Activity调用Service中的方法

Activity通过bindService()方法绑定Service后可通过ServiceConnection实例调用Service的Binder中提供的方法。

绑定标志:
BIND_AUTO_CREATE:用于自动创建服务并绑定客户端(只会执行Service的onCreate(),不执行onStartCommand())。
BIND_ABOVE_CLIENT:服务的优先级高于客户端,即使客户端被杀死,服务仍会继续运行。
BIND_ALLOW_OOM_MANAGEMENT:允许系统在内存不足时杀死服务。
BIND_WAIVE_PRIORITY:服务的优先级不会因为绑定而提升。

public class MainActivity extends AppCompatActivity {
    private MyService myService;
    private boolean isBound = false;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myService = (MyService.MyBinder) service;
            isBound = true;

            // 调用服务中的方法
            myService.fun1();
            myService.fun2();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };

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

        // 绑定服务
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(serviceConnection);
            isBound = false;
        }
    }
}

4. 前台Service

当使用Service的应用程序进入后台后,Service可能会被系统收回,可以使用前台Service防止被系统收回。

前台Serevice会又运行图标显示在系统状态栏,防止恶意应用长期在后台偷偷占用手机资源。

(1)前台Service必须添加权限

    <!-- 前台Service -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

(2)使用前台Service

<MyService.java>

    public void onCreate() {
        super.onCreate();

        // 前台 Service
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);

        Notification notification = new NotificationCompat.Builder(this, "my_service")
                .setContentTitle("Service title")
                .setContentText("Service content")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background))
                .setContentIntent(pi)
                .build();

        startForeground(1, notification);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容