高焕堂——Android框架底层结构知多少?
http://www.android1.net/Topic.aspx?BoardID=11&TopicID=703
看看人家写的东西和大陆这边的文章还是有一些差距。拿了高先生的代码自己编译了一下,有所收获。特将代码贴出。
Android中一个app就是一个process,那么跨process如何通讯呢?答案是IPC,但是自己在模拟器上测试了了一下,
感觉速度还是挺慢的。以下几点应该注意以下:
1. 注册remote process,在androidmenifest.xml中测试远程服务:
<service android:name="com.fp.app.service.FPService" android:process=":remote"> <intent-filter> <action android:name="com.fp.app.service.FPService" /> </intent-filter> </service>
其中
<action android:name="com.fp.app.service.FPService" /> 为client段调用的名称,你可以定义多个remote process,只要保证 intent filter中名称不一样即可。
2. 如果启动了多个remote process,在启动另外之前,应该将前面的process ubind.
super.unbindService(mConnection);
否则,会抛出如下错误:
3. 是否process启动,可以通过ddms观察状态,画红线部分为新的remote process.一般一个process会启动3个或者3个以下thread 进行ipc通讯.I/ActivityManager( 583): Stopping service: com.fp.app.readbinder/com.fp.app.service.myService E/ActivityThread( 868): Activity com.fp.app.readbinder.IPCSyncActivity has leaked ServiceConnection com.fp.app.readbinder.IPCSyncActivity$2@4373599 E/ActivityThread( 868): android.app.ServiceConnectionLeaked: Activity com.fp.app.readbinder.IPCSyncActivity has leaked ServiceConnection com.fp.app E/ActivityThread( 868): at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:886) E/ActivityThread( 868): at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:781) E/ActivityThread( 868): at android.app.ApplicationContext.bindService(ApplicationContext.java:820) E/ActivityThread( 868): at android.content.ContextWrapper.bindService(ContextWrapper.java:307) E/ActivityThread( 868): at com.fp.app.readbinder.IPCSyncActivity.bindService(IPCSyncActivity.java:48) E/ActivityThread( 868): at com.fp.app.readbinder.IPCSyncActivity.onCreate(IPCSyncActivity.java:41) E/ActivityThread( 868): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123) E/ActivityThread( 868): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2231) E/ActivityThread( 868): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2284) E/ActivityThread( 868): at android.app.ActivityThread.access$1800(ActivityThread.java:112) E/ActivityThread( 868): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692) E/ActivityThread( 868): at android.os.Handler.dispatchMessage(Handler.java:99) E/ActivityThread( 868): at android.os.Looper.loop(Looper.java:123) E/ActivityThread( 868): at android.app.ActivityThread.main(ActivityThread.java:3948) E/ActivityThread( 868): at java.lang.reflect.Method.invokeNative(Native Method) E/ActivityThread( 868): at java.lang.reflect.Method.invoke(Method.java:521) E/ActivityThread( 868): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782) E/ActivityThread( 868): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540) E/ActivityThread( 868): at dalvik.system.NativeStart.main(Native Method) W/ActivityManager( 583): Unbind failed: could not find connection for android.os.BinderProxy@436f90f0 E/ActivityThread( 868): Activity com.fp.app.readbinder.IPCSyncActivity has leaked ServiceConnection com.fp.app.readbinder.IPCSyncActivity$1@4373598 E/ActivityThread( 868): android.app.ServiceConnectionLeaked: Activity com.fp.app.readbinder.IPCSyncActivity has leaked ServiceConnection com.fp.app E/ActivityThread( 868): at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:886) E/ActivityThread( 868): at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:781) E/ActivityThread( 868): at android.app.ApplicationContext.bindService(ApplicationContext.java:820) E/ActivityThread( 868): at android.content.ContextWrapper.bindService(ContextWrapper.java:307) E/ActivityThread( 868): at com.fp.app.readbinder.IPCSyncActivity.bindService(IPCSyncActivity.java:46) E/ActivityThread( 868): at com.fp.app.readbinder.IPCSyncActivity.onCreate(IPCSyncActivity.java:41) E/ActivityThread( 868): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123) E/ActivityThread( 868): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2231) E/ActivityThread( 868): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2284) E/ActivityThread( 868): at android.app.ActivityThread.access$1800(ActivityThread.java:112) E/ActivityThread( 868): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692) E/ActivityThread( 868): at android.os.Handler.dispatchMessage(Handler.java:99) E/ActivityThread( 868): at android.os.Looper.loop(Looper.java:123) E/ActivityThread( 868): at android.app.ActivityThread.main(ActivityThread.java:3948) E/ActivityThread( 868): at java.lang.reflect.Method.invokeNative(Native Method) E/ActivityThread( 868): at java.lang.reflect.Method.invoke(Method.java:521) E/ActivityThread( 868): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782) E/ActivityThread( 868): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540) E/ActivityThread( 868): at dalvik.system.NativeStart.main(Native Method) W/ActivityManager( 583): Unbind failed: could not find connection for android.os.BinderProxy@436f6ba8
废话少说,可以通过下面的代码进行体会:
package com.fp.app.debug; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import android.util.Log; public class DebugLog { public final static boolean DEBUG = false; public static void log(String message) { if (true) { String fullClassName = Thread.currentThread().getStackTrace()[3].getClassName(); String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1); String methodName = Thread.currentThread().getStackTrace()[3].getMethodName(); int lineNumber = Thread.currentThread().getStackTrace()[3].getLineNumber(); Log.d(className + "." + methodName + "():" + lineNumber, message); } } public static void printThreadId(String message) { if (DEBUG) { Log.d(message, String.valueOf(Thread.currentThread().getId())); } } public static void log(String tag, String message) { if (DEBUG) { String methodName = Thread.currentThread().getStackTrace()[3].getMethodName(); int lineNumber = Thread.currentThread().getStackTrace()[3].getLineNumber(); Log.d(tag + "." + methodName + "():" + lineNumber, message); } } public static void printTrace(String message) { if (DEBUG) { printIllegalArgumentException("", message); } } public static String getStackTrace(Throwable throwable) { Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); throwable.printStackTrace(printWriter); return writer.toString(); } public static void printIllegalArgumentException(String tag, String arg) { if (DEBUG) { final Throwable throwable = new IllegalArgumentException(arg); Log.w(tag, arg, throwable); } } }package com.fp.app.readbinder; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class IPCSyncActivity extends Activity implements OnClickListener { @Override protected void onDestroy() { super.unbindService(mConnection); super.unbindService(mConnection2); super.onDestroy(); } private static final int BTNTOPMARGIN = 10; private static final int BTNHEIGHT = 50; private static final int BTNWIDTH = 120; private static final String STREXIT = "exit"; private static final String STRBINDSERVICE1 = "bind service connection 1"; private static final String STRBINDSERVICE2 = "bind service connection 2"; private static final int EXIT = 104; private static final int BINDSERVICE = 102; private static final int STARTSERVICE = 101; private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; private final int FP = LinearLayout.LayoutParams.FILL_PARENT; private Button btnBindService1, btnBindService2, btnExit; public TextView headerText; private IBinder mBinder1, mBinder2; public void onCreate(Bundle icicle) { super.onCreate(icicle); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); setView(layout); String tna = Thread.currentThread().getName(); Thread.currentThread().setName(tna + "-ac01"); bindService(); } private void bindService() { bindService(new Intent("com.fp.app.service.myService"), mConnection, Context.BIND_AUTO_CREATE); bindService(new Intent("com.fp.app.service.myService"), mConnection2, Context.BIND_AUTO_CREATE); } private void setView(LinearLayout layout) { setBtn(layout, btnBindService1, STRBINDSERVICE1, STARTSERVICE); setBtn(layout, btnBindService2, STRBINDSERVICE2, BINDSERVICE); setBtn(layout, btnExit, STREXIT, EXIT); setTv(layout); } private void setBtn(LinearLayout layout, Button btn, String text, int resId) { btn = new Button(this); btn.setId(resId); btn.setText(text); btn.setBackgroundResource(R.drawable.music_play); btn.setOnClickListener(this); layout.addView(btn, getPara()); } private void setTv(LinearLayout layout) { headerText = new TextView(this); headerText.setTextColor(Color.WHITE); headerText.setText(""); LinearLayout.LayoutParams tvParam = new LinearLayout.LayoutParams(FP, WC); tvParam.topMargin = BTNTOPMARGIN; layout.addView(headerText, tvParam); setContentView(layout); } private LinearLayout.LayoutParams getPara() { LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(BTNWIDTH, BTNHEIGHT); param.topMargin = BTNTOPMARGIN; return param; } public void onClick(View v) { switch (v.getId()) { case STARTSERVICE: onTransaction1(); break; case BINDSERVICE: onTransaction2(); break; case EXIT: finish(); break; } } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder ibinder) { mBinder1 = ibinder; } public void onServiceDisconnected(ComponentName className) { } }; private ServiceConnection mConnection2 = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder ibinder) { mBinder2 = ibinder; } public void onServiceDisconnected(ComponentName className) { } }; private void onTransaction2() { Parcel pc = Parcel.obtain(); Parcel pc_reply = Parcel.obtain(); try { mBinder2.transact(1, pc, pc_reply, 0); headerText.setText(pc_reply.readString()); } catch (Exception e) { e.printStackTrace(); } } private void onTransaction1() { Parcel p = Parcel.obtain(); Parcel p_reply = Parcel.obtain(); try { mBinder1.transact(0, p, p_reply, 0); headerText.setText(p_reply.readString()); } catch (Exception e) { e.printStackTrace(); } } }package com.fp.app.readbinder; import com.fp.app.debug.DebugLog; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class mainActivity extends Activity implements OnClickListener { @Override protected void onDestroy() { super.unbindService(mConnection); super.onDestroy(); } private static final int BTNTOPMARGIN = 10; private static final int BTNHEIGHT = 50; private static final int BTNWIDTH = 120; private static final String STRIPCSYNC = "transfer to ipc sync"; private static final String STREXIT = "exit"; private static final String STRIPCCALL = "ipc call"; private static final String STRBINDSERVICE = "bind service"; private static final String STRSTARTSERVICE = "start service"; private static final int IPCSYNC = 105; private static final int EXIT = 104; private static final int IPCCALL = 103; private static final int BINDSERVICE = 102; private static final int STARTSERVICE = 101; private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; private final int FP = LinearLayout.LayoutParams.FILL_PARENT; private Button btnStartService, btnBindService, btnIPCCall, btnExit,btnTransferTOIPCSync; public TextView headerText; private IBinder mBinder = null; public void onCreate(Bundle icicle) { super.onCreate(icicle); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); setView(layout); String tna = Thread.currentThread().getName(); Thread.currentThread().setName(tna + "-ac01"); } private void setView(LinearLayout layout) { setBtn(layout, btnStartService, STRSTARTSERVICE, STARTSERVICE); setBtn(layout, btnBindService, STRBINDSERVICE, BINDSERVICE); setBtn(layout, btnIPCCall, STRIPCCALL, IPCCALL); setBtn(layout, btnExit, STREXIT, EXIT); setBtn(layout, btnTransferTOIPCSync, STRIPCSYNC, IPCSYNC); setTv(layout); } private void setBtn(LinearLayout layout, Button btn, String text, int resId) { btn = new Button(this); btn.setId(resId); btn.setText(text); btn.setBackgroundResource(R.drawable.music_play); btn.setOnClickListener(this); layout.addView(btn, getPara()); } private void setTv(LinearLayout layout) { headerText = new TextView(this); headerText.setTextColor(Color.WHITE); headerText.setText(""); LinearLayout.LayoutParams tvParam = new LinearLayout.LayoutParams(FP, WC); tvParam.topMargin = BTNTOPMARGIN; layout.addView(headerText, tvParam); setContentView(layout); } private LinearLayout.LayoutParams getPara() { LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(BTNWIDTH, BTNHEIGHT); param.topMargin = BTNTOPMARGIN; return param; } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder ibinder) { mBinder = ibinder; } public void onServiceDisconnected(ComponentName className) { } }; public void onClick(View v) { switch (v.getId()) { case STARTSERVICE: startService(); break; case BINDSERVICE: bindService(); break; case IPCCALL: transact(); break; case EXIT: finish(); break; case IPCSYNC: transfer(); break; } } private void transfer() { Intent targetIntent = new Intent(this, IPCSyncActivity.class); startActivity(targetIntent); } private void transact() { DebugLog.printTrace("transact"); Parcel pc = Parcel.obtain(); pc.writeString("budoudou"); Parcel pc_reply = Parcel.obtain(); try { mBinder.transact(1, pc, pc_reply, 0); headerText.setText(pc_reply.readString()); } catch (Exception e) { e.printStackTrace(); } } private void bindService() { bindService(new Intent("com.fp.app.service.FPService"), mConnection, Context.BIND_AUTO_CREATE); DebugLog.printTrace("bindService"); } private void startService() { startService(new Intent("com.fp.app.service.FPService")); DebugLog.printTrace("startService"); } }package com.fp.app.readbinder; import com.fp.app.debug.DebugLog; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class subActivity extends Activity implements OnClickListener { private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; private final int FP = LinearLayout.LayoutParams.FILL_PARENT; private Button btn; public TextView tv; public void onCreate(Bundle icicle) { super.onCreate(icicle); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); setBtn(layout); setTV(layout); } private void setTV(LinearLayout layout) { tv = new TextView(this); tv.setTextColor(Color.WHITE); tv.setText(""); LinearLayout.LayoutParams param2 = new LinearLayout.LayoutParams(FP, WC); param2.topMargin = 10; layout.addView(tv, param2); setContentView(layout); DebugLog.printTrace("onCreate"); tv.setText(Thread.currentThread().getName() + "-myActivity"); } private void setBtn(LinearLayout layout) { btn = new Button(this); btn.setId(101); btn.setText("exit"); btn.setBackgroundResource(R.drawable.music_play); btn.setOnClickListener(this); LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(120, 50); param.topMargin = 10; layout.addView(btn, param); } public void onClick(View v) { switch (v.getId()) { case 101: finish(); break; } } }package com.fp.app.service; import com.fp.app.debug.DebugLog; import com.fp.app.readbinder.subActivity; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; public class FPService extends Service { private IBinder mBinder = null; @Override public void onCreate() { Thread.currentThread().setName(Thread.currentThread().getName() + "-onCreate"); mBinder = new myBinder(); DebugLog.printTrace("service onCreate"); } @Override public void onStart(Intent intent, int startId) { Thread.currentThread().setName(Thread.currentThread().getName() + "-onStart"); Intent i = new Intent(getApplicationContext(), subActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getApplicationContext().startActivity(i); DebugLog.printTrace("service onStart"); } @Override public IBinder onBind(Intent intent) { Thread.currentThread().setName(Thread.currentThread().getName() + "-onBind"); Intent in = new Intent(this.getApplicationContext(), subActivity.class); in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(in); DebugLog.printTrace("service onBind"); return mBinder; } public class myBinder extends Binder { public myBinder() { Thread.currentThread().setName(Thread.currentThread().getName() + "-myBinder"); } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException { DebugLog.log(data.readString()); reply.writeString(Thread.currentThread().getName()); DebugLog.printTrace("onTransact"); return true; } } }package com.fp.app.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; public class myService extends Service { private static int count = 0; public IBinder onBind(Intent intent) { count++; return new myBinder(); } public class myBinder extends Binder { private int value = 0; public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException { if (code == 0) { value += 5; try { Thread.sleep(8000); } catch (InterruptedException e) { e.printStackTrace(); } } else if (code == 1) value += 300; reply.writeString(" count:" + String.valueOf(count) + " value:" + String.valueOf(value)); return true; } } }<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.fp.app.readbinder"> <application android:icon="@drawable/icon"> <activity android:name=".mainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".IPCSyncActivity" /> <activity android:name=".subActivity" android:process=":remote"> </activity> <service android:name="com.fp.app.service.FPService" android:process=":remote"> <intent-filter> <action android:name="com.fp.app.service.FPService" /> </intent-filter> </service> <service android:name="com.fp.app.service.myService" android:process=":remote"> <intent-filter> <action android:name="com.fp.app.service.myService" /> </intent-filter> </service> </application> </manifest>