diff --git a/app-catalog/samples/foundation/build.gradle b/app-catalog/samples/foundation/build.gradle
index ddff10a..7813486 100644
--- a/app-catalog/samples/foundation/build.gradle
+++ b/app-catalog/samples/foundation/build.gradle
@@ -12,6 +12,7 @@ android {
}
buildFeatures {
viewBinding = true
+ aidl = true
}
namespace 'com.wintmain.foundation'
}
diff --git a/app-catalog/samples/foundation/src/main/AndroidManifest.xml b/app-catalog/samples/foundation/src/main/AndroidManifest.xml
index 655cee0..b185f15 100644
--- a/app-catalog/samples/foundation/src/main/AndroidManifest.xml
+++ b/app-catalog/samples/foundation/src/main/AndroidManifest.xml
@@ -84,155 +84,155 @@
android:exported="true"
android:theme="@style/Theme.AppCompat.DayNight" />
@@ -244,92 +244,92 @@
@@ -338,7 +338,7 @@
@@ -346,12 +346,18 @@
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/INewPeopleListener.aidl b/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/INewPeopleListener.aidl
new file mode 100644
index 0000000..79bdb21
--- /dev/null
+++ b/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/INewPeopleListener.aidl
@@ -0,0 +1,10 @@
+// INewPeopleListener.aidl
+package com.wintmain.foundation;
+
+import com.wintmain.foundation.People;
+
+// AIDL中无法使用普通接口,所以创建此文件
+
+interface INewPeopleListener {
+ void onNewPeople(in People newPeople);
+}
\ No newline at end of file
diff --git a/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/IPeopleManager.aidl b/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/IPeopleManager.aidl
new file mode 100644
index 0000000..205e754
--- /dev/null
+++ b/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/IPeopleManager.aidl
@@ -0,0 +1,19 @@
+// IPeopleManager.aidl
+// AIDL接口中只支持方法,不支持声明静态常量
+package com.wintmain.foundation;
+
+// 自定义的Parcelable对象和AIDL对象不管是否和当前的AIDL文件位于同一个包内,也必须要显式import进来
+
+import com.wintmain.foundation.People;
+import com.wintmain.foundation.INewPeopleListener;
+
+interface IPeopleManager {
+ List getPeopleList();
+ // addPeople方法的参数中有in关键字,因为aidl中除了基本数据类型,其它类型的参数必须标上方向:in、out或者inout,
+ // 我们需根据实现需要去指定参数类型,因为这在底层实现是有开销的。
+ void addPeople(in People people);
+
+ // 扩展新增代码:新增接口
+ void registerListener(INewPeopleListener listener);
+ void unregisterListener(INewPeopleListener listener);
+}
diff --git a/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/People.aidl b/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/People.aidl
new file mode 100644
index 0000000..d27e4c5
--- /dev/null
+++ b/app-catalog/samples/foundation/src/main/aidl/com/wintmain/foundation/People.aidl
@@ -0,0 +1,20 @@
+// People.aidl
+package com.wintmain.foundation;
+
+// Declare any non-default types here with import statements
+
+// 在Android的AIDL中,仅支持的数据类型有:
+// 1. 基本数据类型(int、long、char、boolean、double等)
+// 2. String和CharSequence
+// 3. List:只支持ArrayList,里面的每个元素都必须能够被AIDL支持
+// 4. Map:只支持HashMap,里面的每个元素都必须能够被AIDL支持
+// 5. Parcelable:所有实现了Parcelable接口的对象
+// 6. AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
+
+// Parcelable的作用是序列化对象,因为跨进程通信传输对象必须是以序列化和反序列化的方式进行。
+// Parcelable的实现有些繁琐,但性能效率很高。
+
+import com.wintmain.foundation.People;
+
+// People should be declared in a file called com/wintmain/foundation/People.aidl
+parcelable People;
diff --git a/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/People.java b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/People.java
new file mode 100644
index 0000000..6c386c7
--- /dev/null
+++ b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/People.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.wintmain.foundation;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import androidx.annotation.NonNull;
+
+public class People implements Parcelable {
+
+ public int pId;
+ public String pName;
+
+ public People(Parcel in) {
+ pId = in.readInt();
+ pName = in.readString();
+ }
+
+ public People(int pId, String pName) {
+ this.pId = pId;
+ this.pName = pName;
+ }
+
+ public String getName() {
+ return pName;
+ }
+
+ public void setName(String pName) {
+ this.pName = pName;
+ }
+
+ public int getId() {
+ return pId;
+ }
+
+ public void setId(int pId) {
+ this.pId = pId;
+ }
+
+ // 内容描述功能由describeContents方法来完成
+ // 几乎在所有情况下这方法都应该返回0,仅当当前对象中存在文件描述符时才返回1。
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ // 序列化功能由writeToParcel方法来完成,最终是通过Parcel中的一系列write方法来完成的。
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(pId);
+ dest.writeString(pName);
+ }
+
+ // 反序列化功能由CREATOR来完成,其内部标明了如何创建序列化对象和数组,
+ // 并通过Parcel的一系列read方法来完成反序列化过程。
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator<>() {
+ public People createFromParcel(Parcel in) {
+ return new People(in);
+ }
+
+ public People[] newArray(int size) {
+ return new People[size];
+ }
+ };
+
+
+ @NonNull
+ @Override
+ public String toString() {
+ return String.format("pId:%s, pName:%s", pId, pName);
+ }
+}
diff --git a/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PeopleRemoteService.kt b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PeopleRemoteService.kt
new file mode 100644
index 0000000..8c38ab2
--- /dev/null
+++ b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PeopleRemoteService.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.wintmain.foundation
+
+import android.app.Service
+import android.content.Intent
+import android.os.Binder
+import android.os.IBinder
+import android.os.RemoteCallbackList
+import java.util.concurrent.CopyOnWriteArrayList
+
+// 服务端代码,客户端见[PlaceHoldActivity.java]
+class PeopleRemoteService : Service() {
+ val mPeopleList = CopyOnWriteArrayList()
+ // 客户端注册和移除注册过程中使用的虽是同一个客户端对象,
+ // 但通过Binder传递到服务端后,产生了两个不同的对象。
+ // 因为对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程
+ val mListenList = RemoteCallbackList()
+
+ private val mBinder: Binder = object : IPeopleManager.Stub() {
+ override fun getPeopleList(): List {
+ return mPeopleList
+ }
+
+ override fun addPeople(people: People) {
+ mPeopleList.add(people)
+
+ // 扩展新增代码:新加对象后执行监听回调
+ for (i in 0 until mListenList.beginBroadcast()) {
+ val listener: INewPeopleListener = mListenList.getBroadcastItem(i)
+ listener.onNewPeople(people)
+ }
+ mListenList.finishBroadcast()
+ }
+
+ // RemoteCallbackList是系统专门提供的用于删除跨进程listener接口,它是一个泛型,支持管理任意AIDL接口。
+ // 另外它内部自动实现了线程同步的功能。
+ // beginBroadcast和finishBroadcast必须要配对使用,哪怕仅仅获取RemoteCallbackList中的元素个数
+ override fun registerListener(listener: INewPeopleListener) {
+ mListenList.beginBroadcast()
+ mListenList.register(listener)
+ mListenList.finishBroadcast()
+ }
+
+ override fun unregisterListener(listener: INewPeopleListener) {
+ mListenList.beginBroadcast()
+ mListenList.unregister(listener)
+ mListenList.finishBroadcast()
+ }
+ }
+
+ override fun onBind(intent: Intent): IBinder {
+ return mBinder
+ }
+}
\ No newline at end of file
diff --git a/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PlaceHolderActivity.java b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PlaceHolderActivity.java
index 65f464a..cdc2af3 100644
--- a/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PlaceHolderActivity.java
+++ b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/PlaceHolderActivity.java
@@ -20,7 +20,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.provider.AlarmClock;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
@@ -36,12 +39,19 @@
description = "一个为了测试API调用的入口",
tags = "A-Self_demos")
public class PlaceHolderActivity extends AppCompatActivity {
+ private static final String TAG = PlaceHolderActivity.class.getSimpleName();
+ IPeopleManager peopleManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pft_activity_main);
+ // AIDL示例 S
+ Intent intent = new Intent(this, PeopleRemoteService.class);
+ bindService(intent, mConnection, BIND_AUTO_CREATE);
+ // AIDL示例 E
+
// 初始化一些三方库
initLibs(getApplication());
@@ -62,9 +72,12 @@ public void onTitleClick(TitleBarExt titleBar) {
newIntent.setAction(AlarmClock.ACTION_SET_ALARM);
newIntent.putExtra(AlarmClock.EXTRA_SKIP_UI, true);
newIntent.setComponent(new ComponentName("com.android.deskclock", "com.android.deskclock.HandleApiCalls"));
- startActivity(newIntent);
-
- ToastUtils.show("你点击了中间,并且新建了一个9:30的闹钟");
+ try {
+ startActivity(newIntent);
+ ToastUtils.show("你点击了中间,并且新建了一个9:30的闹钟");
+ } catch (Exception e) {
+ ToastUtils.show("你点击了中间,但是新建闹钟失败");
+ }
}
@Override
@@ -74,6 +87,20 @@ public void onRightClick(TitleBarExt titleBar) {
});
}
+ @Override
+ protected void onDestroy() {
+ // 移除监听
+ if (peopleManager != null && peopleManager.asBinder().isBinderAlive()) {
+ try {
+ peopleManager.unregisterListener(mNewPeopleListener);
+ } catch (RemoteException e) {
+ android.util.Log.d(TAG, "catch:" + e.getMessage());
+ }
+ }
+ unbindService(mConnection);
+ super.onDestroy();
+ }
+
private void initLibs(Application application) {
// 初始化 TitleBar 默认样式
TitleBarExt.setDefaultStyle(
@@ -97,4 +124,34 @@ public TextView newRightView(Context context) {
// 初始化 Toast
ToastUtils.init(this.getApplication());
}
+
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ android.util.Log.d(TAG, "onServiceConnected...");
+ peopleManager = IPeopleManager.Stub.asInterface(service);
+ try {
+ People people = new People(1000, "wintmain");
+ // 注册监听
+ peopleManager.registerListener(mNewPeopleListener);
+ peopleManager.addPeople(people);
+ } catch (RemoteException e) {
+ android.util.Log.d(TAG, "catch:" + e.getMessage());
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ android.util.Log.d(TAG, "onServiceDisconnected!!!");
+ ToastUtils.show("onServiceDisconnected!!!");
+ peopleManager = null;
+ }
+ };
+
+
+ // 定义监听接口
+ private final INewPeopleListener mNewPeopleListener = new INewPeopleListener.Stub() {
+ @Override
+ public void onNewPeople(People newPeople) {
+ ToastUtils.show("onNewPeople: " + newPeople);
+ }
+ };
}
diff --git a/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/job/DbEntryActivity.kt b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/overview/DbEntryActivity.kt
similarity index 95%
rename from app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/job/DbEntryActivity.kt
rename to app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/overview/DbEntryActivity.kt
index 15d148f..9dac248 100644
--- a/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/job/DbEntryActivity.kt
+++ b/app-catalog/samples/foundation/src/main/java/com/wintmain/foundation/overview/DbEntryActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2024 wintmain
+ * Copyright 2023-2025 wintmain
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.wintmain.foundation.job
+package com.wintmain.foundation.overview
import android.os.Bundle
import android.widget.Button
@@ -37,10 +37,10 @@ class DbEntryActivity : AppCompatActivity(R.layout.activity_db_entry) {
// Initialize DbHelper
dbHelper = DbHelper(applicationContext)
dbHelper.writableDatabase // Ensure database is created
- addlisteners()
+ addListeners()
}
- private fun addlisteners() {
+ private fun addListeners() {
findViewById