編輯:關於Android編程
以上4個問題,前面兩個我不打算展開,我相信,關於這兩個問題的說明和解答,已經有無數的大神詳細的講解過了。關於後面的兩個問題,是我一直關心的問題。到剛才,我解決了第四個問題,但是第三個的話,我相信,得繼續深入才能更好的回答。
android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> android:name=".service.NormalService" android:enabled="true" android:exported="true"/> android:theme="@style/AppTheme.NoActionbar" android:name=".activity.NormalActivity">簡單明了,我也沒有任何的解釋了。關於service上設置的兩個屬性分別是什麼意思?我也不太了解。想了解的,可以自行搜索下,這個不是本文的重點。
publicclassLauncherActivityextendsAppCompatActivity{ @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_launcher); findViewById(R.id.normal_service_btn).setOnClickListener(v->{ LogUtils.e("普通service模式"); //todo startActivity(newIntent(get(),NormalActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); }); findViewById(R.id.aidl_service).setOnClickListener(v->{ LogUtils.e("AIDLservice模式"); //todo }); } publicAppCompatActivityget(){ returnthis; } } public class LauncherActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_launcher); findViewById(R.id.normal_service_btn).setOnClickListener(v -> { LogUtils.e("普通service 模式"); // todo startActivity(new Intent(get(), NormalActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); }); findViewById(R.id.aidl_service).setOnClickListener(v -> { LogUtils.e("AIDL service 模式"); // todo }); } public AppCompatActivity get() { return this; } }
接下來,我們去看看NormalActivity?不,這個是服務的調用者,我們先去看服務,這樣清楚明了一點。依然是簡單明了
publicclassNormalServiceextendsService{ publicNormalService(){ } @Override publicIBinderonBind(Intentintent){ LogUtils.w("normalservicebind"); returnnewLocalBinder(); } @Override publicbooleanonUnbind(Intentintent){ LogUtils.w("normalserviceunbind"); returnsuper.onUnbind(intent); } @Override publicvoidonRebind(Intentintent){ super.onRebind(intent); LogUtils.w("normalservicerebind"); } @Override publicvoidonCreate(){ super.onCreate(); LogUtils.w("normalservicecreate"); } @Override publicintonStartCommand(Intentintent,intflags,intstartId){ LogUtils.w("normalservicestart"); returnsuper.onStartCommand(intent,flags,startId); } @Override publicvoidonDestroy(){ super.onDestroy(); LogUtils.w("normalservicedestroy"); } publicclassLocalBinderextendsBinder{ publicNormalServiceget(){ returnNormalService.this; } } /** *publicmethodinvokedbycaller * *@returnanumberofrandom */ publicintgetResult(){ returnnewRandom().nextInt(500); } } public class NormalService extends Service { public NormalService() { } @Override public IBinder onBind(Intent intent) { LogUtils.w("normal service bind"); return new LocalBinder(); } @Override public boolean onUnbind(Intent intent) { LogUtils.w("normal service unbind"); return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { super.onRebind(intent); LogUtils.w("normal service rebind"); } @Override public void onCreate() { super.onCreate(); LogUtils.w("normal service create"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtils.w("normal service start"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); LogUtils.w("normal service destroy"); } public class LocalBinder extends Binder { public NormalService get() { return NormalService.this; } } /** * public method invoked by caller * * @return a number of random */ public int getResult() { return new Random().nextInt(500); } }
現在,該去看看該服務的調用者了。代碼略長,不過應該還算是清晰。而且裡面沒有什麼實質性邏輯。最關鍵的方法就是getResult()。是讓調用者調用的。另外,我實現了一個LocalBinder,目的很明確,就是要讓調用者可以綁定當前服務。
publicclassNormalActivityextendsAppCompatActivity{ publicbooleanbind; privateNormalService.LocalBindermBinder; privateServiceConnectionmNormalConn; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_normal); setSupportActionBar((Toolbar)findViewById(R.id.toolbar)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("Normalactivity"); finalDrawableupArrow=getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha); upArrow.setColorFilter(getResources().getColor(R.color.colorWhite),PorterDuff.Mode.SRC_ATOP); getSupportActionBar().setHomeAsUpIndicator(upArrow); TextViewtvShowResult=(TextView)findViewById(R.id.normal_show_tv); tvShowResult.setText(getString(R.string.show_about_normal_service,"")); ButtonbtnExecute=(Button)findViewById(R.id.normal_ui_btn); btnExecute.setOnClickListener(v->{ LogUtils.w("executenormalservicemethod..."); LogUtils.w("executenormalservicemethod..."); if(bind){ intresult=mBinder.get().getResult(); LogUtils.e("result====="+result); tvShowResult.setText(getString(R.string.show_about_normal_service,result)); }else{ LogUtils.e("bindservicefail,tryagain......"); bindService(newIntent(this,NormalService.class),mNormalConn,BIND_AUTO_CREATE); ToastHelper.show(getApplication(),"bindservicefail,tryagain......"); } }); mNormalConn=newServiceConnection(){ @Override publicvoidonServiceConnected(ComponentNamename,IBinderservice){ bind=true; LogUtils.w("normalactivityconnectednormalservice..."); mBinder=(NormalService.LocalBinder)service; } @Override publicvoidonServiceDisconnected(ComponentNamename){ bind=false; mBinder=null; LogUtils.w("normalactivitydisconnectednormalservice!"); } }; bindService(newIntent(this,NormalService.class),mNormalConn,BIND_AUTO_CREATE); } @Override protectedvoidonDestroy(){ super.onDestroy(); unbindService(mNormalConn); LogUtils.e("$$$unbindnormalservice"); } @Override publicbooleanonOptionsItemSelected(MenuItemitem){ if(item.getItemId()==android.R.id.home){ finish(); returntrue; } returnsuper.onOptionsItemSelected(item); } }public class NormalActivity extends AppCompatActivity { public boolean bind; private NormalService.LocalBinder mBinder; private ServiceConnection mNormalConn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_normal); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("Normal activity"); final Drawable upArrow = getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha); upArrow.setColorFilter(getResources().getColor(R.color.colorWhite), PorterDuff.Mode.SRC_ATOP); getSupportActionBar().setHomeAsUpIndicator(upArrow); TextView tvShowResult = (TextView) findViewById(R.id.normal_show_tv); tvShowResult.setText(getString(R.string.show_about_normal_service, "")); Button btnExecute = (Button) findViewById(R.id.normal_ui_btn); btnExecute.setOnClickListener(v -> { LogUtils.w("execute normal service method..."); LogUtils.w("execute normal service method..."); if (bind) { int result = mBinder.get().getResult(); LogUtils.e("result=====" + result); tvShowResult.setText(getString(R.string.show_about_normal_service, result)); } else { LogUtils.e("bind service fail ,try again......"); bindService(new Intent(this, NormalService.class), mNormalConn, BIND_AUTO_CREATE); ToastHelper.show(getApplication(), "bind service fail ,try again......"); } }); mNormalConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bind = true; LogUtils.w("normal activity connected normal service..."); mBinder = (NormalService.LocalBinder) service; } @Override public void onServiceDisconnected(ComponentName name) { bind = false; mBinder = null; LogUtils.w("normal activity disconnected normal service!"); } }; bindService(new Intent(this, NormalService.class), mNormalConn, BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mNormalConn); LogUtils.e("$$$ unbind normal service"); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { finish(); return true; } return super.onOptionsItemSelected(item); } }
代碼略長,因為裡面還做了除了調用服務之外的其他操作。關鍵代碼看 20~50 ,這30行的代碼即可。這個代碼也不用解釋了,就是在界面上有一個按鈕,每次點擊按鈕都會調用服務裡面的方法得到一個隨機數,並顯示在textview上面罷了。至於怎麼去綁定服務,我相信大家都很娴熟了,不必再解釋了。
不過我覺得值得注意的是38行的代碼mBinder = (NormalService.LocalBinder) service;,也就是我們通過類型強轉得到一個我們自己定義的IBinder對象,也只有得到它,才能得到服務的對象,然後調用服務裡面的方法。
ok,代碼結束了,一個最簡單的bindService完成了。運行截圖就不貼了,沒意義,大家都知道是什麼樣子的。.........................
那麼,要怎麼修改呢? 首先,我們給service設置一個單獨的進程,修改清單文件,在service節點裡面添加一個屬性android:process=":normal".這樣就讓service和'activity'之間跨進程了。 然後,我們什麼也不做,直接運行代碼。注意:我們所做的修改僅僅是給service設置了一個單獨的進程,java代碼沒有改動一行。
現在運行之後,是什麼樣子的效果呢?程序崩潰了。看日志:
FATALEXCEPTION:main Process:com.pythoncat.ipcorservice,PID:2811 java.lang.ClassCastException:android.os.BinderProxycannotbecasttocom.pythoncat.ipcorservice.service.NormalService$LocalBinder atcom.pythoncat.ipcorservice.activity.NormalActivity$1.onServiceConnected(NormalActivity.java:58) atandroid.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1256) atandroid.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1273) atandroid.os.Handler.handleCallback(Handler.java:815) atandroid.os.Handler.dispatchMessage(Handler.java:104) atandroid.os.Looper.loop(Looper.java:194) atandroid.app.ActivityThread.main(ActivityThread.java:5667) atjava.lang.reflect.Method.invoke(NativeMethod) atjava.lang.reflect.Method.invoke(Method.java:372) atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:962) atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)FATAL EXCEPTION: main Process: com.pythoncat.ipcorservice, PID: 2811 java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.pythoncat.ipcorservice.service.NormalService$LocalBinder at com.pythoncat.ipcorservice.activity.NormalActivity$1.onServiceConnected(NormalActivity.java:58) at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1256) at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1273) at android.os.Handler.handleCallback(Handler.java:815) at android.os.Handler.dispatchMessage(Handler.java:104) at android.os.Looper.loop(Looper.java:194) at android.app.ActivityThread.main(ActivityThread.java:5667) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:962) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
真正有用的日志就一行
java.lang.ClassCastException:android.os.BinderProxycannotbecasttocom.pythoncat.ipcorservice.service.NormalService$LocalBinder`.java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.pythoncat.ipcorservice.service.NormalService$LocalBinder`.
看了日志我們大概明白了,我們拿到的Ibinder對象,並不是我們在service中定義的NormalService$LocalBinder對象。
關於這方面的理論知識,我也不多介紹了,無論是android官方文檔,還是一些大神的博客,都對此有比較清晰的介紹。我就簡單說一下實現方式。那麼,為什麼會這樣呢?簡單的理解就是,每個進程的內存是獨立的,在A進程開辟的內存,B進程並不能訪問。如何解決? 我並不知道怎麼解決。看大神們的博客大概就是說,通過IBinder所在的共享內存區域進行數據交換,實現跨進程通信,實現起來也就是aidl.
既然當前activity和service不在同一個進程了,無法通過之前的方式直接拿到IBinder對象,那麼,我們就定義一個.aidl來實現之間的通信。
從上面的代碼上看,我們實際上是希望調用service中的getResult()方法。那麼,我們就定義一個.aidl裡面包含該方法即可。
1.如何操作?右鍵appmodule --> new --> AIDL -->AIDL File .然後寫一個文件名如:NormalBinder,然後直接回車。在裡面添加需要調用的service中的方法。interfaceNormalBinder{ /** *Demonstratessomebasictypesthatyoucanuseasparameters *andreturnvaluesinAIDL. */ voidbasicTypes(intanInt,longaLong,booleanaBoolean,floataFloat, doubleaDouble,StringaString); intgetResult(); }interface NormalBinder { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); int getResult(); }
publicclassRemoteBinderextendsNormalBinder.Stub{ @Override publicvoidbasicTypes(intanInt,longaLong,booleanaBoolean,floataFloat,doubleaDouble,StringaString)throwsRemoteException{ } @Override publicintgetResult()throwsRemoteException{ returnnewRandom().nextInt(500); } }public class RemoteBinder extends NormalBinder.Stub { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public int getResult() throws RemoteException { return new Random().nextInt(500); } }
修改之後的service長這樣子:
publicclassNormalServiceextendsService{ publicNormalService(){ } @Override publicIBinderonBind(Intentintent){ LogUtils.w("normalservicebind"); //returnnewLocalBinder(); returnnewRemoteBinder(); } @Override publicbooleanonUnbind(Intentintent){ LogUtils.w("normalserviceunbind"); returnsuper.onUnbind(intent); } @Override publicvoidonRebind(Intentintent){ super.onRebind(intent); LogUtils.w("normalservicerebind"); } @Override publicvoidonCreate(){ super.onCreate(); LogUtils.w("normalservicecreate"); } @Override publicintonStartCommand(Intentintent,intflags,intstartId){ LogUtils.w("normalservicestart"); returnsuper.onStartCommand(intent,flags,startId); } @Override publicvoidonDestroy(){ super.onDestroy(); LogUtils.w("normalservicedestroy"); } publicclassLocalBinderextendsBinder{ publicNormalServiceget(){ returnNormalService.this; } } /** *publicmethodinvokedbycaller * *@returnanumberofrandom */ publicintgetResult(){ returnnewRandom().nextInt(500); } publicclassRemoteBinderextendsNormalBinder.Stub{ @Override publicvoidbasicTypes(intanInt,longaLong,booleanaBoolean,floataFloat,doubleaDouble,StringaString)throwsRemoteException{ } @Override publicintgetResult()throwsRemoteException{ returnnewRandom().nextInt(500); } } } public class NormalService extends Service { public NormalService() { } @Override public IBinder onBind(Intent intent) { LogUtils.w("normal service bind"); // return new LocalBinder(); return new RemoteBinder(); } @Override public boolean onUnbind(Intent intent) { LogUtils.w("normal service unbind"); return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { super.onRebind(intent); LogUtils.w("normal service rebind"); } @Override public void onCreate() { super.onCreate(); LogUtils.w("normal service create"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtils.w("normal service start"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); LogUtils.w("normal service destroy"); } public class LocalBinder extends Binder { public NormalService get() { return NormalService.this; } } /** * public method invoked by caller * * @return a number of random */ public int getResult() { return new Random().nextInt(500); } public class RemoteBinder extends NormalBinder.Stub { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public int getResult() throws RemoteException { return new Random().nextInt(500); } } }
得將之前的IBinder對象換成可以aidl的IBinder了。
mNormalConn=newServiceConnection(){ @Override publicvoidonServiceConnected(ComponentNamename,IBinderservice){ bind=true; LogUtils.w("normalactivityconnectednormalservice..."); //mBinder=(NormalService.LocalBinder)service; mBinder=NormalBinder.Stub.asInterface(service); } @Override publicvoidonServiceDisconnected(ComponentNamename){ bind=false; mBinder=null; LogUtils.w("normalactivitydisconnectednormalservice!"); } };mNormalConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bind = true; LogUtils.w("normal activity connected normal service..."); // mBinder = (NormalService.LocalBinder) service; mBinder = NormalBinder.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { bind = false; mBinder = null; LogUtils.w("normal activity disconnected normal service!"); } };
發現什麼改變了沒有,就是將第6行替換成第7行。也就是mbinder的獲取方式改變了,得到的對象也不是之前的對象了。
完全替換之後的activity長這樣子的
publicclassNormalActivityextendsAppCompatActivity{ publicbooleanbind; privateServiceConnectionmNormalConn; privateNormalBindermBinder; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_normal); setSupportActionBar((Toolbar)findViewById(R.id.toolbar)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("Normalactivity"); finalDrawableupArrow=getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha); upArrow.setColorFilter(getResources().getColor(R.color.colorWhite),PorterDuff.Mode.SRC_ATOP); getSupportActionBar().setHomeAsUpIndicator(upArrow); TextViewtvShowResult=(TextView)findViewById(R.id.normal_show_tv); tvShowResult.setText(getString(R.string.show_about_normal_service,"")); ButtonbtnExecute=(Button)findViewById(R.id.normal_ui_btn); btnExecute.setOnClickListener(v->{ LogUtils.w("executenormalservicemethod..."); LogUtils.w("executenormalservicemethod..."); if(bind){ //intresult=mBinder.get().getResult(); intresult=0; try{ result=mBinder.getResult(); }catch(RemoteExceptione){ e.printStackTrace(); } LogUtils.e("result====="+result); tvShowResult.setText(getString(R.string.show_about_normal_service,result)); }else{ LogUtils.e("bindservicefail,tryagain......"); bindService(newIntent(this,NormalService.class),mNormalConn,BIND_AUTO_CREATE); ToastHelper.show(getApplication(),"bindservicefail,tryagain......"); } }); mNormalConn=newServiceConnection(){ @Override publicvoidonServiceConnected(ComponentNamename,IBinderservice){ bind=true; LogUtils.w("normalactivityconnectednormalservice..."); //mBinder=(NormalService.LocalBinder)service; mBinder=NormalBinder.Stub.asInterface(service); } @Override publicvoidonServiceDisconnected(ComponentNamename){ bind=false; mBinder=null; LogUtils.w("normalactivitydisconnectednormalservice!"); } }; bindService(newIntent(this,NormalService.class),mNormalConn,BIND_AUTO_CREATE); } @Override protectedvoidonDestroy(){ super.onDestroy(); unbindService(mNormalConn); LogUtils.e("$$$unbindnormalservice"); } @Override publicbooleanonOptionsItemSelected(MenuItemitem){ if(item.getItemId()==android.R.id.home){ finish(); returntrue; } returnsuper.onOptionsItemSelected(item); } }public class NormalActivity extends AppCompatActivity { public boolean bind; private ServiceConnection mNormalConn; private NormalBinder mBinder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_normal); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("Normal activity"); final Drawable upArrow = getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha); upArrow.setColorFilter(getResources().getColor(R.color.colorWhite), PorterDuff.Mode.SRC_ATOP); getSupportActionBar().setHomeAsUpIndicator(upArrow); TextView tvShowResult = (TextView) findViewById(R.id.normal_show_tv); tvShowResult.setText(getString(R.string.show_about_normal_service, "")); Button btnExecute = (Button) findViewById(R.id.normal_ui_btn); btnExecute.setOnClickListener(v -> { LogUtils.w("execute normal service method..."); LogUtils.w("execute normal service method..."); if (bind) { // int result = mBinder.get().getResult(); int result = 0; try { result = mBinder.getResult(); } catch (RemoteException e) { e.printStackTrace(); } LogUtils.e("result=====" + result); tvShowResult.setText(getString(R.string.show_about_normal_service, result)); } else { LogUtils.e("bind service fail ,try again......"); bindService(new Intent(this, NormalService.class), mNormalConn, BIND_AUTO_CREATE); ToastHelper.show(getApplication(), "bind service fail ,try again......"); } }); mNormalConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bind = true; LogUtils.w("normal activity connected normal service..."); // mBinder = (NormalService.LocalBinder) service; mBinder = NormalBinder.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { bind = false; mBinder = null; LogUtils.w("normal activity disconnected normal service!"); } }; bindService(new Intent(this, NormalService.class), mNormalConn, BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mNormalConn); LogUtils.e("$$$ unbind normal service"); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { finish(); return true; } return super.onOptionsItemSelected(item); } }
現在,一個最簡單的aidl就完成了。現在運行代碼,和之前的效果完全一樣。還是每點擊一次按鈕,就會在textview上顯示一個500以內的隨機數。不過,現在是跨進程調用了,不再是同一個進程了。為什麼說是跨進程?你可以嘗試分別輸出service所在的pid和activity所在的pid就一目了然了。
int pid = Process.myPid();,可以使用這句代碼完成當前pid的獲取。吐槽下 amd 的cpu,超級辣雞!android studio 能卡死,即使是16G內存,2G獨顯,120G的固態。
最後,關於我:野生安卓猿一只,入安卓坑已有幾年,一直徘徊在新手和菜鳥之間。
格式塔原理介紹Material Design 運用了格式塔原理,尤其是在圖形的設計上。 格式塔理論是心理學中為數不多的理性心理學之一。格式塔學派以某些相當抽象的,與知覺和
面對一個項目,對於Android應用開發框架的選擇,我想過三種方案:1.使用Loader + HttpClient + GreenDao + Gson + Fragmen
一. 百度地圖城市定位和POI搜索知識 上一篇文章百度地圖開發(一)中講述了如何申請百度APIKey及解決顯示空白網格的問題.該篇文章主要講述如何定位城市位置、定位自己的
今天給大家講講有關自定義對話框的相關內容,前面兩篇都在在利用系統提供的函數來實現對話框,但局限性太大,當我們想自己定義視圖的時候,就不能利用系統函數了,就需要我們這裡的自
status_t Camera::connectLegacy(int c